summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/IPXrouted/IPXrouted.8189
-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.h106
-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.c395
-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.c212
-rw-r--r--usr.sbin/IPXrouted/sap_output.c197
-rw-r--r--usr.sbin/IPXrouted/sap_tables.c322
-rw-r--r--usr.sbin/IPXrouted/startup.c281
-rw-r--r--usr.sbin/IPXrouted/table.h115
-rw-r--r--usr.sbin/IPXrouted/tables.c434
-rw-r--r--usr.sbin/IPXrouted/timer.c239
-rw-r--r--usr.sbin/IPXrouted/trace.c519
-rw-r--r--usr.sbin/IPXrouted/trace.h138
-rw-r--r--usr.sbin/Makefile139
-rw-r--r--usr.sbin/Makefile.inc3
-rw-r--r--usr.sbin/ac/Makefile18
-rw-r--r--usr.sbin/ac/ac.8155
-rw-r--r--usr.sbin/ac/ac.c562
-rw-r--r--usr.sbin/accton/Makefile6
-rw-r--r--usr.sbin/accton/accton.834
-rw-r--r--usr.sbin/accton/accton.c93
-rw-r--r--usr.sbin/adduser/Makefile14
-rw-r--r--usr.sbin/adduser/adduser.8247
-rw-r--r--usr.sbin/adduser/adduser.perl1394
-rw-r--r--usr.sbin/adduser/rmuser.8169
-rw-r--r--usr.sbin/adduser/rmuser.perl576
-rw-r--r--usr.sbin/amd/Makefile11
-rw-r--r--usr.sbin/amd/Makefile.inc53
-rw-r--r--usr.sbin/amd/NOTES3
-rw-r--r--usr.sbin/amd/amd/Makefile42
-rw-r--r--usr.sbin/amd/amq/Makefile21
-rw-r--r--usr.sbin/amd/doc/Makefile14
-rw-r--r--usr.sbin/amd/fixmount/Makefile26
-rw-r--r--usr.sbin/amd/fsinfo/Makefile24
-rw-r--r--usr.sbin/amd/hlfsd/Makefile21
-rw-r--r--usr.sbin/amd/include/Makefile13
-rw-r--r--usr.sbin/amd/include/amu_nfs_prot.h1
-rw-r--r--usr.sbin/amd/include/aux_conf.h80
-rw-r--r--usr.sbin/amd/include/build_version.h6
-rw-r--r--usr.sbin/amd/include/config.h1946
-rw-r--r--usr.sbin/amd/include/newvers.sh33
-rw-r--r--usr.sbin/amd/libamu/Makefile34
-rw-r--r--usr.sbin/amd/mk-amd-map/Makefile16
-rw-r--r--usr.sbin/amd/pawd/Makefile23
-rw-r--r--usr.sbin/amd/scripts/Makefile7
-rw-r--r--usr.sbin/amd/wire-test/Makefile19
-rw-r--r--usr.sbin/apm/Makefile8
-rw-r--r--usr.sbin/apm/apm.8116
-rw-r--r--usr.sbin/apm/apm.c388
-rw-r--r--usr.sbin/apmconf/Makefile5
-rw-r--r--usr.sbin/apmconf/apmconf.858
-rw-r--r--usr.sbin/apmconf/apmconf.c145
-rw-r--r--usr.sbin/arp/Makefile8
-rw-r--r--usr.sbin/arp/arp.4142
-rw-r--r--usr.sbin/arp/arp.8146
-rw-r--r--usr.sbin/arp/arp.c732
-rw-r--r--usr.sbin/atm/Makefile33
-rw-r--r--usr.sbin/atm/Makefile.inc30
-rw-r--r--usr.sbin/atm/atmarpd/Makefile39
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_config.c130
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_log.c148
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_scsp.c792
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_subr.c962
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_timer.c223
-rw-r--r--usr.sbin/atm/atmarpd/atmarp_var.h224
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.8129
-rw-r--r--usr.sbin/atm/atmarpd/atmarpd.c409
-rw-r--r--usr.sbin/atm/scspd/Makefile44
-rw-r--r--usr.sbin/atm/scspd/scsp_cafsm.c1448
-rw-r--r--usr.sbin/atm/scspd/scsp_config.c1160
-rw-r--r--usr.sbin/atm/scspd/scsp_config_lex.c528
-rw-r--r--usr.sbin/atm/scspd/scsp_config_parse.y410
-rw-r--r--usr.sbin/atm/scspd/scsp_hfsm.c580
-rw-r--r--usr.sbin/atm/scspd/scsp_if.c655
-rw-r--r--usr.sbin/atm/scspd/scsp_if.h194
-rw-r--r--usr.sbin/atm/scspd/scsp_input.c1103
-rw-r--r--usr.sbin/atm/scspd/scsp_log.c265
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.c611
-rw-r--r--usr.sbin/atm/scspd/scsp_msg.h462
-rw-r--r--usr.sbin/atm/scspd/scsp_output.c934
-rw-r--r--usr.sbin/atm/scspd/scsp_print.c1315
-rw-r--r--usr.sbin/atm/scspd/scsp_socket.c1349
-rw-r--r--usr.sbin/atm/scspd/scsp_subr.c1123
-rw-r--r--usr.sbin/atm/scspd/scsp_timer.c265
-rw-r--r--usr.sbin/atm/scspd/scsp_var.h434
-rw-r--r--usr.sbin/atm/scspd/scspd.8443
-rw-r--r--usr.sbin/atm/scspd/scspd.c545
-rw-r--r--usr.sbin/bad144/Makefile10
-rw-r--r--usr.sbin/bad144/bad144.8191
-rw-r--r--usr.sbin/bad144/bad144.c760
-rw-r--r--usr.sbin/boot0cfg/Makefile6
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.8137
-rw-r--r--usr.sbin/boot0cfg/boot0cfg.c270
-rw-r--r--usr.sbin/bootparamd/Makefile4
-rw-r--r--usr.sbin/bootparamd/Makefile.inc3
-rw-r--r--usr.sbin/bootparamd/bootparamd/Makefile23
-rw-r--r--usr.sbin/bootparamd/bootparamd/README75
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.848
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.c342
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparams.579
-rw-r--r--usr.sbin/bootparamd/bootparamd/main.c119
-rw-r--r--usr.sbin/bootparamd/callbootd/Makefile22
-rw-r--r--usr.sbin/bootparamd/callbootd/callbootd.c198
-rw-r--r--usr.sbin/btxld/Makefile7
-rw-r--r--usr.sbin/btxld/btx.h68
-rw-r--r--usr.sbin/btxld/btxld.897
-rw-r--r--usr.sbin/btxld/btxld.c554
-rw-r--r--usr.sbin/btxld/elfh.c122
-rw-r--r--usr.sbin/btxld/elfh.h38
-rw-r--r--usr.sbin/cdcontrol/Makefile4
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.1181
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.c1033
-rw-r--r--usr.sbin/chkgrp/Makefile7
-rw-r--r--usr.sbin/chkgrp/chkgrp.872
-rw-r--r--usr.sbin/chkgrp/chkgrp.c141
-rw-r--r--usr.sbin/chown/Makefile9
-rw-r--r--usr.sbin/chown/chgrp.1133
-rw-r--r--usr.sbin/chown/chown.8152
-rw-r--r--usr.sbin/chown/chown.c273
-rw-r--r--usr.sbin/chroot/Makefile6
-rw-r--r--usr.sbin/chroot/chroot.880
-rw-r--r--usr.sbin/chroot/chroot.c99
-rw-r--r--usr.sbin/ckdist/Makefile12
-rw-r--r--usr.sbin/ckdist/ckdist.196
-rw-r--r--usr.sbin/ckdist/ckdist.c440
-rw-r--r--usr.sbin/config/Makefile14
-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/Makefile10
-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.8183
-rw-r--r--usr.sbin/config/config.h239
-rw-r--r--usr.sbin/config/config.y1159
-rw-r--r--usr.sbin/config/configvers.h11
-rw-r--r--usr.sbin/config/lang.l239
-rw-r--r--usr.sbin/config/main.c445
-rw-r--r--usr.sbin/config/mkglue.c412
-rw-r--r--usr.sbin/config/mkheaders.c229
-rw-r--r--usr.sbin/config/mkioconf.c1369
-rw-r--r--usr.sbin/config/mkmakefile.c867
-rw-r--r--usr.sbin/config/mkoptions.c371
-rw-r--r--usr.sbin/config/mkswapconf.c272
-rw-r--r--usr.sbin/config/mkubglue.c198
-rw-r--r--usr.sbin/cron/Makefile3
-rw-r--r--usr.sbin/cron/Makefile.inc9
-rw-r--r--usr.sbin/cron/cron/Makefile10
-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.875
-rw-r--r--usr.sbin/cron/cron/cron.c322
-rw-r--r--usr.sbin/cron/cron/cron.h289
-rw-r--r--usr.sbin/cron/cron/database.c263
-rw-r--r--usr.sbin/cron/cron/do_command.c541
-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.c179
-rw-r--r--usr.sbin/cron/cron/user.c128
-rw-r--r--usr.sbin/cron/crontab/Makefile15
-rw-r--r--usr.sbin/cron/crontab/crontab.1115
-rw-r--r--usr.sbin/cron/crontab/crontab.5225
-rw-r--r--usr.sbin/cron/crontab/crontab.c593
-rw-r--r--usr.sbin/cron/doc/CHANGES155
-rw-r--r--usr.sbin/cron/doc/CONVERSION85
-rw-r--r--usr.sbin/cron/doc/FEATURES84
-rw-r--r--usr.sbin/cron/doc/INSTALL87
-rw-r--r--usr.sbin/cron/doc/MAIL475
-rw-r--r--usr.sbin/cron/doc/Makefile.vixie128
-rw-r--r--usr.sbin/cron/doc/README72
-rw-r--r--usr.sbin/cron/doc/README.1ST4
-rw-r--r--usr.sbin/cron/doc/THANKS29
-rw-r--r--usr.sbin/cron/lib/Makefile12
-rw-r--r--usr.sbin/cron/lib/compat.c236
-rw-r--r--usr.sbin/cron/lib/entry.c552
-rw-r--r--usr.sbin/cron/lib/env.c203
-rw-r--r--usr.sbin/cron/lib/misc.c648
-rw-r--r--usr.sbin/crunch/COPYRIGHT25
-rw-r--r--usr.sbin/crunch/Makefile4
-rw-r--r--usr.sbin/crunch/Makefile.inc2
-rw-r--r--usr.sbin/crunch/README88
-rw-r--r--usr.sbin/crunch/crunchgen/Makefile10
-rw-r--r--usr.sbin/crunch/crunchgen/crunched_main.c117
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.1278
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.c883
-rw-r--r--usr.sbin/crunch/crunchgen/mkskel.sh15
-rw-r--r--usr.sbin/crunch/crunchide/Makefile6
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.182
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.c267
-rw-r--r--usr.sbin/crunch/crunchide/exec_aout.c203
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf32.c389
-rw-r--r--usr.sbin/crunch/crunchide/exec_elf64.c40
-rw-r--r--usr.sbin/crunch/crunchide/extern.h68
-rw-r--r--usr.sbin/crunch/examples/Makefile32
-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.conf155
-rw-r--r--usr.sbin/ctm/Makefile5
-rw-r--r--usr.sbin/ctm/Makefile.inc5
-rw-r--r--usr.sbin/ctm/README97
-rw-r--r--usr.sbin/ctm/ctm/Makefile25
-rw-r--r--usr.sbin/ctm/ctm/ctm.1301
-rw-r--r--usr.sbin/ctm/ctm/ctm.5214
-rw-r--r--usr.sbin/ctm/ctm/ctm.c318
-rw-r--r--usr.sbin/ctm/ctm/ctm.h164
-rw-r--r--usr.sbin/ctm/ctm/ctm_ed.c114
-rw-r--r--usr.sbin/ctm/ctm/ctm_input.c138
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass1.c250
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass2.c245
-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/Makefile8
-rw-r--r--usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c209
-rw-r--r--usr.sbin/ctm/ctm_rmail/Makefile7
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.1479
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.c663
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.c97
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.h3
-rw-r--r--usr.sbin/ctm/ctm_rmail/options.h137
-rw-r--r--usr.sbin/ctm/ctm_smail/Makefile7
-rw-r--r--usr.sbin/ctm/ctm_smail/ctm_smail.c469
-rw-r--r--usr.sbin/ctm/mkCTM/Makefile25
-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/mkCTM186
-rw-r--r--usr.sbin/ctm/mkCTM/mkctm.c593
-rw-r--r--usr.sbin/dev_mkdb/Makefile6
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.881
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.c153
-rw-r--r--usr.sbin/diskpart/Makefile6
-rw-r--r--usr.sbin/diskpart/diskpart.8143
-rw-r--r--usr.sbin/diskpart/diskpart.c496
-rw-r--r--usr.sbin/dpt/Makefile5
-rw-r--r--usr.sbin/dpt/Makefile.inc3
-rw-r--r--usr.sbin/dpt/dpt_ctlinfo/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.83
-rw-r--r--usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.c123
-rw-r--r--usr.sbin/dpt/dpt_ctls/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_ctls/dpt_ctls.83
-rw-r--r--usr.sbin/dpt/dpt_ctls/dpt_ctls.c81
-rw-r--r--usr.sbin/dpt/dpt_dm/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_dm/dpt_dm.86
-rw-r--r--usr.sbin/dpt/dpt_dm/dpt_dm.c417
-rw-r--r--usr.sbin/dpt/dpt_led/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_led/dpt_led.83
-rw-r--r--usr.sbin/dpt/dpt_led/dpt_led.c122
-rw-r--r--usr.sbin/dpt/dpt_sig/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_sig/dpt_sig.83
-rw-r--r--usr.sbin/dpt/dpt_sig/dpt_sig.c598
-rw-r--r--usr.sbin/dpt/dpt_softc/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_softc/dpt_softc.84
-rw-r--r--usr.sbin/dpt/dpt_softc/dpt_softc.c188
-rw-r--r--usr.sbin/dpt/dpt_sysinfo/Makefile11
-rw-r--r--usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.83
-rw-r--r--usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.c256
-rw-r--r--usr.sbin/edquota/Makefile6
-rw-r--r--usr.sbin/edquota/edquota.8174
-rw-r--r--usr.sbin/edquota/edquota.c756
-rw-r--r--usr.sbin/edquota/pathnames.h39
-rw-r--r--usr.sbin/fdcontrol/Makefile4
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.898
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.c141
-rw-r--r--usr.sbin/fdformat/Makefile9
-rw-r--r--usr.sbin/fdformat/fdformat.1161
-rw-r--r--usr.sbin/fdformat/fdformat.c354
-rw-r--r--usr.sbin/fdwrite/Makefile16
-rw-r--r--usr.sbin/fdwrite/fdwrite.1121
-rw-r--r--usr.sbin/fdwrite/fdwrite.c194
-rw-r--r--usr.sbin/i4b/Makefile4
-rw-r--r--usr.sbin/i4b/Makefile.inc14
-rw-r--r--usr.sbin/i4b/alawulaw/Makefile7
-rw-r--r--usr.sbin/i4b/alawulaw/alaw2ulaw.170
-rw-r--r--usr.sbin/i4b/alawulaw/alaw2ulaw.c126
-rw-r--r--usr.sbin/i4b/alawulaw/ulaw2alaw.170
-rw-r--r--usr.sbin/i4b/dtmfdecode/Makefile15
-rw-r--r--usr.sbin/i4b/dtmfdecode/README44
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.161
-rw-r--r--usr.sbin/i4b/dtmfdecode/dtmfdecode.c158
-rw-r--r--usr.sbin/i4b/isdnd/Makefile34
-rw-r--r--usr.sbin/i4b/isdnd/alias.c191
-rw-r--r--usr.sbin/i4b/isdnd/config.h61
-rw-r--r--usr.sbin/i4b/isdnd/controller.c329
-rw-r--r--usr.sbin/i4b/isdnd/curses.c633
-rw-r--r--usr.sbin/i4b/isdnd/dial.c153
-rw-r--r--usr.sbin/i4b/isdnd/exec.c280
-rw-r--r--usr.sbin/i4b/isdnd/fsm.c446
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.8417
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.acct.5108
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.h754
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rates.5115
-rw-r--r--usr.sbin/i4b/isdnd/isdnd.rc.5723
-rw-r--r--usr.sbin/i4b/isdnd/log.c242
-rw-r--r--usr.sbin/i4b/isdnd/main.c706
-rw-r--r--usr.sbin/i4b/isdnd/monitor.c811
-rw-r--r--usr.sbin/i4b/isdnd/msghdl.c985
-rw-r--r--usr.sbin/i4b/isdnd/pathnames.h58
-rw-r--r--usr.sbin/i4b/isdnd/pcause.c227
-rw-r--r--usr.sbin/i4b/isdnd/process.c219
-rw-r--r--usr.sbin/i4b/isdnd/rates.c507
-rw-r--r--usr.sbin/i4b/isdnd/rc_config.c1178
-rw-r--r--usr.sbin/i4b/isdnd/rc_parse.y392
-rw-r--r--usr.sbin/i4b/isdnd/rc_scan.l172
-rw-r--r--usr.sbin/i4b/isdnd/support.c853
-rw-r--r--usr.sbin/i4b/isdnd/timer.c411
-rw-r--r--usr.sbin/i4b/isdnd/vararray.h120
-rw-r--r--usr.sbin/i4b/isdndebug/Makefile5
-rw-r--r--usr.sbin/i4b/isdndebug/isdndebug.8102
-rw-r--r--usr.sbin/i4b/isdndebug/main.c553
-rw-r--r--usr.sbin/i4b/isdndecode/Makefile7
-rw-r--r--usr.sbin/i4b/isdndecode/decode.h74
-rw-r--r--usr.sbin/i4b/isdndecode/facility.c906
-rw-r--r--usr.sbin/i4b/isdndecode/facility.h154
-rw-r--r--usr.sbin/i4b/isdndecode/isdndecode.8190
-rw-r--r--usr.sbin/i4b/isdndecode/layer1.c80
-rw-r--r--usr.sbin/i4b/isdndecode/layer2.c298
-rw-r--r--usr.sbin/i4b/isdndecode/layer3.c508
-rw-r--r--usr.sbin/i4b/isdndecode/layer3_subr.c1045
-rw-r--r--usr.sbin/i4b/isdndecode/main.c774
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.c328
-rw-r--r--usr.sbin/i4b/isdndecode/pcause.h109
-rw-r--r--usr.sbin/i4b/isdnmonitor/Makefile8
-rw-r--r--usr.sbin/i4b/isdnmonitor/isdnmonitor.840
-rw-r--r--usr.sbin/i4b/isdnmonitor/main.c675
-rw-r--r--usr.sbin/i4b/isdnmonitor/monitor.h263
-rw-r--r--usr.sbin/i4b/isdntel/Makefile9
-rw-r--r--usr.sbin/i4b/isdntel/alias.c139
-rw-r--r--usr.sbin/i4b/isdntel/alias.h49
-rw-r--r--usr.sbin/i4b/isdntel/defs.h156
-rw-r--r--usr.sbin/i4b/isdntel/display.c252
-rw-r--r--usr.sbin/i4b/isdntel/files.c306
-rw-r--r--usr.sbin/i4b/isdntel/isdntel.894
-rw-r--r--usr.sbin/i4b/isdntel/main.c395
-rw-r--r--usr.sbin/i4b/isdntelctl/Makefile5
-rw-r--r--usr.sbin/i4b/isdntelctl/isdntelctl.887
-rw-r--r--usr.sbin/i4b/isdntelctl/main.c224
-rw-r--r--usr.sbin/i4b/isdntest/Makefile8
-rw-r--r--usr.sbin/i4b/isdntest/isdntest.8103
-rw-r--r--usr.sbin/i4b/isdntest/main.c738
-rw-r--r--usr.sbin/i4b/isdntrace/1tr6.c754
-rw-r--r--usr.sbin/i4b/isdntrace/Makefile8
-rw-r--r--usr.sbin/i4b/isdntrace/cable.txt60
-rw-r--r--usr.sbin/i4b/isdntrace/isdntrace.8205
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.c164
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_1tr6.h68
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.c328
-rw-r--r--usr.sbin/i4b/isdntrace/pcause_q850.h109
-rw-r--r--usr.sbin/i4b/isdntrace/q921.c275
-rw-r--r--usr.sbin/i4b/isdntrace/q931.c783
-rw-r--r--usr.sbin/i4b/isdntrace/q931_util.c697
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.c926
-rw-r--r--usr.sbin/i4b/isdntrace/q932_fac.h174
-rw-r--r--usr.sbin/i4b/isdntrace/trace.c809
-rw-r--r--usr.sbin/i4b/isdntrace/trace.h90
-rw-r--r--usr.sbin/i4b/man/Makefile5
-rw-r--r--usr.sbin/i4b/man/daic.498
-rw-r--r--usr.sbin/i4b/man/i4b.4108
-rw-r--r--usr.sbin/i4b/man/i4bctl.450
-rw-r--r--usr.sbin/i4b/man/i4bipr.498
-rw-r--r--usr.sbin/i4b/man/i4bisppp.4107
-rw-r--r--usr.sbin/i4b/man/i4bq921.449
-rw-r--r--usr.sbin/i4b/man/i4bq931.449
-rw-r--r--usr.sbin/i4b/man/i4brbch.450
-rw-r--r--usr.sbin/i4b/man/i4btel.452
-rw-r--r--usr.sbin/i4b/man/i4btrc.452
-rw-r--r--usr.sbin/i4b/man/isic.4379
-rw-r--r--usr.sbin/inetd/Makefile14
-rw-r--r--usr.sbin/inetd/inetd.8542
-rw-r--r--usr.sbin/inetd/inetd.c2106
-rw-r--r--usr.sbin/inetd/pathnames.h40
-rw-r--r--usr.sbin/iostat/Makefile12
-rw-r--r--usr.sbin/iostat/iostat.8400
-rw-r--r--usr.sbin/iostat/iostat.c676
-rw-r--r--usr.sbin/ipfstat/Makefile10
-rw-r--r--usr.sbin/ipftest/Makefile20
-rw-r--r--usr.sbin/ipmon/Makefile11
-rw-r--r--usr.sbin/ipnat/Makefile12
-rw-r--r--usr.sbin/ipresend/Makefile15
-rw-r--r--usr.sbin/ipsend/Makefile22
-rw-r--r--usr.sbin/iptest/Makefile15
-rw-r--r--usr.sbin/kbdcontrol/Makefile6
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.1106
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.c1015
-rw-r--r--usr.sbin/kbdcontrol/lex.h59
-rw-r--r--usr.sbin/kbdcontrol/lex.l137
-rw-r--r--usr.sbin/kbdcontrol/path.h4
-rw-r--r--usr.sbin/kbdmap/Languages.phrases37
-rw-r--r--usr.sbin/kbdmap/Makefile19
-rw-r--r--usr.sbin/kbdmap/TODO5
-rw-r--r--usr.sbin/kbdmap/kbdmap.1131
-rw-r--r--usr.sbin/kbdmap/kbdmap.pl318
-rw-r--r--usr.sbin/kernbb/Makefile9
-rw-r--r--usr.sbin/kernbb/kernbb.870
-rw-r--r--usr.sbin/kernbb/kernbb.c139
-rw-r--r--usr.sbin/keyadmin/Makefile6
-rw-r--r--usr.sbin/keyadmin/keyadmin.8239
-rw-r--r--usr.sbin/keyadmin/keyadmin.c1251
-rw-r--r--usr.sbin/keyadmin/keys18
-rw-r--r--usr.sbin/keyserv/Makefile28
-rw-r--r--usr.sbin/keyserv/crypt_server.c309
-rw-r--r--usr.sbin/keyserv/keyserv.877
-rw-r--r--usr.sbin/keyserv/keyserv.c803
-rw-r--r--usr.sbin/keyserv/keyserv.h19
-rw-r--r--usr.sbin/keyserv/keyserv_uid.c76
-rw-r--r--usr.sbin/keyserv/setkey.c550
-rw-r--r--usr.sbin/kgmon/Makefile14
-rw-r--r--usr.sbin/kgmon/kgmon.8129
-rw-r--r--usr.sbin/kgmon/kgmon.c505
-rw-r--r--usr.sbin/kvm_mkdb/Makefile9
-rw-r--r--usr.sbin/kvm_mkdb/extern.h38
-rw-r--r--usr.sbin/kvm_mkdb/kvm_mkdb.870
-rw-r--r--usr.sbin/kvm_mkdb/kvm_mkdb.c126
-rw-r--r--usr.sbin/kvm_mkdb/nlist.c368
-rw-r--r--usr.sbin/kvm_mkdb/testdb.c117
-rw-r--r--usr.sbin/lpr/Makefile7
-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/Makefile10
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.896
-rw-r--r--usr.sbin/lpr/chkprintcap/chkprintcap.c277
-rw-r--r--usr.sbin/lpr/common_source/Makefile18
-rw-r--r--usr.sbin/lpr/common_source/common.c292
-rw-r--r--usr.sbin/lpr/common_source/displayq.c487
-rw-r--r--usr.sbin/lpr/common_source/lp.h233
-rw-r--r--usr.sbin/lpr/common_source/lp.local.h81
-rw-r--r--usr.sbin/lpr/common_source/net.c260
-rw-r--r--usr.sbin/lpr/common_source/pathnames.h51
-rw-r--r--usr.sbin/lpr/common_source/printcap.c428
-rw-r--r--usr.sbin/lpr/common_source/recvjob.c375
-rw-r--r--usr.sbin/lpr/common_source/request.c80
-rw-r--r--usr.sbin/lpr/common_source/rmjob.c399
-rw-r--r--usr.sbin/lpr/common_source/startdaemon.c110
-rw-r--r--usr.sbin/lpr/filters.ru/Makefile12
-rw-r--r--usr.sbin/lpr/filters.ru/bjc-240.sh.sample64
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt.c101
-rw-r--r--usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c101
-rw-r--r--usr.sbin/lpr/filters/Makefile7
-rw-r--r--usr.sbin/lpr/filters/lpf.c219
-rw-r--r--usr.sbin/lpr/lp/Makefile10
-rw-r--r--usr.sbin/lpr/lp/lp.1107
-rw-r--r--usr.sbin/lpr/lp/lp.sh71
-rw-r--r--usr.sbin/lpr/lpc/Makefile14
-rw-r--r--usr.sbin/lpr/lpc/cmds.c890
-rw-r--r--usr.sbin/lpr/lpc/cmdtab.c83
-rw-r--r--usr.sbin/lpr/lpc/extern.h61
-rw-r--r--usr.sbin/lpr/lpc/lpc.8175
-rw-r--r--usr.sbin/lpr/lpc/lpc.c330
-rw-r--r--usr.sbin/lpr/lpc/lpc.h48
-rw-r--r--usr.sbin/lpr/lpd/Makefile11
-rw-r--r--usr.sbin/lpr/lpd/extern.h50
-rw-r--r--usr.sbin/lpr/lpd/lpd.8256
-rw-r--r--usr.sbin/lpr/lpd/lpd.c634
-rw-r--r--usr.sbin/lpr/lpd/lpdchar.c1071
-rw-r--r--usr.sbin/lpr/lpd/modes.c236
-rw-r--r--usr.sbin/lpr/lpd/printjob.c1650
-rw-r--r--usr.sbin/lpr/lpd/recvjob.c376
-rw-r--r--usr.sbin/lpr/lpq/Makefile13
-rw-r--r--usr.sbin/lpr/lpq/lpq.1136
-rw-r--r--usr.sbin/lpr/lpq/lpq.c201
-rw-r--r--usr.sbin/lpr/lpr/Makefile15
-rw-r--r--usr.sbin/lpr/lpr/lpr.1253
-rw-r--r--usr.sbin/lpr/lpr/lpr.c787
-rw-r--r--usr.sbin/lpr/lpr/printcap.5369
-rw-r--r--usr.sbin/lpr/lprm/Makefile14
-rw-r--r--usr.sbin/lpr/lprm/lprm.1145
-rw-r--r--usr.sbin/lpr/lprm/lprm.c165
-rw-r--r--usr.sbin/lpr/lptest/Makefile5
-rw-r--r--usr.sbin/lpr/lptest/lptest.174
-rw-r--r--usr.sbin/lpr/lptest/lptest.c83
-rw-r--r--usr.sbin/lpr/pac/Makefile11
-rw-r--r--usr.sbin/lpr/pac/pac.8106
-rw-r--r--usr.sbin/lpr/pac/pac.c462
-rw-r--r--usr.sbin/lpr/runqueue/extern.h48
-rw-r--r--usr.sbin/lpr/runqueue/lpdchar.c1071
-rw-r--r--usr.sbin/lpr/runqueue/modes.c234
-rw-r--r--usr.sbin/lpr/runqueue/printjob.c1652
-rw-r--r--usr.sbin/lptcontrol/Makefile5
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.886
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.c97
-rw-r--r--usr.sbin/mailstats/Makefile9
-rw-r--r--usr.sbin/makemap/Makefile12
-rw-r--r--usr.sbin/manctl/Makefile10
-rw-r--r--usr.sbin/manctl/manctl.855
-rw-r--r--usr.sbin/manctl/manctl.sh376
-rw-r--r--usr.sbin/mixer/Makefile6
-rw-r--r--usr.sbin/mixer/mixer.8137
-rw-r--r--usr.sbin/mixer/mixer.c233
-rw-r--r--usr.sbin/mount_portalfs/Makefile13
-rw-r--r--usr.sbin/mount_portalfs/activate.c214
-rw-r--r--usr.sbin/mount_portalfs/conf.c340
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.8149
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c293
-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.c55
-rw-r--r--usr.sbin/mount_portalfs/pt_exec.c60
-rw-r--r--usr.sbin/mount_portalfs/pt_file.c109
-rw-r--r--usr.sbin/mount_portalfs/pt_tcp.c167
-rw-r--r--usr.sbin/mount_portalfs/pt_tcplisten.c206
-rw-r--r--usr.sbin/mountd/Makefile9
-rw-r--r--usr.sbin/mountd/exports.5293
-rw-r--r--usr.sbin/mountd/mountd.8147
-rw-r--r--usr.sbin/mountd/mountd.c2211
-rw-r--r--usr.sbin/mountd/netgroup.5186
-rw-r--r--usr.sbin/mountd/pathnames.h39
-rw-r--r--usr.sbin/moused/Makefile8
-rw-r--r--usr.sbin/moused/moused.8570
-rw-r--r--usr.sbin/moused/moused.c2434
-rw-r--r--usr.sbin/mptable/Makefile8
-rw-r--r--usr.sbin/mptable/mptable.165
-rw-r--r--usr.sbin/mptable/mptable.c1130
-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/Makefile17
-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.c1061
-rw-r--r--usr.sbin/mrouted/map-mbone.880
-rw-r--r--usr.sbin/mrouted/map-mbone/Makefile14
-rw-r--r--usr.sbin/mrouted/mapper.c1046
-rw-r--r--usr.sbin/mrouted/mrinfo.876
-rw-r--r--usr.sbin/mrouted/mrinfo.c634
-rw-r--r--usr.sbin/mrouted/mrinfo/Makefile14
-rw-r--r--usr.sbin/mrouted/mrouted.8583
-rw-r--r--usr.sbin/mrouted/mrouted.conf44
-rw-r--r--usr.sbin/mrouted/mrouted/Makefile19
-rw-r--r--usr.sbin/mrouted/mtrace.8550
-rw-r--r--usr.sbin/mrouted/mtrace.c3177
-rw-r--r--usr.sbin/mrouted/mtrace.h89
-rw-r--r--usr.sbin/mrouted/mtrace/Makefile12
-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.c124
-rw-r--r--usr.sbin/mrouted/vif.c1862
-rw-r--r--usr.sbin/mrouted/vif.h237
-rw-r--r--usr.sbin/mtest/Makefile4
-rw-r--r--usr.sbin/mtest/mtest.852
-rw-r--r--usr.sbin/mtest/mtest.c224
-rw-r--r--usr.sbin/mtree/Makefile15
-rw-r--r--usr.sbin/mtree/compare.c341
-rw-r--r--usr.sbin/mtree/create.c397
-rw-r--r--usr.sbin/mtree/extern.h43
-rw-r--r--usr.sbin/mtree/misc.c110
-rw-r--r--usr.sbin/mtree/mtree.8311
-rw-r--r--usr.sbin/mtree/mtree.c163
-rw-r--r--usr.sbin/mtree/mtree.h96
-rw-r--r--usr.sbin/mtree/spec.c317
-rw-r--r--usr.sbin/mtree/verify.c210
-rw-r--r--usr.sbin/named.reload/Makefile19
-rw-r--r--usr.sbin/named.reload/named.reload.869
-rw-r--r--usr.sbin/named.reload/named.reload.sh7
-rw-r--r--usr.sbin/named.restart/Makefile19
-rw-r--r--usr.sbin/named.restart/named.restart.873
-rw-r--r--usr.sbin/named.restart/named.restart.sh7
-rw-r--r--usr.sbin/named/Makefile24
-rw-r--r--usr.sbin/named/Makefile.inc56
-rw-r--r--usr.sbin/named/Makefile.maninc57
-rw-r--r--usr.sbin/ndc/Makefile22
-rw-r--r--usr.sbin/ndc/ndcedit.awk41
-rw-r--r--usr.sbin/newsyslog/Makefile7
-rw-r--r--usr.sbin/newsyslog/newsyslog.8279
-rw-r--r--usr.sbin/newsyslog/newsyslog.c767
-rw-r--r--usr.sbin/newsyslog/pathnames.h27
-rw-r--r--usr.sbin/nfsd/Makefile17
-rw-r--r--usr.sbin/nfsd/nfsd.8134
-rw-r--r--usr.sbin/nfsd/nfsd.c673
-rw-r--r--usr.sbin/nologin/Makefile12
-rw-r--r--usr.sbin/nologin/nologin.566
-rw-r--r--usr.sbin/nologin/nologin.856
-rw-r--r--usr.sbin/nologin/nologin.sh39
-rw-r--r--usr.sbin/nslookup/Makefile21
-rw-r--r--usr.sbin/nsupdate/Makefile10
-rw-r--r--usr.sbin/pccard/Makefile7
-rw-r--r--usr.sbin/pccard/Makefile.inc2
-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/enabler.c153
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.c95
-rw-r--r--usr.sbin/pccard/pccardc/pccardmem.c71
-rw-r--r--usr.sbin/pccard/pccardc/printcis.c709
-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/Makefile12
-rw-r--r--usr.sbin/pccard/pccardd/cardd.c563
-rw-r--r--usr.sbin/pccard/pccardd/cardd.h148
-rw-r--r--usr.sbin/pccard/pccardd/file.c635
-rw-r--r--usr.sbin/pccard/pccardd/pccard.conf.5202
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.8158
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.c106
-rw-r--r--usr.sbin/pccard/pccardd/readcis.c617
-rw-r--r--usr.sbin/pccard/pccardd/readcis.h133
-rw-r--r--usr.sbin/pccard/pccardd/util.c272
-rw-r--r--usr.sbin/pciconf/Makefile7
-rw-r--r--usr.sbin/pciconf/pathnames.h1
-rw-r--r--usr.sbin/pciconf/pciconf.8193
-rw-r--r--usr.sbin/pciconf/pciconf.c275
-rw-r--r--usr.sbin/pcvt/Makefile7
-rw-r--r--usr.sbin/pcvt/Makefile.inc9
-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/BugList62
-rw-r--r--usr.sbin/pcvt/Misc/Doc/ChangeLog899
-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/Makefile15
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Manifest162
-rw-r--r--usr.sbin/pcvt/Misc/Doc/NotesAndHints321
-rw-r--r--usr.sbin/pcvt/Misc/Doc/TestedHardware79
-rw-r--r--usr.sbin/pcvt/Misc/Doc/ToDo13
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Makefile13
-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/rc.local264
-rw-r--r--usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu594
-rw-r--r--usr.sbin/pcvt/Misc/Etc/xmodmap-german117
-rw-r--r--usr.sbin/pcvt/Misc/Makefile15
-rw-r--r--usr.sbin/pcvt/Misc/Makefile.inc3
-rw-r--r--usr.sbin/pcvt/Misc/README.FIRST291
-rw-r--r--usr.sbin/pcvt/cursor/Makefile3
-rw-r--r--usr.sbin/pcvt/cursor/cursor.176
-rw-r--r--usr.sbin/pcvt/cursor/cursor.c157
-rw-r--r--usr.sbin/pcvt/demo/Makefile54
-rw-r--r--usr.sbin/pcvt/demo/README20
-rw-r--r--usr.sbin/pcvt/demo/chardemo.vt.gz.uu53
-rw-r--r--usr.sbin/pcvt/demo/colors.vt.gz.uu15
-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.c111
-rw-r--r--usr.sbin/pcvt/demo/sgr.vt.gz.uu11
-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/Makefile29
-rw-r--r--usr.sbin/pcvt/fed/edit.c340
-rw-r--r--usr.sbin/pcvt/fed/fed.c168
-rw-r--r--usr.sbin/pcvt/fed/fed.h127
-rw-r--r--usr.sbin/pcvt/fed/misc.c353
-rw-r--r--usr.sbin/pcvt/fed/select.c334
-rw-r--r--usr.sbin/pcvt/fontedit/Makefile3
-rw-r--r--usr.sbin/pcvt/fontedit/README36
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.158
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.c925
-rw-r--r--usr.sbin/pcvt/fonts/COPYRIGHT38
-rw-r--r--usr.sbin/pcvt/fonts/Makefile19
-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/Makefile4
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.891
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.c308
-rw-r--r--usr.sbin/pcvt/kbdio/Makefile36
-rw-r--r--usr.sbin/pcvt/kbdio/kbdio.y338
-rw-r--r--usr.sbin/pcvt/kbdio/lex.l107
-rw-r--r--usr.sbin/pcvt/kcon/Makefile14
-rw-r--r--usr.sbin/pcvt/kcon/kcon.1122
-rw-r--r--usr.sbin/pcvt/kcon/kcon.c753
-rw-r--r--usr.sbin/pcvt/keycap/Makefile32
-rw-r--r--usr.sbin/pcvt/keycap/keycap.3124
-rw-r--r--usr.sbin/pcvt/keycap/keycap.c377
-rw-r--r--usr.sbin/pcvt/keycap/keycap.h49
-rw-r--r--usr.sbin/pcvt/keycap/keycap.src630
-rw-r--r--usr.sbin/pcvt/keycap/man5/keycap.5130
-rw-r--r--usr.sbin/pcvt/loadfont/Makefile3
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.190
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.c345
-rw-r--r--usr.sbin/pcvt/mcon/Makefile3
-rw-r--r--usr.sbin/pcvt/mcon/mcon.1166
-rw-r--r--usr.sbin/pcvt/mcon/mcon.c193
-rw-r--r--usr.sbin/pcvt/scon/Makefile3
-rw-r--r--usr.sbin/pcvt/scon/scon.1214
-rw-r--r--usr.sbin/pcvt/scon/scon.c856
-rw-r--r--usr.sbin/pcvt/set2061/CAUTION28
-rw-r--r--usr.sbin/pcvt/set2061/ICD2061Aalt.c297
-rw-r--r--usr.sbin/pcvt/set2061/Makefile13
-rw-r--r--usr.sbin/pcvt/set2061/README22
-rw-r--r--usr.sbin/pcvt/set2061/compiler.h341
-rw-r--r--usr.sbin/pcvt/set2061/main.c112
-rw-r--r--usr.sbin/pcvt/userkeys/Makefile3
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.1131
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.c297
-rw-r--r--usr.sbin/pcvt/vgaio/CAUTION28
-rw-r--r--usr.sbin/pcvt/vgaio/Makefile38
-rw-r--r--usr.sbin/pcvt/vgaio/lex.l86
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.8143
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.h63
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.y255
-rw-r--r--usr.sbin/pcvt/vttest/Makefile6
-rw-r--r--usr.sbin/pcvt/vttest/README57
-rw-r--r--usr.sbin/pcvt/vttest/esc.c398
-rw-r--r--usr.sbin/pcvt/vttest/header.h43
-rw-r--r--usr.sbin/pcvt/vttest/main.c2016
-rw-r--r--usr.sbin/pcvt/vttest/vttest.113
-rw-r--r--usr.sbin/periodic/Makefile11
-rw-r--r--usr.sbin/periodic/periodic.8137
-rw-r--r--usr.sbin/periodic/periodic.sh67
-rw-r--r--usr.sbin/pkg_install/Makefile3
-rw-r--r--usr.sbin/pkg_install/Makefile.inc10
-rw-r--r--usr.sbin/pkg_install/README8
-rw-r--r--usr.sbin/pkg_install/add/Makefile12
-rw-r--r--usr.sbin/pkg_install/add/add.h44
-rw-r--r--usr.sbin/pkg_install/add/extract.c256
-rw-r--r--usr.sbin/pkg_install/add/futil.c93
-rw-r--r--usr.sbin/pkg_install/add/main.c212
-rw-r--r--usr.sbin/pkg_install/add/perform.c523
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1457
-rw-r--r--usr.sbin/pkg_install/create/Makefile12
-rw-r--r--usr.sbin/pkg_install/create/create.h48
-rw-r--r--usr.sbin/pkg_install/create/main.c171
-rw-r--r--usr.sbin/pkg_install/create/perform.c344
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1462
-rw-r--r--usr.sbin/pkg_install/create/pl.c228
-rw-r--r--usr.sbin/pkg_install/delete/Makefile11
-rw-r--r--usr.sbin/pkg_install/delete/delete.h33
-rw-r--r--usr.sbin/pkg_install/delete/main.c108
-rw-r--r--usr.sbin/pkg_install/delete/perform.c295
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1246
-rw-r--r--usr.sbin/pkg_install/info/Makefile11
-rw-r--r--usr.sbin/pkg_install/info/info.h59
-rw-r--r--usr.sbin/pkg_install/info/main.c159
-rw-r--r--usr.sbin/pkg_install/info/perform.c216
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1169
-rw-r--r--usr.sbin/pkg_install/info/show.c199
-rw-r--r--usr.sbin/pkg_install/lib/Makefile10
-rw-r--r--usr.sbin/pkg_install/lib/exec.c62
-rw-r--r--usr.sbin/pkg_install/lib/file.c556
-rw-r--r--usr.sbin/pkg_install/lib/global.c35
-rw-r--r--usr.sbin/pkg_install/lib/lib.h178
-rw-r--r--usr.sbin/pkg_install/lib/msg.c77
-rw-r--r--usr.sbin/pkg_install/lib/pen.c177
-rw-r--r--usr.sbin/pkg_install/lib/plist.c507
-rw-r--r--usr.sbin/pkg_install/lib/str.c111
-rwxr-xr-xusr.sbin/pkg_install/tkpkg186
-rw-r--r--usr.sbin/pnpinfo/Makefile11
-rw-r--r--usr.sbin/portmap/Makefile13
-rw-r--r--usr.sbin/portmap/from_local.c191
-rw-r--r--usr.sbin/portmap/pmap_check.c264
-rw-r--r--usr.sbin/portmap/pmap_check.h11
-rw-r--r--usr.sbin/portmap/pmap_dump/Makefile7
-rw-r--r--usr.sbin/portmap/pmap_dump/pmap_dump.c68
-rw-r--r--usr.sbin/portmap/pmap_set/Makefile7
-rw-r--r--usr.sbin/portmap/pmap_set/pmap_set.c79
-rw-r--r--usr.sbin/portmap/portmap.8114
-rw-r--r--usr.sbin/portmap/portmap.c613
-rw-r--r--usr.sbin/ppp/Makefile61
-rw-r--r--usr.sbin/ppp/README.alias352
-rw-r--r--usr.sbin/ppp/README.changes82
-rw-r--r--usr.sbin/ppp/README.devel19
-rw-r--r--usr.sbin/ppp/README.nat352
-rw-r--r--usr.sbin/ppp/alias_cmd.c306
-rw-r--r--usr.sbin/ppp/alias_cmd.h13
-rw-r--r--usr.sbin/ppp/arp.c323
-rw-r--r--usr.sbin/ppp/arp.h29
-rw-r--r--usr.sbin/ppp/async.c184
-rw-r--r--usr.sbin/ppp/async.h52
-rw-r--r--usr.sbin/ppp/auth.c366
-rw-r--r--usr.sbin/ppp/auth.h62
-rw-r--r--usr.sbin/ppp/bundle.c1786
-rw-r--r--usr.sbin/ppp/bundle.h191
-rw-r--r--usr.sbin/ppp/cbcp.c729
-rw-r--r--usr.sbin/ppp/cbcp.h65
-rw-r--r--usr.sbin/ppp/ccp.c638
-rw-r--r--usr.sbin/ppp/ccp.h128
-rw-r--r--usr.sbin/ppp/chap.c760
-rw-r--r--usr.sbin/ppp/chap.h55
-rw-r--r--usr.sbin/ppp/chap_ms.c124
-rw-r--r--usr.sbin/ppp/chap_ms.h32
-rw-r--r--usr.sbin/ppp/chat.c804
-rw-r--r--usr.sbin/ppp/chat.h82
-rw-r--r--usr.sbin/ppp/command.c2547
-rw-r--r--usr.sbin/ppp/command.h65
-rw-r--r--usr.sbin/ppp/datalink.c1368
-rw-r--r--usr.sbin/ppp/datalink.h151
-rw-r--r--usr.sbin/ppp/deflate.c593
-rw-r--r--usr.sbin/ppp/deflate.h30
-rw-r--r--usr.sbin/ppp/defs.c153
-rw-r--r--usr.sbin/ppp/defs.h97
-rw-r--r--usr.sbin/ppp/descriptor.h53
-rw-r--r--usr.sbin/ppp/filter.c549
-rw-r--r--usr.sbin/ppp/filter.h93
-rw-r--r--usr.sbin/ppp/fsm.c1050
-rw-r--r--usr.sbin/ppp/fsm.h173
-rw-r--r--usr.sbin/ppp/hdlc.c652
-rw-r--r--usr.sbin/ppp/hdlc.h115
-rw-r--r--usr.sbin/ppp/id.c267
-rw-r--r--usr.sbin/ppp/id.h47
-rw-r--r--usr.sbin/ppp/iface.c484
-rw-r--r--usr.sbin/ppp/iface.h60
-rw-r--r--usr.sbin/ppp/ip.c570
-rw-r--r--usr.sbin/ppp/ip.h34
-rw-r--r--usr.sbin/ppp/ipcp.c1230
-rw-r--r--usr.sbin/ppp/ipcp.h119
-rw-r--r--usr.sbin/ppp/iplist.c224
-rw-r--r--usr.sbin/ppp/iplist.h51
-rw-r--r--usr.sbin/ppp/lcp.c1185
-rw-r--r--usr.sbin/ppp/lcp.h140
-rw-r--r--usr.sbin/ppp/lcpproto.h43
-rw-r--r--usr.sbin/ppp/link.c210
-rw-r--r--usr.sbin/ppp/link.h69
-rw-r--r--usr.sbin/ppp/log.c473
-rw-r--r--usr.sbin/ppp/log.h96
-rw-r--r--usr.sbin/ppp/lqr.c321
-rw-r--r--usr.sbin/ppp/lqr.h59
-rw-r--r--usr.sbin/ppp/main.c578
-rw-r--r--usr.sbin/ppp/main.h25
-rw-r--r--usr.sbin/ppp/mbuf.c232
-rw-r--r--usr.sbin/ppp/mbuf.h67
-rw-r--r--usr.sbin/ppp/modem.c1131
-rw-r--r--usr.sbin/ppp/modem.h43
-rw-r--r--usr.sbin/ppp/mp.c1038
-rw-r--r--usr.sbin/ppp/mp.h136
-rw-r--r--usr.sbin/ppp/nat_cmd.c306
-rw-r--r--usr.sbin/ppp/nat_cmd.h13
-rw-r--r--usr.sbin/ppp/pap.c249
-rw-r--r--usr.sbin/ppp/pap.h32
-rw-r--r--usr.sbin/ppp/physical.c234
-rw-r--r--usr.sbin/ppp/physical.h107
-rw-r--r--usr.sbin/ppp/ppp.84499
-rw-r--r--usr.sbin/ppp/ppp.8.m44499
-rw-r--r--usr.sbin/ppp/pred.c340
-rw-r--r--usr.sbin/ppp/pred.h23
-rw-r--r--usr.sbin/ppp/probe.c53
-rw-r--r--usr.sbin/ppp/probe.h33
-rw-r--r--usr.sbin/ppp/prompt.c555
-rw-r--r--usr.sbin/ppp/prompt.h91
-rw-r--r--usr.sbin/ppp/radius.c423
-rw-r--r--usr.sbin/ppp/radius.h58
-rw-r--r--usr.sbin/ppp/route.c580
-rw-r--r--usr.sbin/ppp/route.h54
-rw-r--r--usr.sbin/ppp/server.c291
-rw-r--r--usr.sbin/ppp/server.h49
-rw-r--r--usr.sbin/ppp/sig.c107
-rw-r--r--usr.sbin/ppp/sig.h35
-rw-r--r--usr.sbin/ppp/slcompress.c590
-rw-r--r--usr.sbin/ppp/slcompress.h149
-rw-r--r--usr.sbin/ppp/systems.c407
-rw-r--r--usr.sbin/ppp/systems.h37
-rw-r--r--usr.sbin/ppp/throughput.c201
-rw-r--r--usr.sbin/ppp/throughput.h57
-rw-r--r--usr.sbin/ppp/timer.c244
-rw-r--r--usr.sbin/ppp/timer.h47
-rw-r--r--usr.sbin/ppp/tun.c89
-rw-r--r--usr.sbin/ppp/tun.h46
-rw-r--r--usr.sbin/ppp/ua.h74
-rw-r--r--usr.sbin/ppp/vjcomp.c182
-rw-r--r--usr.sbin/ppp/vjcomp.h36
-rw-r--r--usr.sbin/pppctl/Makefile10
-rw-r--r--usr.sbin/pppctl/pppctl.8187
-rw-r--r--usr.sbin/pppctl/pppctl.c425
-rw-r--r--usr.sbin/pppd/Makefile36
-rw-r--r--usr.sbin/pppd/RELNOTES724
-rw-r--r--usr.sbin/pppd/auth.c1630
-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.c1857
-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.c1716
-rw-r--r--usr.sbin/pppd/options.c2569
-rw-r--r--usr.sbin/pppd/patchlevel.h6
-rw-r--r--usr.sbin/pppd/pathnames.h32
-rw-r--r--usr.sbin/pppd/pppd.81171
-rw-r--r--usr.sbin/pppd/pppd.h493
-rw-r--r--usr.sbin/pppd/sys-bsd.c1581
-rw-r--r--usr.sbin/pppd/upap.c618
-rw-r--r--usr.sbin/pppd/upap.h87
-rw-r--r--usr.sbin/pppstats/Makefile10
-rw-r--r--usr.sbin/pppstats/pppstats.8217
-rw-r--r--usr.sbin/pppstats/pppstats.c519
-rw-r--r--usr.sbin/praliases/Makefile9
-rw-r--r--usr.sbin/procctl/Makefile6
-rw-r--r--usr.sbin/procctl/procctl.834
-rw-r--r--usr.sbin/procctl/procctl.c81
-rw-r--r--usr.sbin/pstat/Makefile13
-rw-r--r--usr.sbin/pstat/pstat.8390
-rw-r--r--usr.sbin/pstat/pstat.c1096
-rw-r--r--usr.sbin/pw/Makefile18
-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.c116
-rw-r--r--usr.sbin/pw/edgroup.c230
-rw-r--r--usr.sbin/pw/fileupd.c197
-rw-r--r--usr.sbin/pw/grupd.c170
-rw-r--r--usr.sbin/pw/psdate.c299
-rw-r--r--usr.sbin/pw/psdate.h40
-rw-r--r--usr.sbin/pw/pw.8887
-rw-r--r--usr.sbin/pw/pw.c427
-rw-r--r--usr.sbin/pw/pw.conf.5301
-rw-r--r--usr.sbin/pw/pw.h129
-rw-r--r--usr.sbin/pw/pw_conf.c495
-rw-r--r--usr.sbin/pw/pw_group.c334
-rw-r--r--usr.sbin/pw/pw_log.c67
-rw-r--r--usr.sbin/pw/pw_nis.c72
-rw-r--r--usr.sbin/pw/pw_user.c1126
-rw-r--r--usr.sbin/pw/pw_vpw.c316
-rw-r--r--usr.sbin/pw/pwupd.c194
-rw-r--r--usr.sbin/pw/pwupd.h156
-rw-r--r--usr.sbin/pw/rm_r.c74
-rw-r--r--usr.sbin/pwd_mkdb/Makefile8
-rw-r--r--usr.sbin/pwd_mkdb/pw_scan.c144
-rw-r--r--usr.sbin/pwd_mkdb/pw_scan.h36
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.8157
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c619
-rw-r--r--usr.sbin/quot/Makefile6
-rw-r--r--usr.sbin/quot/quot.8103
-rw-r--r--usr.sbin/quot/quot.c583
-rw-r--r--usr.sbin/quotaon/Makefile8
-rw-r--r--usr.sbin/quotaon/quotaon.8137
-rw-r--r--usr.sbin/quotaon/quotaon.c265
-rw-r--r--usr.sbin/rarpd/Makefile10
-rw-r--r--usr.sbin/rarpd/rarpd.8117
-rw-r--r--usr.sbin/rarpd/rarpd.c1011
-rw-r--r--usr.sbin/repquota/Makefile6
-rw-r--r--usr.sbin/repquota/repquota.8103
-rw-r--r--usr.sbin/repquota/repquota.c396
-rw-r--r--usr.sbin/rmt/Makefile11
-rw-r--r--usr.sbin/rmt/rmt.8218
-rw-r--r--usr.sbin/rmt/rmt.c259
-rw-r--r--usr.sbin/rndcontrol/Makefile9
-rw-r--r--usr.sbin/rndcontrol/random.4187
-rw-r--r--usr.sbin/rndcontrol/rndcontrol.896
-rw-r--r--usr.sbin/rndcontrol/rndcontrol.c121
-rw-r--r--usr.sbin/rpc.lockd/Makefile26
-rw-r--r--usr.sbin/rpc.lockd/handles.c61
-rw-r--r--usr.sbin/rpc.lockd/lockd.c107
-rw-r--r--usr.sbin/rpc.lockd/lockd.h44
-rw-r--r--usr.sbin/rpc.lockd/procs.c592
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.895
-rw-r--r--usr.sbin/rpc.lockd/test.c366
-rw-r--r--usr.sbin/rpc.statd/Makefile26
-rw-r--r--usr.sbin/rpc.statd/file.c350
-rw-r--r--usr.sbin/rpc.statd/procs.c355
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8104
-rw-r--r--usr.sbin/rpc.statd/statd.c140
-rw-r--r--usr.sbin/rpc.statd/statd.h123
-rw-r--r--usr.sbin/rpc.statd/test.c144
-rw-r--r--usr.sbin/rpc.yppasswdd/Makefile64
-rw-r--r--usr.sbin/rpc.yppasswdd/pw_copy.c143
-rw-r--r--usr.sbin/rpc.yppasswdd/pw_util.c184
-rw-r--r--usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8327
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswd_private.x72
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_extern.h82
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_main.c337
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_server.c873
-rw-r--r--usr.sbin/rpc.yppasswdd/yppwupdate34
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile36
-rw-r--r--usr.sbin/rpc.ypupdated/update.c363
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c71
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c154
-rwxr-xr-xusr.sbin/rpc.ypupdated/ypupdate33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_extern.h29
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_main.c289
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_server.c235
-rw-r--r--usr.sbin/rpc.ypxfrd/Makefile37
-rw-r--r--usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8140
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_extern.h50
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_main.c304
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_server.c147
-rw-r--r--usr.sbin/rtprio/Makefile10
-rw-r--r--usr.sbin/rtprio/rtprio.1205
-rw-r--r--usr.sbin/rtprio/rtprio.c141
-rw-r--r--usr.sbin/rwhod/Makefile6
-rw-r--r--usr.sbin/rwhod/rwhod.8224
-rw-r--r--usr.sbin/rwhod/rwhod.c736
-rw-r--r--usr.sbin/sa/Makefile7
-rw-r--r--usr.sbin/sa/extern.h100
-rw-r--r--usr.sbin/sa/main.c553
-rw-r--r--usr.sbin/sa/pathnames.h35
-rw-r--r--usr.sbin/sa/pdb.c420
-rw-r--r--usr.sbin/sa/sa.8238
-rw-r--r--usr.sbin/sa/usrdb.c284
-rw-r--r--usr.sbin/sade/Makefile85
-rw-r--r--usr.sbin/sade/command.c179
-rw-r--r--usr.sbin/sade/config.c741
-rw-r--r--usr.sbin/sade/devices.c531
-rw-r--r--usr.sbin/sade/disks.c814
-rw-r--r--usr.sbin/sade/dispatch.c436
-rw-r--r--usr.sbin/sade/dmenu.c308
-rw-r--r--usr.sbin/sade/globals.c71
-rw-r--r--usr.sbin/sade/help/partition.hlp118
-rw-r--r--usr.sbin/sade/help/slice.hlp59
-rw-r--r--usr.sbin/sade/install.c1134
-rw-r--r--usr.sbin/sade/keymap.c95
-rw-r--r--usr.sbin/sade/label.c1303
-rw-r--r--usr.sbin/sade/list.h60
-rw-r--r--usr.sbin/sade/main.c141
-rw-r--r--usr.sbin/sade/menus.c1524
-rw-r--r--usr.sbin/sade/misc.c485
-rw-r--r--usr.sbin/sade/msg.c320
-rw-r--r--usr.sbin/sade/rtermcap.c15
-rw-r--r--usr.sbin/sade/sade.8802
-rw-r--r--usr.sbin/sade/sade.h722
-rw-r--r--usr.sbin/sade/system.c396
-rw-r--r--usr.sbin/sade/termcap.c135
-rw-r--r--usr.sbin/sade/variable.c224
-rw-r--r--usr.sbin/sade/wizard.c206
-rw-r--r--usr.sbin/sendmail/Makefile44
-rw-r--r--usr.sbin/sgsc/Makefile5
-rw-r--r--usr.sbin/sgsc/sgsc.197
-rw-r--r--usr.sbin/sgsc/sgsc.c163
-rw-r--r--usr.sbin/sicontrol/Makefile5
-rw-r--r--usr.sbin/sicontrol/sicontrol.8114
-rw-r--r--usr.sbin/sicontrol/sicontrol.c587
-rw-r--r--usr.sbin/sliplogin/Makefile9
-rw-r--r--usr.sbin/sliplogin/pathnames.h47
-rw-r--r--usr.sbin/sliplogin/sliplogin.8313
-rw-r--r--usr.sbin/sliplogin/sliplogin.c543
-rw-r--r--usr.sbin/slstat/Makefile7
-rw-r--r--usr.sbin/slstat/slstat.8128
-rw-r--r--usr.sbin/slstat/slstat.c244
-rw-r--r--usr.sbin/spkrtest/Makefile11
-rw-r--r--usr.sbin/spkrtest/spkrtest.854
-rw-r--r--usr.sbin/spkrtest/spkrtest.pl138
-rw-r--r--usr.sbin/spray/Makefile8
-rw-r--r--usr.sbin/spray/spray.872
-rw-r--r--usr.sbin/spray/spray.c226
-rw-r--r--usr.sbin/stallion/Makefile5
-rw-r--r--usr.sbin/stallion/Makefile.inc7
-rw-r--r--usr.sbin/stallion/bootcode/2681.sys.uu690
-rw-r--r--usr.sbin/stallion/bootcode/Makefile20
-rw-r--r--usr.sbin/stallion/bootcode/cdk.sys.uu733
-rw-r--r--usr.sbin/stallion/bootcode/stl.4324
-rw-r--r--usr.sbin/stallion/stlload/Makefile8
-rw-r--r--usr.sbin/stallion/stlload/stlload.8124
-rw-r--r--usr.sbin/stallion/stlload/stlload.c502
-rw-r--r--usr.sbin/stallion/stlstats/Makefile9
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.8135
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.c580
-rw-r--r--usr.sbin/sysctl/Makefile10
-rw-r--r--usr.sbin/sysctl/pathconf.c240
-rw-r--r--usr.sbin/sysctl/sysctl.8243
-rw-r--r--usr.sbin/sysctl/sysctl.c502
-rw-r--r--usr.sbin/sysinstall/Makefile85
-rw-r--r--usr.sbin/sysinstall/anonFTP.c316
-rw-r--r--usr.sbin/sysinstall/cdrom.c164
-rw-r--r--usr.sbin/sysinstall/command.c179
-rw-r--r--usr.sbin/sysinstall/config.c741
-rw-r--r--usr.sbin/sysinstall/dev2c.sh80
-rw-r--r--usr.sbin/sysinstall/devices.c531
-rw-r--r--usr.sbin/sysinstall/disks.c814
-rw-r--r--usr.sbin/sysinstall/dispatch.c436
-rw-r--r--usr.sbin/sysinstall/dist.c821
-rw-r--r--usr.sbin/sysinstall/dist.h112
-rw-r--r--usr.sbin/sysinstall/dmenu.c308
-rw-r--r--usr.sbin/sysinstall/doc.c126
-rw-r--r--usr.sbin/sysinstall/dos.c94
-rw-r--r--usr.sbin/sysinstall/floppy.c155
-rw-r--r--usr.sbin/sysinstall/ftp.c253
-rw-r--r--usr.sbin/sysinstall/globals.c71
-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.hlp63
-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.hlp19
-rw-r--r--usr.sbin/sysinstall/help/media.hlp53
-rw-r--r--usr.sbin/sysinstall/help/network_device.hlp56
-rw-r--r--usr.sbin/sysinstall/help/options.hlp124
-rw-r--r--usr.sbin/sysinstall/help/partition.hlp118
-rw-r--r--usr.sbin/sysinstall/help/register.hlp76
-rw-r--r--usr.sbin/sysinstall/help/shortcuts.hlp116
-rw-r--r--usr.sbin/sysinstall/help/slice.hlp59
-rw-r--r--usr.sbin/sysinstall/help/tcp.hlp34
-rw-r--r--usr.sbin/sysinstall/help/usage.hlp68
-rw-r--r--usr.sbin/sysinstall/help/usermgmt.hlp89
-rw-r--r--usr.sbin/sysinstall/index.c705
-rw-r--r--usr.sbin/sysinstall/install.c1134
-rw-r--r--usr.sbin/sysinstall/install.cfg96
-rw-r--r--usr.sbin/sysinstall/installUpgrade.c490
-rw-r--r--usr.sbin/sysinstall/keymap.c95
-rw-r--r--usr.sbin/sysinstall/kget.c151
-rw-r--r--usr.sbin/sysinstall/label.c1303
-rw-r--r--usr.sbin/sysinstall/list.h60
-rw-r--r--usr.sbin/sysinstall/main.c141
-rw-r--r--usr.sbin/sysinstall/media.c789
-rw-r--r--usr.sbin/sysinstall/menus.c1524
-rw-r--r--usr.sbin/sysinstall/misc.c485
-rw-r--r--usr.sbin/sysinstall/mouse.c83
-rw-r--r--usr.sbin/sysinstall/msg.c320
-rw-r--r--usr.sbin/sysinstall/network.c311
-rw-r--r--usr.sbin/sysinstall/nfs.c93
-rw-r--r--usr.sbin/sysinstall/options.c306
-rw-r--r--usr.sbin/sysinstall/package.c219
-rw-r--r--usr.sbin/sysinstall/rtermcap.c15
-rw-r--r--usr.sbin/sysinstall/sysinstall.8802
-rw-r--r--usr.sbin/sysinstall/sysinstall.h722
-rw-r--r--usr.sbin/sysinstall/system.c396
-rw-r--r--usr.sbin/sysinstall/tape.c121
-rw-r--r--usr.sbin/sysinstall/tcpip.c397
-rw-r--r--usr.sbin/sysinstall/termcap.c135
-rw-r--r--usr.sbin/sysinstall/ufs.c49
-rw-r--r--usr.sbin/sysinstall/user.c727
-rw-r--r--usr.sbin/sysinstall/variable.c224
-rw-r--r--usr.sbin/sysinstall/wizard.c206
-rw-r--r--usr.sbin/syslogd/Makefile10
-rw-r--r--usr.sbin/syslogd/pathnames.h40
-rw-r--r--usr.sbin/syslogd/syslog.conf.5361
-rw-r--r--usr.sbin/syslogd/syslogd.8243
-rw-r--r--usr.sbin/syslogd/syslogd.c1903
-rw-r--r--usr.sbin/tcpdchk/Makefile20
-rw-r--r--usr.sbin/tcpdmatch/Makefile19
-rw-r--r--usr.sbin/tcpdump/Makefile5
-rw-r--r--usr.sbin/tcpdump/Makefile.inc3
-rw-r--r--usr.sbin/tcpdump/tcpdump/Makefile32
-rw-r--r--usr.sbin/tcpdump/tcpslice/Makefile21
-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.1269
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.c621
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.h59
-rw-r--r--usr.sbin/tcpdump/tcpslice/util.c56
-rw-r--r--usr.sbin/timed/Makefile5
-rw-r--r--usr.sbin/timed/SMM.doc/timed/Makefile11
-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.ms504
-rw-r--r--usr.sbin/timed/SMM.doc/timed/unused53
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/Makefile7
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/timed.ms279
-rw-r--r--usr.sbin/timed/timed/CHANGES144
-rw-r--r--usr.sbin/timed/timed/Makefile13
-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.c294
-rw-r--r--usr.sbin/timed/timed/extern.h89
-rw-r--r--usr.sbin/timed/timed/globals.h182
-rw-r--r--usr.sbin/timed/timed/master.c910
-rw-r--r--usr.sbin/timed/timed/measure.c352
-rw-r--r--usr.sbin/timed/timed/networkdelta.c264
-rw-r--r--usr.sbin/timed/timed/pathnames.h44
-rw-r--r--usr.sbin/timed/timed/readmsg.c490
-rw-r--r--usr.sbin/timed/timed/slave.c720
-rw-r--r--usr.sbin/timed/timed/timed.8226
-rw-r--r--usr.sbin/timed/timed/timed.c966
-rw-r--r--usr.sbin/timed/timedc/Makefile10
-rw-r--r--usr.sbin/timed/timedc/cmds.c526
-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.8145
-rw-r--r--usr.sbin/timed/timedc/timedc.c260
-rw-r--r--usr.sbin/timed/timedc/timedc.h64
-rw-r--r--usr.sbin/traceroute/Makefile30
-rw-r--r--usr.sbin/trpt/Makefile8
-rw-r--r--usr.sbin/trpt/trpt.8152
-rw-r--r--usr.sbin/trpt/trpt.c380
-rw-r--r--usr.sbin/tzsetup/Makefile9
-rw-r--r--usr.sbin/tzsetup/paths.h5
-rw-r--r--usr.sbin/tzsetup/tzsetup.8127
-rw-r--r--usr.sbin/tzsetup/tzsetup.c690
-rw-r--r--usr.sbin/usbd/Makefile7
-rw-r--r--usr.sbin/usbd/usbd.881
-rw-r--r--usr.sbin/usbd/usbd.c184
-rw-r--r--usr.sbin/usbdevs/Makefile8
-rw-r--r--usr.sbin/usbdevs/usbdevs.868
-rw-r--r--usr.sbin/usbdevs/usbdevs.c213
-rw-r--r--usr.sbin/vidcontrol/Makefile6
-rw-r--r--usr.sbin/vidcontrol/decode.c80
-rw-r--r--usr.sbin/vidcontrol/decode.h1
-rw-r--r--usr.sbin/vidcontrol/path.h4
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.1147
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.c584
-rw-r--r--usr.sbin/vipw/Makefile7
-rw-r--r--usr.sbin/vipw/pw_util.c256
-rw-r--r--usr.sbin/vipw/pw_util.h42
-rw-r--r--usr.sbin/vipw/vipw.893
-rw-r--r--usr.sbin/vipw/vipw.c131
-rw-r--r--usr.sbin/vnconfig/Makefile7
-rw-r--r--usr.sbin/vnconfig/vnconfig.8211
-rw-r--r--usr.sbin/vnconfig/vnconfig.c537
-rw-r--r--usr.sbin/watch/Makefile9
-rw-r--r--usr.sbin/watch/watch.886
-rw-r--r--usr.sbin/watch/watch.c430
-rw-r--r--usr.sbin/wlconfig/Makefile4
-rw-r--r--usr.sbin/wlconfig/wlconfig.848
-rw-r--r--usr.sbin/wlconfig/wlconfig.c153
-rw-r--r--usr.sbin/wormcontrol/Makefile4
-rw-r--r--usr.sbin/wormcontrol/wormcontrol.8180
-rw-r--r--usr.sbin/wormcontrol/wormcontrol.c170
-rw-r--r--usr.sbin/xntpd/COPYRIGHT62
-rw-r--r--usr.sbin/xntpd/Makefile7
-rw-r--r--usr.sbin/xntpd/Makefile.inc21
-rw-r--r--usr.sbin/xntpd/PORTING37
-rw-r--r--usr.sbin/xntpd/README156
-rw-r--r--usr.sbin/xntpd/README.FreeBSD15
-rw-r--r--usr.sbin/xntpd/RELNOTES216
-rw-r--r--usr.sbin/xntpd/TODO26
-rw-r--r--usr.sbin/xntpd/VERSION1
-rw-r--r--usr.sbin/xntpd/authstuff/Makefile20
-rw-r--r--usr.sbin/xntpd/authstuff/README13
-rw-r--r--usr.sbin/xntpd/authstuff/auth.samplekeys44
-rw-r--r--usr.sbin/xntpd/authstuff/auth.speed33
-rw-r--r--usr.sbin/xntpd/authstuff/authcert.c95
-rw-r--r--usr.sbin/xntpd/authstuff/authspeed.c315
-rw-r--r--usr.sbin/xntpd/authstuff/certdata34
-rw-r--r--usr.sbin/xntpd/authstuff/keyparity.c279
-rw-r--r--usr.sbin/xntpd/authstuff/makeIPFP.c345
-rw-r--r--usr.sbin/xntpd/authstuff/makePC1.c286
-rw-r--r--usr.sbin/xntpd/authstuff/makePC2.c238
-rw-r--r--usr.sbin/xntpd/authstuff/makeSP.c183
-rw-r--r--usr.sbin/xntpd/authstuff/md5_sample_output8
-rw-r--r--usr.sbin/xntpd/authstuff/md5driver.c211
-rw-r--r--usr.sbin/xntpd/authstuff/mkrandkeys.c167
-rw-r--r--usr.sbin/xntpd/authstuff/omakeIPFP.c361
-rw-r--r--usr.sbin/xntpd/authstuff/results2
-rw-r--r--usr.sbin/xntpd/authstuff/unixcert.c156
-rw-r--r--usr.sbin/xntpd/clockstuff/Makefile16
-rw-r--r--usr.sbin/xntpd/clockstuff/README31
-rw-r--r--usr.sbin/xntpd/clockstuff/chutest.c798
-rw-r--r--usr.sbin/xntpd/clockstuff/clktest.c511
-rw-r--r--usr.sbin/xntpd/clockstuff/propdelay.c536
-rw-r--r--usr.sbin/xntpd/conf/Config.CHATHAM211
-rw-r--r--usr.sbin/xntpd/conf/Config.HP-UX7
-rw-r--r--usr.sbin/xntpd/conf/Config.MONOMOY186
-rw-r--r--usr.sbin/xntpd/conf/Config.OSF17
-rw-r--r--usr.sbin/xntpd/conf/Config.SunOS7
-rw-r--r--usr.sbin/xntpd/conf/Config.TIGER182
-rw-r--r--usr.sbin/xntpd/conf/Config.TRURO202
-rw-r--r--usr.sbin/xntpd/conf/Config.ULTRIX7
-rw-r--r--usr.sbin/xntpd/conf/Config.VAX7
-rw-r--r--usr.sbin/xntpd/conf/Config.dartnet187
-rw-r--r--usr.sbin/xntpd/conf/Config.local190
-rw-r--r--usr.sbin/xntpd/conf/Config.plain190
-rw-r--r--usr.sbin/xntpd/conf/Config.solaris7
-rw-r--r--usr.sbin/xntpd/conf/Config.svr4167
-rw-r--r--usr.sbin/xntpd/conf/README11
-rw-r--r--usr.sbin/xntpd/conf/baldwin.conf40
-rw-r--r--usr.sbin/xntpd/conf/dewey.conf46
-rw-r--r--usr.sbin/xntpd/conf/grundoon.conf157
-rw-r--r--usr.sbin/xntpd/conf/maccarony.conf33
-rw-r--r--usr.sbin/xntpd/conf/malarky.conf27
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.dcf7719
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.gw34
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.ipl32
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.nsf156
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.shiningtree32
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.suzuki43
-rw-r--r--usr.sbin/xntpd/conf/pogo.conf34
-rw-r--r--usr.sbin/xntpd/conf/rackety.conf69
-rw-r--r--usr.sbin/xntpd/conf/snow-white.conf33
-rw-r--r--usr.sbin/xntpd/doc/README.irig306
-rw-r--r--usr.sbin/xntpd/doc/README.kern1374
-rw-r--r--usr.sbin/xntpd/doc/README.magic346
-rw-r--r--usr.sbin/xntpd/doc/README.refclock1421
-rw-r--r--usr.sbin/xntpd/doc/UofT146
-rw-r--r--usr.sbin/xntpd/doc/acts.c878
-rw-r--r--usr.sbin/xntpd/doc/notes.txt1258
-rw-r--r--usr.sbin/xntpd/doc/ntpdate.8142
-rw-r--r--usr.sbin/xntpd/doc/ntpq.8480
-rw-r--r--usr.sbin/xntpd/doc/ntptrace.865
-rw-r--r--usr.sbin/xntpd/doc/tickadj.8124
-rw-r--r--usr.sbin/xntpd/doc/xntpd.81075
-rw-r--r--usr.sbin/xntpd/doc/xntpdc.8674
-rw-r--r--usr.sbin/xntpd/include/README21
-rw-r--r--usr.sbin/xntpd/include/in.h256
-rw-r--r--usr.sbin/xntpd/include/l_stdlib.h284
-rw-r--r--usr.sbin/xntpd/include/md5.h56
-rw-r--r--usr.sbin/xntpd/include/mx4200.h40
-rw-r--r--usr.sbin/xntpd/include/ntp.h706
-rw-r--r--usr.sbin/xntpd/include/ntp_calendar.h80
-rw-r--r--usr.sbin/xntpd/include/ntp_control.h253
-rw-r--r--usr.sbin/xntpd/include/ntp_datum.h30
-rw-r--r--usr.sbin/xntpd/include/ntp_filegen.h51
-rw-r--r--usr.sbin/xntpd/include/ntp_fp.h316
-rw-r--r--usr.sbin/xntpd/include/ntp_if.h51
-rwxr-xr-xusr.sbin/xntpd/include/ntp_in.h259
-rw-r--r--usr.sbin/xntpd/include/ntp_io.h25
-rw-r--r--usr.sbin/xntpd/include/ntp_machine.h742
-rw-r--r--usr.sbin/xntpd/include/ntp_malloc.h15
-rw-r--r--usr.sbin/xntpd/include/ntp_refclock.h224
-rw-r--r--usr.sbin/xntpd/include/ntp_request.h808
-rw-r--r--usr.sbin/xntpd/include/ntp_select.h20
-rw-r--r--usr.sbin/xntpd/include/ntp_stdlib.h93
-rw-r--r--usr.sbin/xntpd/include/ntp_string.h35
-rw-r--r--usr.sbin/xntpd/include/ntp_syslog.h15
-rw-r--r--usr.sbin/xntpd/include/ntp_timex.h273
-rw-r--r--usr.sbin/xntpd/include/ntp_types.h60
-rw-r--r--usr.sbin/xntpd/include/ntp_unixtime.h119
-rw-r--r--usr.sbin/xntpd/include/ntpd.h175
-rw-r--r--usr.sbin/xntpd/include/parse.h459
-rw-r--r--usr.sbin/xntpd/include/parse_conf.h54
-rw-r--r--usr.sbin/xntpd/include/sys/bsd_audioirig.h101
-rw-r--r--usr.sbin/xntpd/include/sys/chudefs.h22
-rw-r--r--usr.sbin/xntpd/include/sys/clkdefs.h38
-rw-r--r--usr.sbin/xntpd/include/sys/parsestreams.h66
-rw-r--r--usr.sbin/xntpd/include/sys/ppsclock.h58
-rw-r--r--usr.sbin/xntpd/include/sys/timex.h290
-rw-r--r--usr.sbin/xntpd/include/sys/tpro.h34
-rw-r--r--usr.sbin/xntpd/kernel/README.kern596
-rw-r--r--usr.sbin/xntpd/kernel/chuinit.c76
-rw-r--r--usr.sbin/xntpd/kernel/clkinit.c76
-rw-r--r--usr.sbin/xntpd/lib/Makefile35
-rw-r--r--usr.sbin/xntpd/lib/README5
-rw-r--r--usr.sbin/xntpd/lib/a_md512crypt.c86
-rw-r--r--usr.sbin/xntpd/lib/a_md5decrypt.c59
-rw-r--r--usr.sbin/xntpd/lib/a_md5encrypt.c69
-rw-r--r--usr.sbin/xntpd/lib/adjtimex.c15
-rw-r--r--usr.sbin/xntpd/lib/atoint.c48
-rw-r--r--usr.sbin/xntpd/lib/atolfp.c117
-rw-r--r--usr.sbin/xntpd/lib/atouint.c33
-rw-r--r--usr.sbin/xntpd/lib/auth12crypt.c125
-rw-r--r--usr.sbin/xntpd/lib/authdecrypt.c82
-rw-r--r--usr.sbin/xntpd/lib/authdes.c.export41
-rw-r--r--usr.sbin/xntpd/lib/authencrypt.c88
-rw-r--r--usr.sbin/xntpd/lib/authkeys.c601
-rw-r--r--usr.sbin/xntpd/lib/authparity.c58
-rw-r--r--usr.sbin/xntpd/lib/authreadkeys.c191
-rw-r--r--usr.sbin/xntpd/lib/authusekey.c132
-rw-r--r--usr.sbin/xntpd/lib/buftvtots.c61
-rw-r--r--usr.sbin/xntpd/lib/caljulian.c105
-rw-r--r--usr.sbin/xntpd/lib/calleapwhen.c61
-rw-r--r--usr.sbin/xntpd/lib/caltontp.c90
-rw-r--r--usr.sbin/xntpd/lib/calyearstart.c62
-rw-r--r--usr.sbin/xntpd/lib/clocktime.c131
-rw-r--r--usr.sbin/xntpd/lib/clocktypes.c72
-rw-r--r--usr.sbin/xntpd/lib/decodenetnum.c58
-rw-r--r--usr.sbin/xntpd/lib/dofptoa.c117
-rw-r--r--usr.sbin/xntpd/lib/dolfptoa.c161
-rw-r--r--usr.sbin/xntpd/lib/emalloc.c20
-rwxr-xr-xusr.sbin/xntpd/lib/findconfig.c62
-rw-r--r--usr.sbin/xntpd/lib/fptoa.c24
-rw-r--r--usr.sbin/xntpd/lib/fptoms.c23
-rw-r--r--usr.sbin/xntpd/lib/getopt.c105
-rw-r--r--usr.sbin/xntpd/lib/gettstamp.c29
-rw-r--r--usr.sbin/xntpd/lib/hextoint.c38
-rw-r--r--usr.sbin/xntpd/lib/hextolfp.c66
-rw-r--r--usr.sbin/xntpd/lib/humandate.c61
-rw-r--r--usr.sbin/xntpd/lib/inttoa.c19
-rw-r--r--usr.sbin/xntpd/lib/lib_strbuf.c21
-rw-r--r--usr.sbin/xntpd/lib/lib_strbuf.h22
-rw-r--r--usr.sbin/xntpd/lib/machines.c61
-rw-r--r--usr.sbin/xntpd/lib/md5.c322
-rw-r--r--usr.sbin/xntpd/lib/mfptoa.c22
-rw-r--r--usr.sbin/xntpd/lib/mfptoms.c22
-rw-r--r--usr.sbin/xntpd/lib/modetoa.c33
-rw-r--r--usr.sbin/xntpd/lib/mstolfp.c99
-rw-r--r--usr.sbin/xntpd/lib/msutotsf.c35
-rw-r--r--usr.sbin/xntpd/lib/msyslog.c111
-rw-r--r--usr.sbin/xntpd/lib/netof.c24
-rw-r--r--usr.sbin/xntpd/lib/numtoa.c22
-rw-r--r--usr.sbin/xntpd/lib/numtohost.c38
-rw-r--r--usr.sbin/xntpd/lib/octtoint.c34
-rw-r--r--usr.sbin/xntpd/lib/prettydate.c44
-rw-r--r--usr.sbin/xntpd/lib/ranny.c81
-rw-r--r--usr.sbin/xntpd/lib/refnumtoa.c30
-rw-r--r--usr.sbin/xntpd/lib/syssignal.c45
-rw-r--r--usr.sbin/xntpd/lib/systime.c376
-rw-r--r--usr.sbin/xntpd/lib/tsftomsu.c37
-rw-r--r--usr.sbin/xntpd/lib/tstotod.c21
-rw-r--r--usr.sbin/xntpd/lib/tstotv.c135
-rw-r--r--usr.sbin/xntpd/lib/tvtoa.c33
-rw-r--r--usr.sbin/xntpd/lib/tvtots.c159
-rw-r--r--usr.sbin/xntpd/lib/uglydate.c49
-rw-r--r--usr.sbin/xntpd/lib/uinttoa.c19
-rw-r--r--usr.sbin/xntpd/lib/utvtoa.c21
-rw-r--r--usr.sbin/xntpd/ntpdate/Makefile19
-rw-r--r--usr.sbin/xntpd/ntpdate/README7
-rw-r--r--usr.sbin/xntpd/ntpdate/ntpdate.c1586
-rw-r--r--usr.sbin/xntpd/ntpdate/ntpdate.h89
-rw-r--r--usr.sbin/xntpd/ntpq/Makefile20
-rw-r--r--usr.sbin/xntpd/ntpq/README6
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq.c3091
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq.h97
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq_ops.c1610
-rw-r--r--usr.sbin/xntpd/ntptrace/Makefile19
-rw-r--r--usr.sbin/xntpd/ntptrace/README7
-rw-r--r--usr.sbin/xntpd/ntptrace/ntptrace.c772
-rw-r--r--usr.sbin/xntpd/ntptrace/ntptrace.h36
-rw-r--r--usr.sbin/xntpd/parse/Makefile19
-rw-r--r--usr.sbin/xntpd/parse/README100
-rw-r--r--usr.sbin/xntpd/parse/README.new_clocks212
-rw-r--r--usr.sbin/xntpd/parse/README.parse142
-rw-r--r--usr.sbin/xntpd/parse/README.parse_clocks264
-rw-r--r--usr.sbin/xntpd/parse/clk_dcf7000.c150
-rw-r--r--usr.sbin/xntpd/parse/clk_meinberg.c473
-rw-r--r--usr.sbin/xntpd/parse/clk_rawdcf.c580
-rw-r--r--usr.sbin/xntpd/parse/clk_schmid.c217
-rw-r--r--usr.sbin/xntpd/parse/clk_trimble.c140
-rw-r--r--usr.sbin/xntpd/parse/clk_trimtaip.c140
-rw-r--r--usr.sbin/xntpd/parse/clk_trimtsip.c477
-rw-r--r--usr.sbin/xntpd/parse/empty.c7
-rw-r--r--usr.sbin/xntpd/parse/parse.c1309
-rw-r--r--usr.sbin/xntpd/parse/parse_conf.c133
-rw-r--r--usr.sbin/xntpd/parse/parsesolaris.c1249
-rw-r--r--usr.sbin/xntpd/parse/parsestreams.c1363
-rw-r--r--usr.sbin/xntpd/parse/util/Makefile24
-rw-r--r--usr.sbin/xntpd/parse/util/Makefile.tmpl49
-rw-r--r--usr.sbin/xntpd/parse/util/README19
-rw-r--r--usr.sbin/xntpd/parse/util/dcfd.c1643
-rw-r--r--usr.sbin/xntpd/parse/util/parsetest.c275
-rw-r--r--usr.sbin/xntpd/parse/util/testdcf.c488
-rw-r--r--usr.sbin/xntpd/refclocks/Dependencies30
-rw-r--r--usr.sbin/xntpd/refclocks/README4
-rwxr-xr-xusr.sbin/xntpd/refclocks/check2
-rwxr-xr-xusr.sbin/xntpd/refclocks/echon2
-rwxr-xr-xusr.sbin/xntpd/refclocks/query11
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.AS220129
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.CHU24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.DATUM22
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.GOES32
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.GPSTM33
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.IRIG24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.LEITCH29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK22
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MOTO29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MSFEES26
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MX420027
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.NMEA23
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.OMEGA29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.PARSE55
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.PST37
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.TPRO24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.TRAK29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.WWVB38
-rw-r--r--usr.sbin/xntpd/refclocks/rconfig130
-rw-r--r--usr.sbin/xntpd/refclocks/setup16
-rw-r--r--usr.sbin/xntpd/refclocks/setupfn27
-rwxr-xr-xusr.sbin/xntpd/scripts/Guess.sh130
-rw-r--r--usr.sbin/xntpd/scripts/README41
-rwxr-xr-xusr.sbin/xntpd/scripts/autoconf885
-rwxr-xr-xusr.sbin/xntpd/scripts/install.sh100
-rwxr-xr-xusr.sbin/xntpd/scripts/makeconfig.sh85
-rwxr-xr-xusr.sbin/xntpd/scripts/mklinks9
-rwxr-xr-xusr.sbin/xntpd/scripts/mkversion36
-rw-r--r--usr.sbin/xntpd/scripts/monitoring/README154
-rw-r--r--usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE89
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/lr.pl145
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntp.pl477
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntploopstat457
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntploopwatch1631
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntptrap453
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/timelocal.pl78
-rwxr-xr-xusr.sbin/xntpd/scripts/ntp-groper95
-rwxr-xr-xusr.sbin/xntpd/scripts/ntp-restart9
-rw-r--r--usr.sbin/xntpd/scripts/stats/README39
-rw-r--r--usr.sbin/xntpd/scripts/stats/README.stats246
-rw-r--r--usr.sbin/xntpd/scripts/stats/README.timecodes149
-rw-r--r--usr.sbin/xntpd/scripts/stats/clock.awk341
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/clock.sh17
-rw-r--r--usr.sbin/xntpd/scripts/stats/dupe.awk8
-rw-r--r--usr.sbin/xntpd/scripts/stats/ensemble.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/ensemble.awk17
-rw-r--r--usr.sbin/xntpd/scripts/stats/etf.S15
-rw-r--r--usr.sbin/xntpd/scripts/stats/etf.awk19
-rw-r--r--usr.sbin/xntpd/scripts/stats/itf.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/itf.awk19
-rw-r--r--usr.sbin/xntpd/scripts/stats/loop.S7
-rw-r--r--usr.sbin/xntpd/scripts/stats/loop.awk41
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/loop.sh13
-rw-r--r--usr.sbin/xntpd/scripts/stats/peer.awk57
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/peer.sh13
-rw-r--r--usr.sbin/xntpd/scripts/stats/psummary.awk43
-rw-r--r--usr.sbin/xntpd/scripts/stats/rms.awk41
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/summary.sh88
-rw-r--r--usr.sbin/xntpd/scripts/stats/tdata.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/tdata.awk45
-rw-r--r--usr.sbin/xntpd/scripts/support/README73
-rwxr-xr-xusr.sbin/xntpd/scripts/support/bin/monl213
-rwxr-xr-xusr.sbin/xntpd/scripts/support/bin/mvstats23
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp300.hp30070
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp700.hp70067
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui4771
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp800.hp80070
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.conf36
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.keys0
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb0
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun3.sun336
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui0183
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10176
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45228
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4c63
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer174
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m69
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42152
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m165
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/tickconf19
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/cron18
-rw-r--r--usr.sbin/xntpd/scripts/support/etc/crontab8
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/install67
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/rc198
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/setup72
-rw-r--r--usr.sbin/xntpd/util/Makefile19
-rw-r--r--usr.sbin/xntpd/util/README67
-rw-r--r--usr.sbin/xntpd/util/byteorder.c52
-rw-r--r--usr.sbin/xntpd/util/jitter.c73
-rw-r--r--usr.sbin/xntpd/util/kern.c210
-rw-r--r--usr.sbin/xntpd/util/longsize.c11
-rw-r--r--usr.sbin/xntpd/util/ntptime.c236
-rw-r--r--usr.sbin/xntpd/util/precision.c150
-rw-r--r--usr.sbin/xntpd/util/testrs6000.c44
-rw-r--r--usr.sbin/xntpd/util/tickadj.c559
-rw-r--r--usr.sbin/xntpd/util/timetrim.c85
-rw-r--r--usr.sbin/xntpd/xntpd/Makefile36
-rw-r--r--usr.sbin/xntpd/xntpd/README6
-rw-r--r--usr.sbin/xntpd/xntpd/minpoll0
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_config.c1717
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_control.c2690
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_filegen.c538
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_intres.c799
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_io.c1807
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_leap.c315
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_loopfilter.c721
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_monitor.c349
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_peer.c667
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_proto.c2268
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_refclock.c1286
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_request.c2453
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_restrict.c459
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_timer.c187
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_unixclock.c634
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_util.c441
-rw-r--r--usr.sbin/xntpd/xntpd/ntpd.c461
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_acts.c895
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_as2201.c453
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_atom.c499
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_chu.c800
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_conf.c185
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_datum.c871
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_goes.c533
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_gpstm.c999
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_heath.c393
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_irig.c259
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_leitch.c710
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_local.c170
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_moto.c2
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_msfees.c1557
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_mx4200.c1343
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_nmea.c391
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_omega.c999
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_parse.c3925
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_pst.c329
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_tpro.c227
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_trak.c339
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_wwvb.c420
-rw-r--r--usr.sbin/xntpd/xntpdc/Makefile19
-rw-r--r--usr.sbin/xntpd/xntpdc/README6
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc.c1533
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc.h59
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc_ops.c2441
-rw-r--r--usr.sbin/xten/Makefile7
-rw-r--r--usr.sbin/xten/README367
-rw-r--r--usr.sbin/xten/xten.1113
-rw-r--r--usr.sbin/xten/xten.c178
-rw-r--r--usr.sbin/yp_mkdb/Makefile13
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.8171
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.c338
-rw-r--r--usr.sbin/ypbind/Makefile9
-rw-r--r--usr.sbin/ypbind/yp_ping.c545
-rw-r--r--usr.sbin/ypbind/yp_ping.h1
-rw-r--r--usr.sbin/ypbind/ypbind.8181
-rw-r--r--usr.sbin/ypbind/ypbind.c1009
-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/Makefile29
-rw-r--r--usr.sbin/yppush/yppush.8169
-rw-r--r--usr.sbin/yppush/yppush_extern.h44
-rw-r--r--usr.sbin/yppush/yppush_main.c688
-rw-r--r--usr.sbin/ypserv/Makefile41
-rw-r--r--usr.sbin/ypserv/Makefile.yp583
-rw-r--r--usr.sbin/ypserv/yp_access.c325
-rw-r--r--usr.sbin/ypserv/yp_dblookup.c754
-rw-r--r--usr.sbin/ypserv/yp_dnslookup.c542
-rw-r--r--usr.sbin/ypserv/yp_error.c90
-rw-r--r--usr.sbin/ypserv/yp_extern.h113
-rw-r--r--usr.sbin/ypserv/yp_main.c337
-rw-r--r--usr.sbin/ypserv/yp_server.c961
-rw-r--r--usr.sbin/ypserv/yp_svc_udp.c89
-rw-r--r--usr.sbin/ypserv/ypinit.8177
-rw-r--r--usr.sbin/ypserv/ypinit.sh390
-rw-r--r--usr.sbin/ypserv/ypserv.8426
-rw-r--r--usr.sbin/ypset/Makefile7
-rw-r--r--usr.sbin/ypset/ypset.885
-rw-r--r--usr.sbin/ypset/ypset.c156
-rw-r--r--usr.sbin/zic/Arts.htm177
-rw-r--r--usr.sbin/zic/Makefile4
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/README66
-rw-r--r--usr.sbin/zic/Theory285
-rw-r--r--usr.sbin/zic/WWW.htm97
-rw-r--r--usr.sbin/zic/ialloc.c86
-rw-r--r--usr.sbin/zic/private.h181
-rw-r--r--usr.sbin/zic/scheck.c64
-rw-r--r--usr.sbin/zic/zdump.844
-rw-r--r--usr.sbin/zic/zdump.c375
-rw-r--r--usr.sbin/zic/zdump/Makefile13
-rw-r--r--usr.sbin/zic/zic.8387
-rw-r--r--usr.sbin/zic/zic.c2228
-rw-r--r--usr.sbin/zic/zic/Makefile14
1672 files changed, 422927 insertions, 41 deletions
diff --git a/usr.sbin/IPXrouted/IPXrouted.8 b/usr.sbin/IPXrouted/IPXrouted.8
new file mode 100644
index 0000000..79779d5
--- /dev/null
+++ b/usr.sbin/IPXrouted/IPXrouted.8
@@ -0,0 +1,189 @@
+.\" 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.
+.\"
+.Dd Oct 11, 1995
+.Dt IPXROUTED 8
+.Os FreeBSD
+.Sh NAME
+.Nm IPXrouted
+.Nd IPX Routing Information Protocol daemon
+.Sh SYNOPSIS
+.Nm IPXrouted
+.Op Fl q
+.Op Fl s
+.Op Fl S
+.Op Fl t
+.Op Ar logfile
+.Sh DESCRIPTION
+.Nm IPXrouted
+is invoked at boot time to manage the IPX routing tables.
+The IPX routing daemon uses the Novell IPX Routing
+Information Protocol in maintaining up to date kernel routing
+table entries.
+.Pp
+Available options:
+.Bl -tag -width logfile
+.It Fl q
+Do not supply routing information (opposite of
+.Fl s
+option below).
+.It Fl s
+Forces
+.Nm IPXrouted
+to supply routing information whether it is acting as an internetwork
+router or not.
+.It Fl S
+Do not supply Service Advertizing Protocol
+.Nm (SAP)
+information. The default is to supply
+.Nm SAP
+information.
+.It Fl t
+All packets sent or received are
+printed on the standard output. In addition,
+.Nm IPXrouted
+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 IPXrouted 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 IPXrouted
+listens
+for routing information packets. If the host is connected to
+multiple IPX networks, it periodically supplies copies
+of its routing tables to any directly connected hosts
+and networks.
+.Pp
+When
+.Nm IPXrouted
+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.
+.Nm IPXrouted
+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 IPXrouted
+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 IPXrouted
+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 ``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 IPXrouted
+records the change in its internal tables and generates a
+.Em response
+packet to all directly connected hosts and networks.
+.Xr Routed 8
+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 IPXrouted
+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 IPXrouted
+receives a SIGINFO signal the current contents of the RIP and SAP
+tables are appended to the file /tmp/ipxrouted.dmp.
+.Sh SEE ALSO
+.Xr ipx 3
+.Sh HISTORY
diff --git a/usr.sbin/IPXrouted/Makefile b/usr.sbin/IPXrouted/Makefile
new file mode 100644
index 0000000..5c8be5e
--- /dev/null
+++ b/usr.sbin/IPXrouted/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id$
+
+PROG= IPXrouted
+MAN8= 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
+DPADD= ${LIBCOMPAT} ${LIBIPX}
+LDADD= -lcompat -lipx
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/IPXrouted/af.c b/usr.sbin/IPXrouted/af.c
new file mode 100644
index 0000000..05034ab
--- /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.
+ *
+ * $Id: af.c,v 1.4 1997/02/22 16:00:54 peter Exp $
+ */
+
+#ifndef lint
+static 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..cad2861
--- /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
+ *
+ * $Id$
+ */
+
+/*
+ * 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..55e08b3
--- /dev/null
+++ b/usr.sbin/IPXrouted/defs.h
@@ -0,0 +1,106 @@
+/*
+ * 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
+ *
+ * $Id: defs.h,v 1.5 1997/02/22 16:00:55 peter Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.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 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..c2522a9
--- /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)
+ *
+ * $Id: if.c,v 1.3 1997/02/22 16:00:55 peter Exp $
+ */
+
+#ifndef lint
+static 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..a9e2f39
--- /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.
+ *
+ * $Id: input.c,v 1.5 1997/02/22 16:00:56 peter Exp $
+ */
+
+#ifndef lint
+static 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 %x\n",
+ (u_int)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..ed12087
--- /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
+ *
+ * $Id$
+ */
+
+/*
+ * 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..9af5b2e
--- /dev/null
+++ b/usr.sbin/IPXrouted/main.c
@@ -0,0 +1,395 @@
+/*
+ * 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.
+ *
+ * $Id: main.c,v 1.6 1997/02/22 16:00:57 peter Exp $
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static 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;
+ }
+ fprintf(stderr,
+ "usage: ipxrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ]\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("/tmp/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..f824ecd
--- /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.
+ *
+ * $Id: output.c,v 1.6 1997/02/22 16:00:58 peter Exp $
+ */
+
+#ifndef lint
+static 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..90a013a
--- /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
+ *
+ * $Id$
+ */
+
+/*
+ * 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..5473851
--- /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.
+ *
+ * $Id: sap.h,v 1.5 1997/02/22 16:00:59 peter Exp $
+ */
+#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..26130fe
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_input.c
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ *
+ * $Id: sap_input.c,v 1.4 1997/02/22 16:00:59 peter Exp $
+ */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+
+/*
+ * 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");
+ 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..7f7dbcd
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_output.c
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ *
+ * $Id: sap_output.c,v 1.7 1997/02/22 16:01:00 peter Exp $
+ */
+
+/*
+ * 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..b5b0f7a
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_tables.c
@@ -0,0 +1,322 @@
+/*
+ * 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.
+ *
+ * $Id: sap_tables.c,v 1.5 1997/07/06 07:38:33 jhay Exp $
+ */
+
+#include "defs.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;
+ register struct sap_entry *csap;
+ 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);
+ }
+next:;
+ }
+ return best;
+}
+
+/*
+ * Add a 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 %04.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..44f883b
--- /dev/null
+++ b/usr.sbin/IPXrouted/startup.c
@@ -0,0 +1,281 @@
+/*
+ * 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.
+ *
+ * $Id: startup.c,v 1.6 1997/02/22 16:01:01 peter Exp $
+ */
+
+#ifndef lint
+static 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 <nlist.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;
+{
+ extern int errno;
+ 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;
+/* 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]
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+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;
+ ADVANCE(cp, 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..dbfdfe5
--- /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
+ *
+ * $Id: table.h,v 1.4 1997/02/22 16:01:02 peter Exp $
+ */
+
+/*
+ * 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..9fe7f45
--- /dev/null
+++ b/usr.sbin/IPXrouted/tables.c
@@ -0,0 +1,434 @@
+/*
+ * 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.
+ *
+ * $Id: tables.c,v 1.5 1997/07/01 00:33:42 bde Exp $
+ */
+
+#ifndef lint
+static 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 <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..ea75a98
--- /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.
+ *
+ * $Id: timer.c,v 1.3 1997/02/22 16:01:03 peter Exp $
+ */
+
+#ifndef lint
+static 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..a4c5df9
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.c
@@ -0,0 +1,519 @@
+/*
+ * 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.
+ *
+ * $Id: trace.c,v 1.4 1997/02/22 16:01:04 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
+#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, 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=%x packet=%x\n", size,
+ (u_int)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=%x packet=%x\n", size,
+ (u_int)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, "%lx", 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..17fef0f
--- /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
+ *
+ * $Id: trace.h,v 1.5 1997/02/22 16:01:04 peter Exp $
+ */
+
+/*
+ * 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..d524839
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,139 @@
+# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
+# $Id: Makefile,v 1.149 1999/03/12 15:38:54 brian Exp $
+
+# XXX MISSING: mkproto
+SUBDIR= IPXrouted \
+ ac \
+ accton \
+ adduser \
+ amd \
+ arp \
+ atm \
+ bootparamd \
+ cdcontrol \
+ chkgrp \
+ chown \
+ chroot \
+ ckdist \
+ config \
+ cron \
+ crunch \
+ ctm \
+ dev_mkdb \
+ diskpart \
+ edquota \
+ inetd \
+ iostat \
+ ipfstat \
+ ipftest \
+ ipmon \
+ ipnat \
+ ipresend \
+ ipsend \
+ iptest \
+ kbdcontrol \
+ kbdmap \
+ kernbb \
+ keyadmin \
+ keyserv \
+ kvm_mkdb \
+ lpr \
+ manctl \
+ moused \
+ mrouted \
+ mtest \
+ mtree \
+ named \
+ named.reload \
+ named.restart \
+ ndc \
+ newsyslog \
+ nslookup \
+ nsupdate \
+ pccard \
+ pciconf \
+ periodic \
+ pkg_install \
+ portmap \
+ ppp \
+ pppctl \
+ pppd \
+ pppstats \
+ procctl \
+ pstat \
+ pw \
+ pwd_mkdb \
+ quot \
+ quotaon \
+ rarpd \
+ repquota \
+ rmt \
+ rpc.lockd \
+ rpc.statd \
+ rpc.yppasswdd \
+ rpc.ypupdated \
+ rpc.ypxfrd \
+ rtprio \
+ rwhod \
+ sa \
+ sliplogin \
+ slstat \
+ spray \
+ sysctl \
+ syslogd \
+ tcpdchk \
+ tcpdump \
+ tcpdmatch \
+ timed \
+ traceroute \
+ trpt \
+ tzsetup \
+ usbd \
+ usbdevs \
+ vidcontrol \
+ vipw \
+ vnconfig \
+ watch \
+ wormcontrol \
+ xntpd \
+ xten \
+ yp_mkdb \
+ ypbind \
+ yppoll \
+ yppush \
+ ypserv \
+ ypset \
+ zic
+
+.if !defined(NO_SENDMAIL)
+SUBDIR+=mailstats \
+ makemap \
+ praliases \
+ sendmail
+.endif
+
+.if ${MACHINE_ARCH} == "i386"
+SUBDIR+=apm \
+ apmconf \
+ bad144 \
+ boot0cfg \
+ btxld \
+ fdcontrol \
+ fdformat \
+ fdwrite \
+ i4b \
+ kgmon \
+ lptcontrol \
+ mixer \
+ mptable \
+ pcvt \
+ pnpinfo \
+ rndcontrol \
+ sgsc \
+ sicontrol \
+ spkrtest \
+ stallion \
+ wlconfig
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc
new file mode 100644
index 0000000..fd92864
--- /dev/null
+++ b/usr.sbin/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/ac/Makefile b/usr.sbin/ac/Makefile
new file mode 100644
index 0000000..e3b47f5
--- /dev/null
+++ b/usr.sbin/ac/Makefile
@@ -0,0 +1,18 @@
+# $Id$
+
+PROG= ac
+MAN8= ac.8
+
+# 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..341226a
--- /dev/null
+++ b/usr.sbin/ac/ac.8
@@ -0,0 +1,155 @@
+.\"
+.\" Copyright (c) 1994 Simon J. Gerraty
+.\" Copyright (c) 1994 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ac.8,v 1.9 1997/02/22 16:01:10 peter Exp $
+.\"
+.Dd March 15, 1994
+.Dt AC 8
+.Os
+.Sh NAME
+.Nm ac
+.Nd connect time accounting
+.Sh SYNOPSIS
+.Nm ac
+.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.
+.Nm \&Ac
+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 .
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if a fatal error occurs.
+.Sh FILES
+.Bl -tag -width /var/log/wtmp.[0-7] -compact
+.It Pa /var/log/wtmp
+connect time accounting file
+.It Pa /var/log/wtmp.[0-7]
+rotated files
+.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..0916d3c
--- /dev/null
+++ b/usr.sbin/ac/ac.c
@@ -0,0 +1,562 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: ac.c,v 1.9 1998/05/25 05:21:29 steve Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.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];
+ int 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 __P((int, char **));
+int ac __P((FILE *));
+struct tty_list *add_tty __P((char *));
+int do_tty __P((char *));
+FILE *file __P((char *));
+struct utmp_list *log_in __P((struct utmp_list *, struct utmp *));
+struct utmp_list *log_out __P((struct utmp_list *, struct utmp *));
+int on_console __P((struct utmp_list *));
+void show __P((char *, time_t));
+void show_today __P((struct user_list *, struct utmp_list *,
+ time_t));
+void show_users __P((struct user_list *));
+struct user_list *update_user __P((struct user_list *, char *, time_t));
+void usage __P((void));
+
+/*
+ * open wtmp or die
+ */
+FILE *
+file(name)
+ 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(name)
+ char *name;
+{
+ struct tty_list *tp;
+ register char *rcp;
+
+ Flags |= AC_T;
+
+ if ((tp = NEW(struct tty_list)) == NULL)
+ err(1, "malloc");
+ tp->len = 0; /* full match */
+ tp->ret = 1; /* do if match */
+ if (*name == '!') { /* don't do if match */
+ tp->ret = 0;
+ name++;
+ }
+ (void)strncpy(tp->name, name, sizeof (tp->name) - 1);
+ tp->name[sizeof (tp->name) - 1] = '\0';
+ 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(name)
+ 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(head)
+ 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(head, name, secs)
+ 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)
+ err(1, "malloc");
+ up->next = head;
+ (void)strncpy(up->name, name, sizeof (up->name) - 1);
+ up->name[sizeof (up->name) - 1] = '\0'; /* paranoid! */
+ up->secs = secs;
+ Total += secs;
+ return up;
+}
+
+int
+main(argc, argv)
+ 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], 0L);
+ }
+ 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(name, secs)
+ char *name;
+ time_t secs;
+{
+ (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
+ ((double)secs / 3600));
+}
+
+void
+show_users(list)
+ 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(users, logins, secs)
+ 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;
+
+ (void)strftime(date, sizeof (date), "%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(head, up)
+ 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 %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
+ 19, ctime(&up->ut_time),
+ sizeof (lp->usr.ut_line), lp->usr.ut_line,
+ sizeof (lp->usr.ut_name), lp->usr.ut_name,
+ secs / 3600, (secs % 3600) / 60, 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(head, up)
+ 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
+ */
+ (void)strncpy(up->ut_line, Console, sizeof (up->ut_line) - 1);
+ up->ut_line[sizeof (up->ut_line) - 1] = '\0'; /* paranoid! */
+ }
+#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)
+ err(1, "malloc");
+ lp->next = head;
+ head = lp;
+ memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
+#ifdef DEBUG
+ if (Debug) {
+ printf("%-.*s %-.*s: %-.*s logged in", 19,
+ ctime(&lp->usr.ut_time), sizeof (up->ut_line),
+ up->ut_line, sizeof (up->ut_name), up->ut_name);
+ if (*up->ut_host)
+ printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host);
+ putchar('\n');
+ }
+#endif
+ return head;
+}
+
+int
+ac(fp)
+ FILE *fp;
+{
+ struct utmp_list *lp, *head = NULL;
+ struct utmp usr;
+ struct tm *ltm;
+ time_t secs;
+ int day = -1;
+
+ while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
+ if (!FirstTime)
+ FirstTime = usr.ut_time;
+ if (Flags & AC_D) {
+ ltm = localtime(&usr.ut_time);
+ if (day >= 0 && day != ltm->tm_yday) {
+ day = ltm->tm_yday;
+ /*
+ * print yesterday's total
+ */
+ secs = usr.ut_time;
+ 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 = usr.ut_time;
+ break;
+ case '{':
+ secs -= usr.ut_time;
+ /*
+ * 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 = usr.ut_time; /* 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);
+ } 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) {
+ ltm = localtime(&usr.ut_time);
+ if (day >= 0 && day != ltm->tm_yday) {
+ /*
+ * print yesterday's total
+ */
+ secs = usr.ut_time;
+ 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);
+ }
+ return 0;
+}
+
+void
+usage()
+{
+ (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..8e3f336
--- /dev/null
+++ b/usr.sbin/accton/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= accton
+MAN8= accton.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/accton/accton.8 b/usr.sbin/accton/accton.8
new file mode 100644
index 0000000..1b5ad28
--- /dev/null
+++ b/usr.sbin/accton/accton.8
@@ -0,0 +1,34 @@
+.Dd May 21, 1993
+.Dt ACCTON 8
+.Os
+.Sh NAME
+.Nm accton
+.Nd enable/disable system accounting
+.Sh SYNOPSIS
+.Nm accton
+.Op Ar acctfile
+.Sh DESCRIPTION
+The
+.Nm
+command is used
+for switching system accounting on or off.
+If called with the argument
+.Ar acctfile ,
+system accounting is enabled and 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 examining the accounting records.
+If no arguments are given, system account 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 sa 8
diff --git a/usr.sbin/accton/accton.c b/usr.sbin/accton/accton.c
new file mode 100644
index 0000000..af2fa89
--- /dev/null
+++ b/usr.sbin/accton/accton.c
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)accton.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ 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/adduser/Makefile b/usr.sbin/adduser/Makefile
new file mode 100644
index 0000000..f60716f
--- /dev/null
+++ b/usr.sbin/adduser/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.12 1997/07/05 19:12:43 pst Exp $
+
+MAINTAINER= wosch
+
+SCRIPTS= adduser.perl rmuser.perl
+MAN8= adduser.8 rmuser.8
+
+beforeinstall:
+.for script in ${SCRIPTS}
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/${script} ${DESTDIR}${BINDIR}/${script:R}
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8
new file mode 100644
index 0000000..cbd8c05
--- /dev/null
+++ b/usr.sbin/adduser/adduser.8
@@ -0,0 +1,247 @@
+.\" Copyright (c) 1995-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.
+.\"
+.\" $Id: adduser.8,v 1.25 1998/03/18 16:21:55 hoek Exp $
+.\"
+.Dd January 9, 1995
+.Dt ADDUSER 8
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm adduser
+.Nd command for adding new users
+.Sh SYNOPSIS
+.Nm adduser
+.Op Fl check_only
+.br
+.Op Fl class Ar login_class
+.br
+.Op Fl config_create
+.br
+.Op Fl dotdir Ar dotdir
+.br
+.Op Fl group Ar login_group
+.br
+.Op Fl h | help
+.br
+.Op Fl home Ar home
+.br
+.Op Fl message Ar message_file
+.br
+.Op Fl noconfig
+.br
+.Op Fl shell Ar shell
+.br
+.Op Fl s | silent | q | quiet
+.br
+.Op Fl uid Ar uid_start
+.br
+.Op Fl v | verbose
+.Sh DESCRIPTION
+.Nm Adduser
+is a simple program for adding new users. Adduser checks
+the passwd, group and shell databases. It creates passwd/group entries,
+.Ev HOME
+directory, dotfiles and sends the new user a welcome message.
+.Sh RESTRICTIONS
+.Bl -tag -width Ds -compact
+.It Sy username
+Login name. May contain only lowercase characters or digits. Maximum length
+is 16 characters (see
+.Xr setlogin 2
+BUGS section).
+The reasons for this limit are "Historical".
+Given that people have traditionally wanted to break this
+limit for aesthetic reasons, it's never been of great importance to break
+such a basic fundamental parameter in UNIX.
+You can change
+.Dv UT_NAMESIZE
+in
+.Pa /usr/include/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/aliases .
+.It Sy fullname
+Firstname and surname.
+The
+.Ql Pa \:
+character is not allowed.
+.It Sy shell
+Only valid shells from the shell database or sliplogin and pppd
+.It Sy uid
+Automatically generated or your choice, must be less than 32000.
+.It Sy gid/login group
+Your choice or automatically generated.
+.It Sy password
+If not empty, password is encoded with
+.Xr crypt 3 .
+.El
+.Sh UNIQUE GROUPS
+Perhaps you're missing what
+.Em can
+be done with this scheme that falls apart
+with most other schemes. With each user in his/her own group the user can
+safely run with a umask of 002 and have files created in their home directory
+and not worry about others being able to read 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
+.Bl -enum
+.It
+Read internal variables.
+.It
+Read configuration file (/etc/adduser.conf).
+.It
+Parse command line options.
+.El
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Sy -check_only
+Check /etc/passwd, /etc/group, /etc/shells and exit.
+.It Sy -class Ar login_class
+Set default login class.
+.It Sy -config_create
+Create new configuration and message file and exit.
+.It Sy -dotdir Ar directory
+Copy files from
+.Ar directory
+into the
+.Ev HOME
+directory of new users,
+.Ql Pa dot.foo
+will be renamed to
+.Ql Pa .foo .
+Don't copy files if
+.Ar directory
+specified is equal to
+.Ar no .
+For security make all files writable and readable for owner,
+don't allow group or world to write files and allow only owner
+to read/execute/write
+.Pa .rhost ,
+.Pa .Xauthority ,
+.Pa .kermrc ,
+.Pa .netrc ,
+.Pa Mail ,
+.Pa prv ,
+.Pa iscreen ,
+.Pa term .
+.It Sy -group Ar login_group
+Login group.
+.Ar USER
+means that the username is to be used as login group.
+.It Sy -help,-h,-?
+Print a summary of options and exit.
+.It Sy -home Ar partition
+Default home partition where all users located.
+.It Sy -message Ar file
+Send new users a welcome message from
+.Ar file .
+Specifying a value of
+.Ar no
+for
+.Ar file
+causes no message to be sent to new users.
+.It Sy -noconfig
+Do not read the default configuration file.
+.It Sy -shell Ar shell
+Default shell for new users.
+.It Sy -silent,-s,-quiet,-q
+Few warnings, questions, bug reports.
+.It Sy -uid Ar uid
+Use uid's from
+.Ar uid
+on up.
+.It Sy -verbose,-v
+Many warnings, questions. Recommended for novice users.
+.Sh FORMATS
+.Bl -tag -width Ds -compact
+.Ql Pa #
+is a comment.
+.It Sy configuration file
+.Nm Adduser
+reads and writes this file.
+See
+.Pa /etc/adduser.conf
+for more details.
+.It Sy message file
+Eval variables in this file. See
+.Pa /etc/adduser.message
+for more
+details.
+.El
+.Sh FILES
+.Bl -tag -width /etc/master.passwdxx -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 adduser
+.It Pa /etc/adduser.message
+message file for adduser
+.It Pa /usr/share/skel
+skeletal login directory
+.It Pa /var/log/adduser
+logfile for adduser
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr setlogin 2 ,
+.Xr yp 4 ,
+.Xr aliases 5 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr shells 5 ,
+.Xr addgroup 8 ,
+.Xr pw 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmgroup 8 ,
+.Xr rmuser 8 ,
+.Xr vipw 8
+.\" .Sh BUGS
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
diff --git a/usr.sbin/adduser/adduser.perl b/usr.sbin/adduser/adduser.perl
new file mode 100644
index 0000000..256e27c
--- /dev/null
+++ b/usr.sbin/adduser/adduser.perl
@@ -0,0 +1,1394 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1995-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.
+#
+# $Id: adduser.perl,v 1.41 1998/08/17 18:50:29 wosch Exp $
+
+
+# read variables
+sub variables {
+ $verbose = 1; # verbose = [0-2]
+ $defaultpasswd = "yes"; # use password for new users
+ $dotdir = "/usr/share/skel"; # copy dotfiles from this dir
+ $dotdir_bak = $dotdir;
+ $send_message = "/etc/adduser.message"; # send message to new user
+ $send_message_bak = '/etc/adduser.message';
+ $config = "/etc/adduser.conf"; # config file for adduser
+ $config_read = 1; # read config file
+ $logfile = "/var/log/adduser"; # logfile
+ $home = "/home"; # default HOME
+ $etc_shells = "/etc/shells";
+ $etc_passwd = "/etc/master.passwd";
+ $group = "/etc/group";
+ $pwd_mkdb = "pwd_mkdb -p"; # program for building passwd database
+
+
+ # List of directories where shells located
+ @path = ('/bin', '/usr/bin', '/usr/local/bin');
+ # common shells, first element has higher priority
+ @shellpref = ('csh', 'sh', 'bash', 'tcsh', 'ksh');
+
+ $defaultshell = 'sh'; # defaultshell if not empty
+ $group_uniq = 'USER';
+ $defaultgroup = $group_uniq;# login groupname, $group_uniq means username
+ $defaultclass = '';
+
+ $uid_start = 1000; # new users get this uid
+ $uid_end = 32000; # max. uid
+
+ # global variables
+ # passwd
+ $username = ''; # $username{username} = uid
+ $uid = ''; # $uid{uid} = username
+ $pwgid = ''; # $pwgid{pwgid} = username; gid from passwd db
+
+ $password = ''; # password for new users
+
+ # group
+ $groupname =''; # $groupname{groupname} = gid
+ $groupmembers = ''; # $groupmembers{gid} = members of group/kommalist
+ $gid = ''; # $gid{gid} = groupname; gid form group db
+
+ # shell
+ $shell = ''; # $shell{`basename sh`} = sh
+
+ umask 022; # don't give login group write access
+
+ $ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin";
+ @passwd_backup = '';
+ @group_backup = '';
+ @message_buffer = '';
+ @user_variable_list = ''; # user variables in /etc/adduser.conf
+ $do_not_delete = '## DO NOT DELETE THIS LINE!';
+}
+
+# read shell database, see also: shells(5)
+sub shells_read {
+ local($sh);
+ local($err) = 0;
+
+ print "Check $etc_shells\n" if $verbose;
+ open(S, $etc_shells) || die "$etc_shells:$!\n";
+
+ while(<S>) {
+ if (/^\s*\//) {
+ s/^\s*//; s/\s+.*//; # chop
+ $sh = $_;
+ if (-x $sh) {
+ $shell{&basename($sh)} = $sh;
+ } else {
+ warn "Shell: $sh not executable!\n";
+ $err++;
+ }
+ }
+ }
+
+ # Allow /nonexistent and /bin/date as a valid shell for system utils
+ push(@list, "/nonexistent");
+ push(@shellpref, "no") if !grep(/^no$/, @shellpref);
+ $shell{"no"} = "/nonexistent";
+
+ push(@list, "/bin/date");
+ push(@shellpref, "date") if !grep(/^date$/, @shellpref);
+ $shell{"date"} = "/bin/date";
+
+ return $err;
+}
+
+# add new shells if possible
+sub shells_add {
+ local($sh,$dir,@list);
+
+ return 1 unless $verbose;
+
+ foreach $sh (@shellpref) {
+ # all known shells
+ if (!$shell{$sh}) {
+ # shell $sh is not defined as login shell
+ foreach $dir (@path) {
+ if (-x "$dir/$sh") {
+ # found shell
+ if (&confirm_yn("Found shell: $dir/$sh. Add to $etc_shells?", "yes")) {
+ push(@list, "$dir/$sh");
+ push(@shellpref, "$sh");
+ $shell{&basename("$dir/$sh")} = "$dir/$sh";
+ $changes++;
+ }
+ }
+ }
+ }
+ }
+ &append_file($etc_shells, @list) if $#list >= 0;
+}
+
+# choose your favourite shell and return the shell
+sub shell_default {
+ local($e,$i,$new_shell);
+ local($sh);
+
+ $sh = &shell_default_valid($defaultshell);
+ return $sh unless $verbose;
+
+ $new_shell = &confirm_list("Enter your default shell:", 0,
+ $sh, sort(keys %shell));
+ print "Your default shell is: $new_shell -> $shell{$new_shell}\n";
+ $changes++ if $new_shell ne $sh;
+ return $new_shell;
+}
+
+sub shell_default_valid {
+ local($sh) = @_;
+ local($s,$e);
+
+ return $sh if $shell{$sh};
+
+ foreach $e (@shellpref) {
+ $s = $e;
+ last if defined($shell{$s});
+ }
+ $s = "sh" unless $s;
+ warn "Shell ``$sh'' is undefined, use ``$s''\n";
+ return $s;
+}
+
+# return default home partition (e.g. "/home")
+# create base directory if nesseccary
+sub home_partition {
+ local($home) = @_;
+ $home = &stripdir($home);
+ local($h) = $home;
+
+ return $h if !$verbose && $h eq &home_partition_valid($h);
+
+ while(1) {
+ $h = &confirm_list("Enter your default HOME partition:", 1, $home, "");
+ $h = &stripdir($h);
+ last if $h eq &home_partition_valid($h);
+ }
+
+ $changes++ if $h ne $home;
+ return $h;
+}
+
+sub home_partition_valid {
+ local($h) = @_;
+
+ $h = &stripdir($h);
+ # all right (I hope)
+ return $h if $h =~ "^/" && -e $h && -w $h && (-d $h || -l $h);
+
+ # Errors or todo
+ if ($h !~ "^/") {
+ warn "Please use absolute path for home: ``$h''.\a\n";
+ return 0;
+ }
+
+ if (-e $h) {
+ warn "$h exists, but is not a directory or symlink!\n"
+ unless -d $h || -l $h;
+ warn "$h is not writable!\n"
+ unless -w $h;
+ return 0;
+ } else {
+ # create home partition
+ return $h if &mkdir_home($h);
+ }
+ return 0;
+}
+
+# check for valid passwddb
+sub passwd_check {
+ system("$pwd_mkdb -C $etc_passwd");
+ die "\nInvalid $etc_passwd - cannot add any users!\n" if $?;
+}
+
+# read /etc/passwd
+sub passwd_read {
+ local($p_username, $pw, $p_uid, $p_gid, $sh, %shlist);
+
+ print "Check $etc_passwd\n" if $verbose;
+ open(P, "$etc_passwd") || die "$passwd: $!\n";
+
+ while(<P>) {
+ chop;
+ push(@passwd_backup, $_);
+ # ignore comments
+ next if /^\s*$/;
+ next if /^\s*#/;
+
+ ($p_username, $pw, $p_uid, $p_gid, $sh) = (split(/:/, $_))[0..3,9];
+
+ print "$p_username already exists with uid: $username{$p_username}!\n"
+ if $username{$p_username} && $verbose;
+ $username{$p_username} = $p_uid;
+ print "User $p_username: uid $p_uid exists twice: $uid{$p_uid}\n"
+ if $uid{$p_uid} && $verbose && $p_uid; # don't warn for uid 0
+ print "User $p_username: illegal shell: ``$sh''\n"
+ if ($verbose && $sh &&
+ !$shell{&basename($sh)} &&
+ $p_username !~ /^(news|xten|bin|nobody|uucp)$/ &&
+ $sh !~ /\/(pppd|sliplogin|nologin|nonexistent)$/);
+ $uid{$p_uid} = $p_username;
+ $pwgid{$p_gid} = $p_username;
+ }
+ close P;
+}
+
+# read /etc/group
+sub group_read {
+ local($g_groupname,$pw,$g_gid, $memb);
+
+ print "Check $group\n" if $verbose;
+ open(G, "$group") || die "$group: $!\n";
+ while(<G>) {
+ chop;
+ push(@group_backup, $_);
+ # ignore comments
+ next if /^\s*$/;
+ next if /^\s*#/;
+
+ ($g_groupname, $pw, $g_gid, $memb) = (split(/:/, $_))[0..3];
+
+ $groupmembers{$g_gid} = $memb;
+ warn "Groupname exists twice: $g_groupname:$g_gid -> $g_groupname:$groupname{$g_groupname}\n"
+ if $groupname{$g_groupname} && $verbose;
+ $groupname{$g_groupname} = $g_gid;
+ warn "Groupid exists twice: $g_groupname:$g_gid -> $gid{$g_gid}:$g_gid\n"
+ if $gid{$g_gid} && $verbose;
+ $gid{$g_gid} = $g_groupname;
+ }
+ close G;
+}
+
+# check gids /etc/passwd <-> /etc/group
+sub group_check {
+ local($c_gid, $c_username, @list);
+
+ foreach $c_gid (keys %pwgid) {
+ if (!$gid{$c_gid}) {
+ $c_username = $pwgid{$c_gid};
+ warn "User ``$c_username'' has gid $c_gid but a group with this " .
+ "gid does not exist.\n" if $verbose;
+ }
+ }
+}
+
+#
+# main loop for creating new users
+#
+
+# return username
+sub new_users_name {
+ local($name);
+
+ while(1) {
+ $name = &confirm_list("Enter username", 1, "a-z0-9_-", "");
+ if (length($name) > 16) {
+ warn "Username is longer than 16 chars\a\n";
+ next;
+ }
+ last if (&new_users_name_valid($name) eq $name);
+ }
+ return $name;
+}
+
+sub new_users_name_valid {
+ local($name) = @_;
+
+ if ($name !~ /^[a-z0-9_][a-z0-9_\-]*$/ || $name eq "a-z0-9_-") {
+ warn "Wrong username. " .
+ "Please use only lowercase characters or digits\a\n";
+ return 0;
+ } elsif ($username{$name}) {
+ warn "Username ``$name'' already exists!\a\n"; return 0;
+ }
+ return $name;
+}
+
+# return full name
+sub new_users_fullname {
+ local($name) = @_;
+ local($fullname);
+
+ while(1) {
+ $fullname = &confirm_list("Enter full name", 1, "", "");
+ last if $fullname eq &new_users_fullname_valid($fullname);
+ }
+ $fullname = $name unless $fullname;
+ return $fullname;
+}
+
+sub new_users_fullname_valid {
+ local($fullname) = @_;
+
+ return $fullname if $fullname !~ /:/;
+
+ warn "``:'' is not allowed!\a\n";
+ return 0;
+}
+
+# return shell (full path) for user
+sub new_users_shell {
+ local($sh);
+
+ $sh = &confirm_list("Enter shell", 0, $defaultshell, keys %shell);
+ return $shell{$sh};
+}
+
+# return home (full path) for user
+# Note that the home path defaults to $home/$name for batch
+sub new_users_home {
+ local($name) = @_;
+ local($userhome);
+
+ while(1) {
+ $userhome = &confirm_list("Enter home directory (full path)", 1, "$home/$name", "");
+ last if $userhome =~ /^\//;
+ warn qq{Home directory "$userhome" is not a full path\a\n};
+ }
+ return $userhome;
+}
+
+# return free uid and gid
+sub new_users_id {
+ local($name) = @_;
+ local($u_id, $g_id) = &next_id($name);
+ local($u_id_tmp, $e);
+
+ while(1) {
+ $u_id_tmp = &confirm_list("Uid", 1, $u_id, "");
+ last if $u_id_tmp =~ /^[0-9]+$/ && $u_id_tmp <= $uid_end &&
+ ! $uid{$u_id_tmp};
+ if ($uid{$u_id_tmp}) {
+ warn "Uid ``$u_id_tmp'' in use!\a\n";
+ $uid_start = $u_id_tmp;
+ ($u_id, $g_id) = &next_id($name);
+ next;
+ } else {
+ warn "Wrong uid.\a\n";
+ }
+ }
+ # use calculated uid
+ # return ($u_id_tmp, $g_id) if $u_id_tmp eq $u_id;
+ # recalculate gid
+ $uid_start = $u_id_tmp;
+ return &next_id($name);
+}
+
+# return login class for user
+sub new_users_class {
+ local($def) = @_;
+ local($class);
+
+ $class = &confirm_list("Enter login class:", 1, $def, ($def, "default"));
+ $class = "" if $class eq "default";
+ return $class;
+}
+
+# add user to group
+sub add_group {
+ local($gid, $name) = @_;
+
+ return 0 if
+ $groupmembers{$gid} =~ /^(.+,)?$name(,.+)?$/;
+
+ $groupmembers_bak{$gid} = $groupmembers{$gid};
+ $groupmembers{$gid} .= "," if $groupmembers{$gid};
+ $groupmembers{$gid} .= "$name";
+
+ return $name;
+}
+
+
+# return login group
+sub new_users_grplogin {
+ local($name, $defaultgroup, $new_users_ok) = @_;
+ local($group_login, $group);
+
+ $group = $name;
+ $group = $defaultgroup if $defaultgroup ne $group_uniq;
+
+ if ($new_users_ok) {
+ # clean up backup
+ foreach $e (keys %groupmembers_bak) { delete $groupmembers_bak{$e}; }
+ } else {
+ # restore old groupmembers, user was not accept
+ foreach $e (keys %groupmembers_bak) {
+ $groupmembers{$e} = $groupmembers_bak{$e};
+ }
+ }
+
+ while(1) {
+ $group_login = &confirm_list("Login group", 1, $group,
+ ($name, $group));
+ last if $group_login eq $group;
+ last if $group_login eq $name;
+ last if defined $groupname{$group_login};
+ if ($group_login eq $group_uniq) {
+ $group_login = $name; last;
+ }
+
+ if (defined $gid{$group_login}) {
+ # convert numeric groupname (gid) to groupname
+ $group_login = $gid{$group_login};
+ last;
+ }
+ warn "Group does not exist!\a\n";
+ }
+
+ #if (defined($groupname{$group_login})) {
+ # &add_group($groupname{$group_login}, $name);
+ #}
+
+ return ($group_login, $group_uniq) if $group_login eq $name;
+ return ($group_login, $group_login);
+}
+
+# return other groups (string)
+sub new_users_groups {
+ local($name, $other_groups) = @_;
+ local($string) =
+ "Login group is ``$group_login''. Invite $name into other groups:";
+ local($e, $flag);
+ local($new_groups,$groups);
+
+ $other_groups = "no" unless $other_groups;
+
+ while(1) {
+ $groups = &confirm_list($string, 1, $other_groups,
+ ("no", $other_groups, "guest"));
+ # no other groups
+ return "" if $groups eq "no";
+
+ ($flag, $new_groups) = &new_users_groups_valid($groups);
+ last unless $flag;
+ }
+ $new_groups =~ s/\s*$//;
+ return $new_groups;
+}
+
+sub new_users_groups_valid {
+ local($groups) = @_;
+ local($e, $new_groups);
+ local($flag) = 0;
+
+ foreach $e (split(/[,\s]+/, $groups)) {
+ # convert numbers to groupname
+ if ($e =~ /^[0-9]+$/ && $gid{$e}) {
+ $e = $gid{$e};
+ }
+ if (defined($groupname{$e})) {
+ if ($e eq $group_login) {
+ # do not add user to a group if this group
+ # is also the login group.
+ } elsif (&add_group($groupname{$e}, $name)) {
+ $new_groups .= "$e ";
+ } else {
+ warn "$name is already member of group ``$e''\n";
+ }
+ } else {
+ warn "Group ``$e'' does not exist\a\n"; $flag++;
+ }
+ }
+ return ($flag, $new_groups);
+}
+
+# your last change
+sub new_users_ok {
+
+ print <<EOF;
+
+Name: $name
+Password: ****
+Fullname: $fullname
+Uid: $u_id
+Gid: $g_id ($group_login)
+Class: $class
+Groups: $group_login $new_groups
+HOME: $userhome
+Shell: $sh
+EOF
+
+ return &confirm_yn("OK?", "yes");
+}
+
+# make password database
+sub new_users_pwdmkdb {
+ local($last) = @_;
+
+ system("$pwd_mkdb $etc_passwd");
+ if ($?) {
+ warn "$last\n";
+ warn "``$pwd_mkdb'' failed\n";
+ exit($? >> 8);
+ }
+}
+
+# update group database
+sub new_users_group_update {
+ local($e, @a);
+
+ # Add *new* group
+ if (!defined($groupname{$group_login}) &&
+ !defined($gid{$groupname{$group_login}})) {
+ push(@group_backup, "$group_login:*:$g_id:");
+ $groupname{$group_login} = $g_id;
+ $gid{$g_id} = $group_login;
+ # $groupmembers{$g_id} = $group_login;
+ }
+
+ if ($new_groups || defined($groupname{$group_login}) ||
+ defined($gid{$groupname{$group_login}}) &&
+ $gid{$groupname{$group_login}} ne "+") {
+ # new user is member of some groups
+ # new login group is already in name space
+ rename($group, "$group.bak");
+ #warn "$group_login $groupname{$group_login} $groupmembers{$groupname{$group_login}}\n";
+ foreach $e (sort {$a <=> $b} (keys %gid)) {
+ push(@a, "$gid{$e}:*:$e:$groupmembers{$e}");
+ }
+ &append_file($group, @a);
+ } else {
+ &append_file($group, "$group_login:*:$g_id:");
+ }
+
+}
+
+sub new_users_passwd_update {
+ # update passwd/group variables
+ push(@passwd_backup, $new_entry);
+ $username{$name} = $u_id;
+ $uid{$u_id} = $name;
+ $pwgid{$g_id} = $name;
+}
+
+# send message to new user
+sub new_users_sendmessage {
+ return 1 if $send_message eq "no";
+
+ local($cc) =
+ &confirm_list("Send message to ``$name'' and:",
+ 1, "no", ("root", "second_mail_address", "no"));
+ local($e);
+ $cc = "" if $cc eq "no";
+
+ foreach $e (@message_buffer) {
+ print eval "\"$e\"";
+ }
+ print "\n";
+
+ local(@message_buffer_append) = ();
+ if (!&confirm_yn("Add anything to default message", "no")) {
+ print "Use ``.'' or ^D alone on a line to finish your message.\n";
+ push(@message_buffer_append, "\n");
+ while($read = <STDIN>) {
+ last if $read eq "\.\n";
+ push(@message_buffer_append, $read);
+ }
+ }
+
+ &sendmessage("$name $cc", (@message_buffer, @message_buffer_append))
+ if (&confirm_yn("Send message", "yes"));
+}
+
+sub sendmessage {
+ local($to, @message) = @_;
+ local($e);
+
+ if (!open(M, "| mail -s Welcome $to")) {
+ warn "Cannot send mail to: $to!\n";
+ return 0;
+ } else {
+ foreach $e (@message) {
+ print M eval "\"$e\"";
+ }
+ close M;
+ }
+}
+
+
+sub new_users_password {
+
+ # empty password
+ return "" if $defaultpasswd ne "yes";
+
+ local($password);
+
+ while(1) {
+ system("stty -echo");
+ $password = &confirm_list("Enter password", 1, "", "");
+ system("stty echo");
+ print "\n";
+ if ($password ne "") {
+ system("stty -echo");
+ $newpass = &confirm_list("Enter password again", 1, "", "");
+ system("stty echo");
+ print "\n";
+ last if $password eq $newpass;
+ print "They didn't match, please try again\n";
+ }
+ elsif (&confirm_yn("Use an empty password?", "yes")) {
+ last;
+ }
+ }
+
+ return $password;
+}
+
+
+sub new_users {
+
+ print "\n" if $verbose;
+ print "Ok, let's go.\n" .
+ "Don't worry about mistakes. I will give you the chance later to " .
+ "correct any input.\n" if $verbose;
+
+ # name: Username
+ # fullname: Full name
+ # sh: shell
+ # userhome: home path for user
+ # u_id: user id
+ # g_id: group id
+ # class: login class
+ # group_login: groupname of g_id
+ # new_groups: some other groups
+ local($name, $group_login, $fullname, $sh, $u_id, $g_id, $class, $new_groups);
+ local($userhome);
+ local($groupmembers_bak, $cryptpwd);
+ local($new_users_ok) = 1;
+
+
+ $new_groups = "no";
+ $new_groups = "no" unless $groupname{$new_groups};
+
+ while(1) {
+ $name = &new_users_name;
+ $fullname = &new_users_fullname($name);
+ $sh = &new_users_shell;
+ $userhome = &new_users_home($name);
+ ($u_id, $g_id) = &new_users_id($name);
+ $class = &new_users_class($defaultclass);
+ ($group_login, $defaultgroup) =
+ &new_users_grplogin($name, $defaultgroup, $new_users_ok);
+ # do not use uniq username and login group
+ $g_id = $groupname{$group_login} if (defined($groupname{$group_login}));
+
+ $new_groups = &new_users_groups($name, $new_groups);
+ $password = &new_users_password;
+
+
+ if (&new_users_ok) {
+ $new_users_ok = 1;
+
+ $cryptpwd = "";
+ $cryptpwd = crypt($password, &salt) if $password ne "";
+ # obscure perl bug
+ $new_entry = "$name\:" . "$cryptpwd" .
+ "\:$u_id\:$g_id\:$class\:0:0:$fullname:$userhome:$sh";
+ &append_file($etc_passwd, "$new_entry");
+ &new_users_pwdmkdb("$new_entry");
+ &new_users_group_update;
+ &new_users_passwd_update; print "Added user ``$name''\n";
+ &new_users_sendmessage;
+ &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname");
+ &home_create($userhome, $name, $group_login);
+ } else {
+ $new_users_ok = 0;
+ }
+ if (!&confirm_yn("Add another user?", "yes")) {
+ print "Goodbye!\n" if $verbose;
+ last;
+ }
+ print "\n" if !$verbose;
+ }
+}
+
+# ask for password usage
+sub password_default {
+ local($p) = $defaultpasswd;
+ if ($verbose) {
+ $p = &confirm_yn("Use passwords", $defaultpasswd);
+ $changes++ unless $p;
+ }
+ return "yes" if (($defaultpasswd eq "yes" && $p) ||
+ ($defaultpasswd eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+# misc
+sub check_root {
+ die "You are not root!\n" if $< && !$test;
+}
+
+sub usage {
+ warn <<USAGE;
+usage: adduser
+ [-check_only]
+ [-class login_class]
+ [-config_create]
+ [-dotdir dotdir]
+ [-group login_group]
+ [-h|-help]
+ [-home home]
+ [-message message_file]
+ [-noconfig]
+ [-shell shell]
+ [-s|-silent|-q|-quiet]
+ [-uid uid_start]
+ [-v|-verbose]
+
+home=$home shell=$defaultshell dotdir=$dotdir login_group=$defaultgroup
+login_class=$defaultclass message_file=$send_message uid_start=$uid_start
+USAGE
+ exit 1;
+}
+
+# uniq(1)
+sub uniq {
+ local(@list) = @_;
+ local($e, $last, @array);
+
+ foreach $e (sort @list) {
+ push(@array, $e) unless $e eq $last;
+ $last = $e;
+ }
+ return @array;
+}
+
+# see /usr/src/usr.bin/passwd/local_passwd.c or librcypt, crypt(3)
+sub salt {
+ local($salt); # initialization
+ local($i, $rand);
+ local(@itoa64) = ( '0' .. '9', 'a' .. 'z', 'A' .. 'Z' ); # 0 .. 63
+
+ warn "calculate salt\n" if $verbose > 1;
+ # to64
+ for ($i = 0; $i < 8; $i++) {
+ srand(time + $rand + $$);
+ $rand = rand(25*29*17 + $rand);
+ $salt .= $itoa64[$rand & $#itoa64];
+ }
+ warn "Salt is: $salt\n" if $verbose > 1;
+
+ return $salt;
+}
+
+
+# print banner
+sub copyright {
+ return;
+}
+
+# hints
+sub hints {
+ if ($verbose) {
+ print "Use option ``-silent'' if you don't want see " .
+ "all warnings & questions.\n\n";
+ } else {
+ print "Use option ``-verbose'' if you want see more warnings & " .
+ "questions \nor try to repair bugs.\n\n";
+ }
+}
+
+#
+sub parse_arguments {
+ local(@argv) = @_;
+
+ while ($_ = $argv[0], /^-/) {
+ shift @argv;
+ last if /^--$/;
+ if (/^--?(v|verbose)$/) { $verbose = 1 }
+ elsif (/^--?(s|silent|q|quiet)$/) { $verbose = 0 }
+ elsif (/^--?(debug)$/) { $verbose = 2 }
+ elsif (/^--?(h|help|\?)$/) { &usage }
+ elsif (/^--?(home)$/) { $home = $argv[0]; shift @argv }
+ elsif (/^--?(shell)$/) { $defaultshell = $argv[0]; shift @argv }
+ elsif (/^--?(dotdir)$/) { $dotdir = $argv[0]; shift @argv }
+ elsif (/^--?(uid)$/) { $uid_start = $argv[0]; shift @argv }
+ elsif (/^--?(class)$/) { $defaultclass = $argv[0]; shift @argv }
+ elsif (/^--?(group)$/) { $defaultgroup = $argv[0]; shift @argv }
+ elsif (/^--?(check_only)$/) { $check_only = 1 }
+ elsif (/^--?(message)$/) { $send_message = $argv[0]; shift @argv;
+ $sendmessage = 1; }
+ elsif (/^--?(batch)$/) {
+ warn "The -batch option is not supported anymore.\n",
+ "Please use the pw(8) command line tool!\n";
+ exit(0);
+ }
+ # see &config_read
+ elsif (/^--?(config_create)$/) { &create_conf; }
+ elsif (/^--?(noconfig)$/) { $config_read = 0; }
+ else { &usage }
+ }
+ #&usage if $#argv < 0;
+}
+
+sub basename {
+ local($name) = @_;
+ $name =~ s|/+$||;
+ $name =~ s|.*/+||;
+ return $name;
+}
+
+sub dirname {
+ local($name) = @_;
+ $name = &stripdir($name);
+ $name =~ s|/+[^/]+$||;
+ $name = "/" unless $name; # dirname of / is /
+ return $name;
+}
+
+# return 1 if $file is a readable file or link
+sub filetest {
+ local($file, $verb) = @_;
+
+ if (-e $file) {
+ if (-f $file || -l $file) {
+ return 1 if -r _;
+ warn "$file unreadable\n" if $verbose;
+ } else {
+ warn "$file is not a plain file or link\n" if $verbose;
+ }
+ }
+ return 0;
+}
+
+# create configuration files and exit
+sub create_conf {
+ $create_conf = 1;
+ if ($send_message ne 'no') {
+ &message_create($send_message);
+ } else {
+ &message_create($send_message_bak);
+ }
+ &config_write(1);
+ exit(0);
+}
+
+# log for new user in /var/log/adduser
+sub adduser_log {
+ local($string) = @_;
+ local($e);
+
+ return 1 if $logfile eq "no";
+
+ local($sec, $min, $hour, $mday, $mon, $year) = localtime;
+ $year += 1900;
+ $mon++;
+
+ foreach $e ('sec', 'min', 'hour', 'mday', 'mon', 'year') {
+ # '7' -> '07'
+ eval "\$$e = 0 . \$$e" if (eval "\$$e" < 10);
+ }
+
+ &append_file($logfile, "$year/$mon/$mday $hour:$min:$sec $string");
+}
+
+# create HOME directory, copy dotfiles from $dotdir to $HOME
+sub home_create {
+ local($homedir, $name, $group) = @_;
+ local($rootdir);
+
+ if (-e "$homedir") {
+ warn "HOME Directory ``$homedir'' already exist\a\n";
+ return 0;
+ }
+
+ # if the home directory prefix doesn't exist, create it
+ # First, split the directory into a list; then remove the user's dir
+ @dir = split('/', $homedir); pop(@dir);
+ # Put back together & strip to get directory prefix
+ $rootdir = &stripdir(join('/', @dir));
+
+ if (!&mkdirhier("$rootdir")) {
+ # warn already displayed
+ return 0;
+ }
+
+ if ($dotdir eq 'no') {
+ if (!mkdir("$homedir", 0755)) {
+ warn "$dir: $!\n"; return 0;
+ }
+ system 'chown', "$name:$group", $homedir;
+ return !$?;
+ }
+
+ # copy files from $dotdir to $homedir
+ # rename 'dot.foo' files to '.foo'
+ print "Copy files from $dotdir to $homedir\n" if $verbose;
+ system("cp -R $dotdir $homedir");
+ system("chmod -R u+wrX,go-w $homedir");
+ system("chown -R $name:$group $homedir");
+
+ # security
+ opendir(D, $homedir);
+ foreach $file (readdir(D)) {
+ if ($file =~ /^dot\./ && -f "$homedir/$file") {
+ $file =~ s/^dot\././;
+ rename("$homedir/dot$file", "$homedir/$file");
+ }
+ chmod(0600, "$homedir/$file")
+ if ($file =~ /^\.(rhosts|Xauthority|kermrc|netrc)$/);
+ chmod(0700, "$homedir/$file")
+ if ($file =~ /^(Mail|prv|\.(iscreen|term))$/);
+ }
+ closedir D;
+ return 1;
+}
+
+# makes a directory hierarchy
+sub mkdir_home {
+ local($dir) = @_;
+ $dir = &stripdir($dir);
+ local($user_partition) = "/usr";
+ local($dirname) = &dirname($dir);
+
+ -e $dirname || &mkdirhier($dirname);
+
+ if (((stat($dirname))[0]) == ((stat("/"))[0])){
+ # home partition is on root partition
+ # create home partition on $user_partition and make
+ # a symlink from $dir to $user_partition/`basename $dir`
+ # For instance: /home -> /usr/home
+
+ local($basename) = &basename($dir);
+ local($d) = "$user_partition/$basename";
+
+
+ if (-d $d) {
+ warn "Oops, $d already exist\n" if $verbose;
+ } else {
+ print "Create $d\n" if $verbose;
+ if (!mkdir("$d", 0755)) {
+ warn "$d: $!\a\n"; return 0;
+ }
+ }
+
+ unlink($dir); # symlink to nonexist file
+ print "Create symlink: $dir -> $d\n" if $verbose;
+ if (!symlink("$d", $dir)) {
+ warn "Symlink $d: $!\a\n"; return 0;
+ }
+ } else {
+ print "Create $dir\n" if $verbose;
+ if (!mkdir("$dir", 0755)) {
+ warn "Directory ``$dir'': $!\a\n"; return 0;
+ }
+ }
+ return 1;
+}
+
+sub mkdirhier {
+ local($dir) = @_;
+ local($d,$p);
+
+ $dir = &stripdir($dir);
+
+ foreach $d (split('/', $dir)) {
+ $dir = "$p/$d";
+ $dir =~ s|^//|/|;
+ if (! -e "$dir") {
+ print "Create $dir\n" if $verbose;
+ if (!mkdir("$dir", 0755)) {
+ warn "$dir: $!\n"; return 0;
+ }
+ }
+ $p .= "/$d";
+ }
+ return 1;
+}
+
+# stript unused '/'
+# F.i.: //usr///home// -> /usr/home
+sub stripdir {
+ local($dir) = @_;
+
+ $dir =~ s|/+|/|g; # delete double '/'
+ $dir =~ s|/$||; # delete '/' at end
+ return $dir if $dir ne "";
+ return '/';
+}
+
+# Read one of the elements from @list. $confirm is default.
+# If !$allow accept only elements from @list.
+sub confirm_list {
+ local($message, $allow, $confirm, @list) = @_;
+ local($read, $c, $print);
+
+ $print = "$message" if $message;
+ $print .= " " unless $message =~ /\n$/ || $#list == 0;
+
+ $print .= join($", &uniq(@list)); #"
+ $print .= " " unless $message =~ /\n$/ && $#list == 0;
+ print "$print";
+ print "\n" if (length($print) + length($confirm)) > 60;
+ print "[$confirm]: ";
+
+ chop($read = <STDIN>);
+ $read =~ s/^\s*//;
+ $read =~ s/\s*$//;
+ return $confirm if $read eq "";
+ return "$read" if $allow;
+
+ foreach $c (@list) {
+ return $read if $c eq $read;
+ }
+ warn "$read: is not allowed!\a\n";
+ return &confirm_list($message, $allow, $confirm, @list);
+}
+
+# YES or NO question
+# return 1 if &confirm("message", "yes") and answer is yes
+# or if &confirm("message", "no") an answer is no
+# otherwise 0
+sub confirm_yn {
+ local($message, $confirm) = @_;
+ local($yes) = '^(yes|YES|y|Y)$';
+ local($no) = '^(no|NO|n|N)$';
+ local($read, $c);
+
+ if ($confirm && ($confirm =~ "$yes" || $confirm == 1)) {
+ $confirm = "y";
+ } else {
+ $confirm = "n";
+ }
+ print "$message (y/n) [$confirm]: ";
+ chop($read = <STDIN>);
+ $read =~ s/^\s*//;
+ $read =~ s/\s*$//;
+ return 1 unless $read;
+
+ if (($confirm eq "y" && $read =~ "$yes") ||
+ ($confirm eq "n" && $read =~ "$no")) {
+ return 1;
+ }
+
+ if ($read !~ "$yes" && $read !~ "$no") {
+ warn "Wrong value. Enter again!\a\n";
+ return &confirm_yn($message, $confirm);
+ }
+ return 0;
+}
+
+# test if $dotdir exist
+# return "no" if $dotdir not exist or dotfiles should not copied
+sub dotdir_default {
+ local($dir) = $dotdir;
+
+ return &dotdir_default_valid($dir) unless $verbose;
+ while($verbose) {
+ $dir = &confirm_list("Copy dotfiles from:", 1,
+ $dir, ("no", $dotdir_bak, $dir));
+ last if $dir eq &dotdir_default_valid($dir);
+ }
+ warn "Do not copy dotfiles.\n" if $verbose && $dir eq "no";
+
+ $changes++ if $dir ne $dotdir;
+ return $dir;
+}
+
+sub dotdir_default_valid {
+ local($dir) = @_;
+
+ return $dir if (-e $dir && -r _ && (-d _ || -l $dir) && $dir =~ "^/");
+ return $dir if $dir eq "no";
+ warn "Dotdir ``$dir'' is not a directory\a\n";
+ return "no";
+}
+
+# ask for messages to new users
+sub message_default {
+ local($file) = $send_message;
+ local(@d) = ($file, $send_message_bak, "no");
+
+ while($verbose) {
+ $file = &confirm_list("Send message from file:", 1, $file, @d);
+ last if $file eq "no";
+ last if &filetest($file, 1);
+
+ # maybe create message file
+ &message_create($file) if &confirm_yn("Create ``$file''?", "yes");
+ last if &filetest($file, 0);
+ last if !&confirm_yn("File ``$file'' does not exist, try again?",
+ "yes");
+ }
+
+ if ($file eq "no" || !&filetest($file, 0)) {
+ warn "Do not send message\n" if $verbose;
+ $file = "no";
+ } else {
+ &message_read($file);
+ }
+
+ $changes++ if $file ne $send_message && $verbose;
+ return $file;
+}
+
+# create message file
+sub message_create {
+ local($file) = @_;
+
+ rename($file, "$file.bak");
+ if (!open(M, "> $file")) {
+ warn "Messagefile ``$file'': $!\n"; return 0;
+ }
+ print M <<EOF;
+#
+# Message file for adduser(8)
+# comment: ``#''
+# default variables: \$name, \$fullname, \$password
+# other variables: see /etc/adduser.conf after
+# line ``$do_not_delete''
+#
+
+\$fullname,
+
+your account ``\$name'' was created.
+Have fun!
+
+See also chpass(1), finger(1), passwd(1)
+EOF
+ close M;
+ return 1;
+}
+
+# read message file into buffer
+sub message_read {
+ local($file) = @_;
+ @message_buffer = '';
+
+ if (!open(R, "$file")) {
+ warn "File ``$file'':$!\n"; return 0;
+ }
+ while(<R>) {
+ push(@message_buffer, $_) unless /^\s*#/;
+ }
+ close R;
+}
+
+# write @list to $file with file-locking
+sub append_file {
+ local($file,@list) = @_;
+ local($e);
+ local($LOCK_EX) = 2;
+ local($LOCK_NB) = 4;
+ local($LOCK_UN) = 8;
+
+ open(F, ">> $file") || die "$file: $!\n";
+ print "Lock $file.\n" if $verbose > 1;
+ while(!flock(F, $LOCK_EX | $LOCK_NB)) {
+ warn "Cannot lock file: $file\a\n";
+ die "Sorry, give up\n"
+ unless &confirm_yn("Try again?", "yes");
+ }
+ print F join("\n", @list) . "\n";
+ close F;
+ print "Unlock $file.\n" if $verbose > 1;
+ flock(F, $LOCK_UN);
+}
+
+# return free uid+gid
+# uid == gid if possible
+sub next_id {
+ local($group) = @_;
+
+ $uid_start = 1000 if ($uid_start <= 0 || $uid_start >= $uid_end);
+ # looking for next free uid
+ while($uid{$uid_start}) {
+ $uid_start++;
+ $uid_start = 1000 if $uid_start >= $uid_end;
+ print "$uid_start\n" if $verbose > 1;
+ }
+
+ local($gid_start) = $uid_start;
+ # group for user (username==groupname) already exist
+ if ($groupname{$group}) {
+ $gid_start = $groupname{$group};
+ }
+ # gid is in use, looking for another gid.
+ # Note: uid an gid are not equal
+ elsif ($gid{$uid_start}) {
+ while($gid{$gid_start} || $uid{$gid_start}) {
+ $gid_start--;
+ $gid_start = $uid_end if $gid_start < 100;
+ }
+ }
+ return ($uid_start, $gid_start);
+}
+
+# read config file
+sub config_read {
+ local($opt) = @_;
+ local($user_flag) = 0;
+
+ # don't read config file
+ return 1 if $opt =~ /-(noconfig|config_create)/ || !$config_read;
+
+ if(!open(C, "$config")) {
+ warn "$config: $!\n"; return 0;
+ }
+
+ while(<C>) {
+ # user defined variables
+ /^$do_not_delete/ && $user_flag++;
+ # found @array or $variable
+ if (s/^(\w+\s*=\s*\()/\@$1/ || s/^(\w+\s*=)/\$$1/) {
+ eval $_;
+ #warn "$_";
+ }
+ # lines with '^##' are not saved
+ push(@user_variable_list, $_)
+ if $user_flag && !/^##/ && (s/^[\$\@]// || /^[#\s]/);
+ }
+ #warn "X @user_variable_list X\n";
+ close C;
+}
+
+
+# write config file
+sub config_write {
+ local($silent) = @_;
+
+ # nothing to do
+ return 1 unless ($changes || ! -e $config || !$config_read || $silent);
+
+ if (!$silent) {
+ if (-e $config) {
+ return 1 if &confirm_yn("\nWrite your changes to $config?", "no");
+ } else {
+ return 1 unless
+ &confirm_yn("\nWrite your configuration to $config?", "yes");
+ }
+ }
+
+ rename($config, "$config.bak");
+ open(C, "> $config") || die "$config: $!\n";
+
+ # prepare some variables
+ $send_message = "no" unless $send_message;
+ $defaultpasswd = "no" unless $defaultpasswd;
+ local($shpref) = "'" . join("', '", @shellpref) . "'";
+ local($shpath) = "'" . join("', '", @path) . "'";
+ local($user_var) = join('', @user_variable_list);
+
+ print C <<EOF;
+#
+# $config - automatic generated by adduser(8)
+#
+# Note: adduser read *and* write this file.
+# You may change values, but don't add new things befor the
+# line ``$do_not_delete''
+#
+
+# verbose = [0-2]
+verbose = $verbose
+
+# use password for new users
+# defaultpasswd = yes | no
+defaultpasswd = $defaultpasswd
+
+# copy dotfiles from this dir ("/usr/share/skel" or "no")
+dotdir = "$dotdir"
+
+# send this file to new user ("/etc/adduser.message" or "no")
+send_message = "$send_message"
+
+# config file for adduser ("/etc/adduser.conf")
+config = "$config"
+
+# logfile ("/var/log/adduser" or "no")
+logfile = "$logfile"
+
+# default HOME directory ("/home")
+home = "$home"
+
+# List of directories where shells located
+# path = ('/bin', '/usr/bin', '/usr/local/bin')
+path = ($shpath)
+
+# common shell list, first element has higher priority
+# shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh')
+shellpref = ($shpref)
+
+# defaultshell if not empty ("bash")
+defaultshell = "$defaultshell"
+
+# defaultgroup ('USER' for same as username or any other valid group)
+defaultgroup = $defaultgroup
+
+# defaultclass if not empty
+defaultclass = "$defaultclass"
+
+# new users get this uid (1000)
+uid_start = "$uid_start"
+
+$do_not_delete
+## your own variables, see /etc/adduser.message
+$user_var
+
+## end
+EOF
+ close C;
+}
+
+################
+# main
+#
+$test = 0; # test mode, only for development
+$check_only = 0;
+
+&check_root; # you must be root to run this script!
+&variables; # initialize variables
+&config_read(@ARGV); # read variables form config-file
+&parse_arguments(@ARGV); # parse arguments
+
+if (!$check_only) {
+ &copyright; &hints;
+}
+
+# check
+$changes = 0;
+&passwd_check; # check for valid passwdb
+&shells_read; # read /etc/shells
+&passwd_read; # read /etc/master.passwd
+&group_read; # read /etc/group
+&group_check; # check for incon*
+exit 0 if $check_only; # only check consistence and exit
+
+
+# interactive
+# some questions
+&shells_add; # maybe add some new shells
+$defaultshell = &shell_default; # enter default shell
+$home = &home_partition($home); # find HOME partition
+$dotdir = &dotdir_default; # check $dotdir
+$send_message = &message_default; # send message to new user
+$defaultpasswd = &password_default; # maybe use password
+&config_write(!$verbose); # write variables in file
+
+# main loop for creating new users
+&new_users; # add new users
+
+#end
diff --git a/usr.sbin/adduser/rmuser.8 b/usr.sbin/adduser/rmuser.8
new file mode 100644
index 0000000..5edaeef
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.8
@@ -0,0 +1,169 @@
+.\" 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.
+.\"
+.\" $Id: rmuser.8,v 1.7 1997/09/01 06:14:17 charnier Exp $
+.\"
+.Dd February 23, 1997
+.Dt RMUSER 8
+.Os
+.Sh NAME
+.Nm rmuser
+.Nd removes users from the system
+.Sh SYNOPSIS
+.Nm rmuser
+.Op Fl y
+.Op Ar username
+.Sh DESCRIPTION
+The utility
+.Nm
+.Pp
+.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 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 ,
+.Pa /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 's
+per-user unique groups).
+.El
+.Pp
+.Nm Rmuser
+politely 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, by which the desired
+user's
+.Xr passwd 5
+entry may be removed manually.
+.Pp
+If not running "affirmatively" (i.e., option
+.Fl y
+is not specified),
+.Nm
+shows the selected user's password file entry and asks for confirmation
+that you wish to remove the user. If the user's home directory is owned
+by the user,
+.Nm
+asks whether you wish to remove the user's home directory and everything
+below.
+.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
+Available options:
+.Pp
+.Bl -tag -width username
+.It Fl y
+Affirm - any question that would be asked is answered implicitly in
+the affirmative (i.e., yes). A username must also be specified on the
+command line if this option is used.
+.It Ar \&username
+Identifies the user to be removed; if not present,
+.Nm
+interactively asks for the user to be removed.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -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 addgroup 8 ,
+.Xr adduser 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmgroup 8 ,
+.Xr vipw 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.2 .
+.\" .Sh AUTHOR
+.\" Guy Helmer, Ames, Iowa
+.Sh BUGS
+.Nm Rmuser
+does not comprehensively search the filesystem 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.
+.Nm Rmuser
+also is unable to remove symbolic links that were created by the
+user in
+.Pa /tmp
+or
+.Pa /var/tmp
+as symbolic links on 4.4BSD filesystems 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/username
+and
+.Pa /var/mail/.pop.username
+that are not owned by the removed user but should be removed.
+.Pp
+.Nm Rmuser
+has no knowledge of NIS (Yellow Pages), and it operates only on the
+local password file.
diff --git a/usr.sbin/adduser/rmuser.perl b/usr.sbin/adduser/rmuser.perl
new file mode 100644
index 0000000..57bd74f
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.perl
@@ -0,0 +1,576 @@
+#!/usr/bin/perl
+# -*- perl -*-
+# 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.
+#
+# rmuser - Perl script to remove users
+#
+# Guy Helmer <ghelmer@cs.iastate.edu>, 02/23/97
+#
+# $Id: rmuser.perl,v 1.6 1997/03/08 18:04:45 wosch Exp $
+
+sub LOCK_SH {0x01;}
+sub LOCK_EX {0x02;}
+sub LOCK_NB {0x04;}
+sub LOCK_UN {0x08;}
+sub F_SETFD {2;}
+
+$ENV{"PATH"} = "/bin:/sbin:/usr/bin:/usr/sbin";
+umask(022);
+$whoami = $0;
+$passwd_file = "/etc/master.passwd";
+$new_passwd_file = "${passwd_file}.new.$$";
+$group_file = "/etc/group";
+$new_group_file = "${group_file}.new.$$";
+$mail_dir = "/var/mail";
+$crontab_dir = "/var/cron/tabs";
+$atjob_dir = "/var/at/jobs";
+$affirm = 0;
+
+#$debug = 1;
+
+sub cleanup {
+ local($sig) = @_;
+
+ print STDERR "Caught signal SIG$sig -- cleaning up.\n";
+ &unlockpw;
+ if (-e $new_passwd_file) {
+ unlink $new_passwd_file;
+ }
+ exit(0);
+}
+
+sub lockpw {
+ # Open the password file for reading
+ if (!open(MASTER_PW, "$passwd_file")) {
+ print STDERR "${whoami}: Error: Couldn't open ${passwd_file}: $!\n";
+ exit(1);
+ }
+ # Set the close-on-exec flag just in case
+ fcntl(MASTER_PW, &F_SETFD, 1);
+ # Apply an advisory lock the password file
+ if (!flock(MASTER_PW, &LOCK_EX|&LOCK_NB)) {
+ print STDERR "${whoami}: Error: Couldn't lock ${passwd_file}: $!\n";
+ exit(1);
+ }
+}
+
+sub unlockpw {
+ flock(MASTER_PW, &LOCK_UN);
+}
+
+$SIG{'INT'} = 'cleanup';
+$SIG{'QUIT'} = 'cleanup';
+$SIG{'HUP'} = 'cleanup';
+$SIG{'TERM'} = 'cleanup';
+
+if ($#ARGV == 1 && $ARGV[0] eq '-y') {
+ shift @ARGV;
+ $affirm = 1;
+}
+
+if ($#ARGV > 0) {
+ print STDERR "usage: ${whoami} [-y] [username]\n";
+ exit(1);
+}
+
+if ($< != 0) {
+ print STDERR "${whoami}: Error: you must be root to use ${whoami}\n";
+ exit(1);
+}
+
+&lockpw;
+
+if ($#ARGV == 0) {
+ # Username was given as a parameter
+ $login_name = pop(@ARGV);
+ die "Sorry, login name must contain alphanumeric characters only.\n"
+ if ($login_name !~ /^[a-z0-9_][a-z0-9_\-]*$/);
+} else {
+ if ($affirm) {
+ print STDERR "${whoami}: Error: -y option given without username!\n";
+ &unlockpw;
+ exit 1;
+ }
+ # Get the user name from the user
+ $login_name = &get_login_name;
+}
+
+if (($pw_ent = &check_login_name($login_name)) eq '0') {
+ print STDERR "${whoami}: Error: User ${login_name} not in password database\n";
+ &unlockpw;
+ exit 1;
+}
+
+($name, $password, $uid, $gid, $class, $change, $expire, $gecos, $home_dir,
+ $shell) = split(/:/, $pw_ent);
+
+if ($uid == 0) {
+ print "${whoami}: Error: I'd rather not remove a user with a uid of 0.\n";
+ &unlockpw;
+ exit 1;
+}
+
+if (! $affirm) {
+ print "Matching password entry:\n\n$pw_ent\n\n";
+
+ $ans = &get_yn("Is this the entry you wish to remove? ");
+
+ if ($ans eq 'N') {
+ print "${whoami}: Informational: User ${login_name} not removed.\n";
+ &unlockpw;
+ exit 0;
+ }
+}
+
+#
+# Get owner of user's home directory; don't remove home dir if not
+# owned by $login_name
+
+$remove_directory = 1;
+
+if (-l $home_dir) {
+ $real_home_dir = &resolvelink($home_dir);
+} else {
+ $real_home_dir = $home_dir;
+}
+
+#
+# If home_dir is a symlink and points to something that isn't a directory,
+# or if home_dir is not a symlink and is not a directory, don't remove
+# home_dir -- seems like a good thing to do, but probably isn't necessary...
+
+if (((-l $home_dir) && ((-e $real_home_dir) && !(-d $real_home_dir))) ||
+ (!(-l $home_dir) && !(-d $home_dir))) {
+ print STDERR "${whoami}: Informational: Home ${home_dir} is not a directory, so it won't be removed\n";
+ $remove_directory = 0;
+}
+
+if (length($real_home_dir) && -d $real_home_dir) {
+ $dir_owner = (stat($real_home_dir))[4]; # UID
+ if ($dir_owner != $uid) {
+ print STDERR "${whoami}: Informational: Home dir ${real_home_dir} is" .
+ " not owned by ${login_name} (uid ${dir_owner})\n," .
+ "\tso it won't be removed\n";
+ $remove_directory = 0;
+ }
+}
+
+if ($remove_directory && ! $affirm) {
+ $ans = &get_yn("Remove user's home directory ($home_dir)? ");
+ if ($ans eq 'N') {
+ $remove_directory = 0;
+ }
+}
+
+#exit 0 if $debug;
+
+#
+# Remove the user's crontab, if there is one
+# (probably needs to be done before password databases are updated)
+
+if (-e "$crontab_dir/$login_name") {
+ print STDERR "Removing user's crontab:";
+ system('/usr/bin/crontab', '-u', $login_name, '-r');
+ print STDERR " done.\n";
+}
+
+#
+# Remove the user's at jobs, if any
+# (probably also needs to be done before password databases are updated)
+
+&remove_at_jobs($login_name, $uid);
+
+#
+# Kill all the user's processes
+
+&kill_users_processes($login_name, $uid);
+
+#
+# Copy master password file to new file less removed user's entry
+
+&update_passwd_file;
+
+#
+# Remove the user from all groups in /etc/group
+
+&update_group_file($login_name);
+
+#
+# Remove the user's home directory
+
+if ($remove_directory) {
+ print STDERR "Removing user's home directory ($home_dir):";
+ &remove_dir($home_dir);
+ print STDERR " done.\n";
+}
+
+#
+# Remove files related to the user from the mail directory
+
+#&remove_files_from_dir($mail_dir, $login_name, $uid);
+$file = "$mail_dir/$login_name";
+if (-e $file || -l $file) {
+ print STDERR "Removing user's incoming mail file ${file}:";
+ unlink $file ||
+ print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
+ print STDERR " done.\n";
+}
+
+#
+# Remove some pop daemon's leftover file
+
+$file = "$mail_dir/.${login_name}.pop";
+if (-e $file || -l $file) {
+ print STDERR "Removing pop daemon's temporary mail file ${file}:";
+ unlink $file ||
+ print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
+ print STDERR " done.\n";
+}
+
+#
+# Remove files belonging to the user from the directories /tmp, /var/tmp,
+# and /var/tmp/vi.recover. Note that this doesn't take care of the
+# problem where a user may have directories or symbolic links in those
+# directories -- only regular files are removed.
+
+&remove_files_from_dir('/tmp', $login_name, $uid);
+&remove_files_from_dir('/var/tmp', $login_name, $uid);
+&remove_files_from_dir('/var/tmp/vi.recover', $login_name, $uid)
+ if (-e '/var/tmp/vi.recover');
+
+#
+# All done!
+
+exit 0;
+
+sub get_login_name {
+ #
+ # Get new user's name
+ local($done, $login_name);
+
+ for ($done = 0; ! $done; ) {
+ print "Enter login name for user to remove: ";
+ $login_name = <>;
+ chop $login_name;
+ if (!($login_name =~ /^[a-z0-9_][a-z0-9_\-]*$/)) {
+ print STDERR "Sorry, login name must contain alphanumeric characters only.\n";
+ } elsif (length($login_name) > 16 || length($login_name) == 0) {
+ print STDERR "Sorry, login name must be 16 characters or less.\n";
+ } else {
+ $done = 1;
+ }
+ }
+
+ print "User name is ${login_name}\n" if $debug;
+ return($login_name);
+}
+
+sub check_login_name {
+ #
+ # Check to see whether login name is in password file
+ local($login_name) = @_;
+ local($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
+ $Mgecos, $Mhome_dir, $Mshell);
+ local($i);
+
+ seek(MASTER_PW, 0, 0);
+ while ($i = <MASTER_PW>) {
+ chop $i;
+ ($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
+ $Mgecos, $Mhome_dir, $Mshell) = split(/:/, $i);
+ if ($Mname eq $login_name) {
+ seek(MASTER_PW, 0, 0);
+ return($i); # User is in password database
+ }
+ }
+ seek(MASTER_PW, 0, 0);
+
+ return '0'; # User wasn't found
+}
+
+sub get_yn {
+ #
+ # Get a yes or no answer; return 'Y' or 'N'
+ local($prompt) = @_;
+ local($done, $ans);
+
+ for ($done = 0; ! $done; ) {
+ print $prompt;
+ $ans = <>;
+ chop $ans;
+ $ans =~ tr/a-z/A-Z/;
+ if (!($ans =~ /^[YN]/)) {
+ print STDERR "Please answer (y)es or (n)o.\n";
+ } else {
+ $done = 1;
+ }
+ }
+
+ return(substr($ans, 0, 1));
+}
+
+sub update_passwd_file {
+ local($skipped, $i);
+
+ print STDERR "Updating password file,";
+ seek(MASTER_PW, 0, 0);
+ open(NEW_PW, ">$new_passwd_file") ||
+ die "\n${whoami}: Error: Couldn't open file ${new_passwd_file}:\n $!\n";
+ chmod(0600, $new_passwd_file) ||
+ print STDERR "\n${whoami}: Warning: couldn't set mode of $new_passwd_file to 0600 ($!)\n\tcontinuing, but please check mode of /etc/master.passwd!\n";
+ $skipped = 0;
+ while ($i = <MASTER_PW>) {
+ if ($i =~ /\n$/) {
+ chop $i;
+ }
+ if ($i ne $pw_ent) {
+ print NEW_PW "$i\n";
+ } else {
+ print STDERR "Dropped entry for $login_name\n" if $debug;
+ $skipped = 1;
+ }
+ }
+ close(NEW_PW);
+ seek(MASTER_PW, 0, 0);
+
+ if ($skipped == 0) {
+ print STDERR "\n${whoami}: Whoops! Didn't find ${login_name}'s entry second time around!\n";
+ unlink($new_passwd_file) ||
+ print STDERR "\n${whoami}: Warning: couldn't unlink $new_passwd_file ($!)\n\tPlease investigate, as this file should not be left in the filesystem\n";
+ &unlockpw;
+ exit 1;
+ }
+
+ #
+ # Run pwd_mkdb to install the updated password files and databases
+
+ print STDERR " updating databases,";
+ system('/usr/sbin/pwd_mkdb', '-p', ${new_passwd_file});
+ print STDERR " done.\n";
+
+ close(MASTER_PW); # Not useful anymore
+}
+
+sub update_group_file {
+ local($login_name) = @_;
+
+ local($i, $j, $grmember_list, $new_grent, $changes);
+ local($grname, $grpass, $grgid, $grmember_list, @grmembers);
+
+ $changes = 0;
+ print STDERR "Updating group file:";
+ open(GROUP, $group_file) ||
+ die "\n${whoami}: Error: couldn't open ${group_file}: $!\n";
+ if (!flock(GROUP, &LOCK_EX|&LOCK_NB)) {
+ print STDERR "\n${whoami}: Error: couldn't lock ${group_file}: $!\n";
+ exit 1;
+ }
+ local($group_perms, $group_uid, $group_gid) =
+ (stat(GROUP))[2, 4, 5]; # File Mode, uid, gid
+ open(NEW_GROUP, ">$new_group_file") ||
+ die "\n${whoami}: Error: couldn't open ${new_group_file}: $!\n";
+ chmod($group_perms, $new_group_file) ||
+ printf STDERR "\n${whoami}: Warning: could not set permissions of new group file to %o ($!)\n\tContinuing, but please check permissions of $group_file!\n", $group_perms;
+ chown($group_uid, $group_gid, $new_group_file) ||
+ print STDERR "\n${whoami}: Warning: could not set owner/group of new group file to ${group_uid}/${group_gid} ($!)\n\rContinuing, but please check ownership of $group_file!\n";
+ while ($i = <GROUP>) {
+ if (!($i =~ /$login_name/)) {
+ # Line doesn't contain any references to the user, so just add it
+ # to the new file
+ print NEW_GROUP $i;
+ } else {
+ #
+ # Remove the user from the group
+ if ($i =~ /\n$/) {
+ chop $i;
+ }
+ ($grname, $grpass, $grgid, $grmember_list) = split(/:/, $i);
+ @grmembers = split(/,/, $grmember_list);
+ undef @new_grmembers;
+ local(@new_grmembers);
+ foreach $j (@grmembers) {
+ if ($j ne $login_name) {
+ push(@new_grmembers, $j);
+ } else {
+ print STDERR " $grname";
+ $changes = 1;
+ }
+ }
+ if ($grname eq $login_name && $#new_grmembers == -1) {
+ # Remove a user's personal group if empty
+ print STDERR " (removing group $grname -- personal group is empty)";
+ $changes = 1;
+ } else {
+ $grmember_list = join(',', @new_grmembers);
+ $new_grent = join(':', $grname, $grpass, $grgid, $grmember_list);
+ print NEW_GROUP "$new_grent\n";
+ }
+ }
+ }
+ close(NEW_GROUP);
+ rename($new_group_file, $group_file) || # Replace old group file with new
+ die "\n${whoami}: Error: couldn't rename $new_group_file to $group_file ($!)\n";
+ close(GROUP); # File handle is worthless now
+ print STDERR " (no changes)" if (! $changes);
+ print STDERR " done.\n";
+}
+
+sub remove_dir {
+ # Remove the user's home directory
+ local($dir) = @_;
+ local($linkdir);
+
+ if (-l $dir) {
+ $linkdir = &resolvelink($dir);
+ # Remove the symbolic link
+ unlink($dir) ||
+ warn "${whoami}: Warning: could not unlink symlink $dir: $!\n";
+ if (!(-e $linkdir)) {
+ #
+ # Dangling symlink - just return now
+ return;
+ }
+ # Set dir to be the resolved pathname
+ $dir = $linkdir;
+ }
+ if (!(-d $dir)) {
+ print STDERR "${whoami}: Warning: $dir is not a directory\n";
+ unlink($dir) || warn "${whoami}: Warning: could not unlink $dir: $!\n";
+ return;
+ }
+ system('/bin/rm', '-rf', $dir);
+}
+
+sub remove_files_from_dir {
+ local($dir, $login_name, $uid) = @_;
+ local($path, $i, $owner);
+
+ print STDERR "Removing files belonging to ${login_name} from ${dir}:";
+
+ if (!opendir(DELDIR, $dir)) {
+ print STDERR "\n${whoami}: Warning: couldn't open directory ${dir} ($!)\n";
+ return;
+ }
+ while ($i = readdir(DELDIR)) {
+ next if $i eq '.';
+ next if $i eq '..';
+
+ $owner = (stat("$dir/$i"))[4]; # UID
+ if ($uid == $owner) {
+ if (-f "$dir/$i") {
+ print STDERR " $i";
+ unlink "$dir/$i" ||
+ print STDERR "\n${whoami}: Warning: unlink on ${dir}/${i} failed ($!) - continuing\n";
+ } else {
+ print STDERR " ($i not a regular file - skipped)";
+ }
+ }
+ }
+ closedir(DELDIR);
+
+ printf STDERR " done.\n";
+}
+
+sub remove_at_jobs {
+ local($login_name, $uid) = @_;
+ local($i, $owner, $found);
+
+ $found = 0;
+ opendir(ATDIR, $atjob_dir) || return;
+ while ($i = readdir(ATDIR)) {
+ next if $i eq '.';
+ next if $i eq '..';
+ next if $i eq '.lockfile';
+
+ $owner = (stat("$atjob_dir/$i"))[4]; # UID
+ if ($uid == $owner) {
+ if (!$found) {
+ print STDERR "Removing user's at jobs:";
+ $found = 1;
+ }
+ # Use atrm to remove the job
+ print STDERR " $i";
+ system('/usr/bin/atrm', $i);
+ }
+ }
+ closedir(ATDIR);
+ if ($found) {
+ print STDERR " done.\n";
+ }
+}
+
+sub resolvelink {
+ local($path) = @_;
+ local($l);
+
+ while (-l $path && -e $path) {
+ if (!defined($l = readlink($path))) {
+ die "${whoami}: readlink on $path failed (but it should have worked!): $!\n";
+ }
+ if ($l =~ /^\//) {
+ # Absolute link
+ $path = $l;
+ } else {
+ # Relative link
+ $path =~ s/\/[^\/]+\/?$/\/$l/; # Replace last component of path
+ }
+ }
+ return $path;
+}
+
+sub kill_users_processes {
+ local($login_name, $uid) = @_;
+ local($pid, $result);
+
+ #
+ # Do something a little complex: fork a child that changes its
+ # real and effective UID to that of the removed user, then issues
+ # a "kill(9, -1)" to kill all processes of the same uid as the sender
+ # (see kill(2) for details).
+ # The parent waits for the exit of the child and then returns.
+
+ if ($pid = fork) {
+ # Parent process
+ waitpid($pid, 0);
+ } elsif (defined $pid) {
+ # Child process
+ $< = $uid;
+ $> = $uid;
+ if ($< != $uid || $> != $uid) {
+ print STDERR "${whoami}: Error (kill_users_processes):\n" .
+ "\tCouldn't reset uid/euid to ${uid}: current uid/euid's are $< and $>\n";
+ exit 1;
+ }
+ $result = kill(9, -1);
+ print STDERR "Killed process(es) belonging to $login_name.\n"
+ if $result;
+ exit 0;
+ } else {
+ # Couldn't fork!
+ print STDERR "${whoami}: Error: couldn't fork to kill ${login_name}'s processes - continuing\n";
+ }
+}
diff --git a/usr.sbin/amd/Makefile b/usr.sbin/amd/Makefile
new file mode 100644
index 0000000..6eff9c4
--- /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
+#
+# $Id: Makefile,v 1.4 1998/09/02 21:09:09 brian Exp $
+#
+
+SUBDIR= 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..32c6370
--- /dev/null
+++ b/usr.sbin/amd/Makefile.inc
@@ -0,0 +1,53 @@
+# ex:ts=8
+#
+# $Id: Makefile.inc,v 1.3 1998/09/09 16:11:01 obrien Exp $
+#
+# 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 $
+#
+
+#INCGEN!= cd ${.CURDIR}/../include; \
+# printf 'xwhere: .MAKE\n\t@echo \$${.OBJDIR}\n' | ${MAKE} -Bs -f-
+
+#CFLAGS+= -I${INCGEN}
+CFLAGS+= -I. -I${.CURDIR}
+CFLAGS+= -I${.CURDIR}/../include
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/include
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd
+CFLAGS+= -DHAVE_CONFIG_H
+
+LIBAMUDIR!= cd ${.CURDIR}/../libamu; \
+ printf 'xwhere: .MAKE\n\t@echo \$${.OBJDIR}\n' | ${MAKE} -Bs -f-
+LIBAMU= ${LIBAMUDIR}/libamu.a
+
+.if !defined(INFO)
+
+SRCS+= config_local.h
+CLEANFILES+= config_local.h
+config_local.h: ${.CURDIR}/../include/newvers.sh
+ @rm -f ${.TARGET}
+ sh ${.ALLSRC} > ${.TARGET}
+
+
+RPCCOM= rpcgen
+MOUNT_X= ${DESTDIR}/usr/include/rpcsvc/mount.x
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+# These are generated at compile time
+SRCS+= mount.h nfs_prot.h
+CLEANFILES+= mount.h nfs_prot.h
+
+mount.h: ${MOUNT_X}
+ ${RPCCOM} -h -C -DWANT_NFS3 ${MOUNT_X} -o ${.TARGET}
+
+nfs_prot.h: ${NFS_PROT_X}
+ ${RPCCOM} -h -C -DWANT_NFS3 ${NFS_PROT_X} -o ${.TARGET}
+
+.endif
+
+.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..48a3ec2
--- /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
+#
+# $Id: Makefile,v 1.15 1998/08/30 19:59:49 gpalmer Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amd
+
+PROG= amd
+MAN8= 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_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_lofs.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
+
+DPADD= ${LIBAMU} ${LIBRPCSVC}
+LDADD= ${LIBAMU} -lrpcsvc
+
+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..4681a29
--- /dev/null
+++ b/usr.sbin/amd/amq/Makefile
@@ -0,0 +1,21 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.10 1998/08/27 08:09:40 obrien Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+PROG= amq
+MAN8= 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..99e9902
--- /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.
+
+# $Id: Makefile,v 1.8 1998/08/27 08:09:41 obrien Exp $
+
+INFO= am-utils
+
+INFOSECTION= "AMD Documentation"
+INFOENTRY= "* Am-utils: (am-utils). The Amd automounter suite of utilities"
+
+MAKEINFOFLAGS+= -I ${.CURDIR}/../../../contrib/amd/doc
+
+.PATH: ${.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..3ea214f
--- /dev/null
+++ b/usr.sbin/amd/fixmount/Makefile
@@ -0,0 +1,26 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.2 1998/08/30 19:59:52 gpalmer Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fixmount \
+ ${.CURDIR}/../../../contrib/amd/conf/checkmount
+
+PROG= fixmount
+MAN8= fixmount.8
+
+SRCS= fixmount.c
+
+# These are generated at compile time
+SRCS+= mount.h nfs_prot.h
+
+# 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..5e54f6c
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/Makefile
@@ -0,0 +1,24 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id$
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/fsinfo
+
+PROG= fsinfo
+MAN8= 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..3cf19bc
--- /dev/null
+++ b/usr.sbin/amd/hlfsd/Makefile
@@ -0,0 +1,21 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.2 1998/08/30 19:59:52 gpalmer Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/hlfsd
+
+PROG= hlfsd
+MAN8= hlfsd.8
+
+SRCS= hlfsd.c homedir.c nfs_prot_svc.c stubs.c
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/hlfsd
+
+DPADD= ${LIBAMU} ${LIBRPCSVC}
+LDADD= ${LIBAMU} -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/include/Makefile b/usr.sbin/amd/include/Makefile
new file mode 100644
index 0000000..edf048b
--- /dev/null
+++ b/usr.sbin/amd/include/Makefile
@@ -0,0 +1,13 @@
+# ex:ts=8
+#
+# $Id$
+#
+# 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 $
+#
+
+all: config_local.h
+
+.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..802b7a4
--- /dev/null
+++ b/usr.sbin/amd/include/aux_conf.h
@@ -0,0 +1,80 @@
+/*
+ * 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_GET_DIRECTLY_FROM_FILE
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) mount(type, mnt->mnt_dir, flags, mnt_data)
+#endif /* COMMENT_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_GET_DIRECTLY_FROM_FILE
+#define NFS_FH_DREF(dst, src) (dst) = (u_char *) (src)
+#endif /* COMMENT_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_GET_DIRECTLY_FROM_FILE
+#define NFS_SA_DREF(dst, src) { \
+ (dst)->addr = (struct sockaddr *) (src); \
+ (dst)->addrlen = sizeof(*src); \
+ }
+#endif /* COMMENT_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_GET_DIRECTLY_FROM_FILE
+#define NFS_HN_DREF(dst, src) (dst) = (src)
+#endif /* COMMENT_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..af004d1
--- /dev/null
+++ b/usr.sbin/amd/include/config.h
@@ -0,0 +1,1946 @@
+/*
+ * $Id: config.h,v 1.10 1999/02/13 11:06:19 obrien Exp $
+ *
+ * portions derived from
+ * $NetBSD: config.h,v 1.11 1998/08/08 22:33:37 christos Exp $
+ *
+ */
+
+/* config.h. Generated automatically by configure. */
+/* aux/config.h.in. Generated automatically from ./aux/configure.in by autoheader. */
+/*
+ * Start of am-utils-6.x config.h file.
+ * Erez Zadok <ezk@cs.columbia.edu>
+ *
+ * DO NOT EDIT BY HAND.
+ * Note: accconfig.h generates config.h.in, which generates config.h.
+ */
+
+#ifndef _CONFIG_H
+#define _CONFIG_H
+
+
+/*
+ * Check for types of amd filesystems available.
+ */
+
+/* Define if have automount filesystem */
+#define HAVE_AM_FS_AUTO 1
+
+/* Define if have direct automount filesystem */
+#define HAVE_AM_FS_DIRECT 1
+
+/* Define if have "top-level" filesystem */
+#define HAVE_AM_FS_TOPLVL 1
+
+/* Define if have error filesystem */
+#define HAVE_AM_FS_ERROR 1
+
+/* Define if have inheritance filesystem */
+#define HAVE_AM_FS_INHERIT 1
+
+/* Define if have program filesystem */
+#define HAVE_AM_FS_PROGRAM 1
+
+/* Define if have symbolic-link filesystem */
+#define HAVE_AM_FS_LINK 1
+
+/* Define if have symlink with exitence check filesystem */
+#define HAVE_AM_FS_LINKX 1
+
+/* Define if have NFS host-tree filesystem */
+#define HAVE_AM_FS_HOST 1
+
+/* Define if have nfsl (NFS with local link check) filesystem */
+#define HAVE_AM_FS_NFSL 1
+
+/* Define if have multi-NFS filesystem */
+#define HAVE_AM_FS_NFSX 1
+
+/* Define if have union filesystem */
+#define HAVE_AM_FS_UNION 1
+
+/* Define if have Sun's autofs filesystem (NO LONGER NEEDED?) */
+/* #undef HAVE_AM_FS_AUTOFS */
+
+
+/*
+ * Check for types of maps available.
+ */
+
+/* Define if have file maps (everyone should have it!) */
+#define HAVE_MAP_FILE 1
+
+/* Define if have NIS maps */
+#define HAVE_MAP_NIS 1
+
+/* Define if have NIS+ maps */
+/* #undef HAVE_MAP_NISPLUS */
+
+/* Define if have DBM maps */
+/* #undef HAVE_MAP_DBM */
+
+/* Define if have NDBM maps */
+#define HAVE_MAP_NDBM 1
+
+/* Define if have HESIOD maps */
+/* #undef HAVE_MAP_HESIOD */
+
+/* Define if have LDAP maps */
+/* #undef HAVE_MAP_LDAP */
+
+/* Define if have PASSWD maps */
+#define HAVE_MAP_PASSWD 1
+
+/* Define if have UNION maps */
+#define HAVE_MAP_UNION 1
+
+/*
+ * Check for filesystem types available.
+ */
+
+/* Define if have UFS filesystem */
+#define HAVE_FS_UFS 1
+
+/* Define if have XFS filesystem (irix) */
+/* #undef HAVE_FS_XFS */
+
+/* Define if have EFS filesystem (irix) */
+/* #undef HAVE_FS_EFS */
+
+/* Define if have NFS filesystem */
+#define HAVE_FS_NFS 1
+
+/* Define if have NFS3 filesystem */
+#define HAVE_FS_NFS3 1
+
+/* Define if have PCFS filesystem */
+#define HAVE_FS_PCFS 1
+
+/* Define if have LOFS filesystem */
+#define HAVE_FS_LOFS 1
+
+/* Define if have HSFS filesystem */
+/* #undef HAVE_FS_HSFS */
+
+/* Define if have CDFS filesystem */
+#define HAVE_FS_CDFS 1
+
+/* Define if have TFS filesystem */
+#define HAVE_FS_TFS 1
+
+/* Define if have TMPFS filesystem */
+/* #undef HAVE_FS_TMPFS */
+
+/* Define if have MFS filesystem */
+#define HAVE_FS_MFS 1
+
+/* Define if have CFS (crypto) filesystem */
+/* #undef HAVE_FS_CFS */
+
+/* Define if have AUTOFS filesystem */
+/* #undef HAVE_FS_AUTOFS */
+
+/* Define if have CACHEFS filesystem */
+/* #undef HAVE_FS_CACHEFS */
+
+/* Define if have NULLFS (loopback on bsd44) filesystem */
+#define HAVE_FS_NULLFS 1
+
+/* Define if have UNIONFS filesystem */
+#define HAVE_FS_UNIONFS 1
+
+/* Define if have UMAPFS (uid/gid mapping) filesystem */
+#define HAVE_FS_UMAPFS 1
+
+
+/*
+ * Check for the type of the mount(2) system name for a filesystem.
+ * Normally this is "nfs" (e.g. Solaris) or an integer (older systems)
+ */
+
+/* Mount(2) type/name for UFS filesystem */
+#define MOUNT_TYPE_UFS "ufs"
+
+/* Mount(2) type/name for XFS filesystem (irix) */
+/* #undef MOUNT_TYPE_XFS */
+
+/* Mount(2) type/name for EFS filesystem (irix) */
+/* #undef MOUNT_TYPE_EFS */
+
+/* Mount(2) type/name for NFS filesystem */
+#define MOUNT_TYPE_NFS "nfs"
+
+/* Mount(2) type/name for NFS3 filesystem */
+#define MOUNT_TYPE_NFS3 "nfs"
+
+/* 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 "msdos"
+
+/* Mount(2) type/name for LOFS filesystem */
+#define MOUNT_TYPE_LOFS "lofs"
+
+/* Mount(2) type/name for CDFS filesystem */
+#define MOUNT_TYPE_CDFS "cd9660"
+
+/* Mount(2) type/name for TFS filesystem */
+#define MOUNT_TYPE_TFS "tfs"
+
+/* Mount(2) type/name for TMPFS filesystem */
+/* #undef MOUNT_TYPE_TMPFS */
+
+/* Mount(2) type/name for MFS filesystem */
+#define MOUNT_TYPE_MFS "mfs"
+
+/* Mount(2) type/name for CFS (crypto) filesystem */
+/* #undef MOUNT_TYPE_CFS */
+
+/* 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 IGNORE filesystem (not real just ignore for df) */
+/* #undef MOUNT_TYPE_IGNORE */
+
+/* Mount(2) type/name for NULLFS (loopback on bsd44) filesystem */
+#define MOUNT_TYPE_NULLFS "null"
+
+/* Mount(2) type/name for UNIONFS filesystem */
+#define MOUNT_TYPE_UNIONFS "union"
+
+/* Mount(2) type/name for UMAPFS (uid/gid mapping) filesystem */
+#define MOUNT_TYPE_UMAPFS "umap"
+
+
+/*
+ * Check for the string name for the mount-table of a filesystem.
+ */
+
+/* Mount-table entry name for UFS filesystem */
+#define MNTTAB_TYPE_UFS "ufs"
+
+/* Mount-table entry name for XFS filesystem (irix) */
+/* #undef MNTTAB_TYPE_XFS */
+
+/* Mount-table entry name for EFS filesystem (irix) */
+/* #undef MNTTAB_TYPE_EFS */
+
+/* Mount-table entry name for NFS filesystem */
+#define MNTTAB_TYPE_NFS "nfs"
+
+/* Mount-table entry name for NFS3 filesystem */
+#define MNTTAB_TYPE_NFS3 "nfs"
+
+/* Mount-table entry name for PCFS filesystem */
+#define MNTTAB_TYPE_PCFS "msdos"
+
+/* Mount-table entry name for LOFS filesystem */
+#define MNTTAB_TYPE_LOFS "lofs"
+
+/* Mount-table entry name for CDFS filesystem */
+#define MNTTAB_TYPE_CDFS "cd9660"
+
+/* Mount-table entry name for TFS filesystem */
+#define MNTTAB_TYPE_TFS "tfs"
+
+/* Mount-table entry name for TMPFS filesystem */
+/* #undef MNTTAB_TYPE_TMPFS */
+
+/* Mount-table entry name for MFS filesystem */
+#define MNTTAB_TYPE_MFS "mfs"
+
+/* Mount-table entry name for CFS (crypto) filesystem */
+/* #undef MNTTAB_TYPE_CFS */
+
+/* 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 NULLFS (loopback on bsd44) filesystem */
+#define MNTTAB_TYPE_NULLFS "null"
+
+/* Mount-table entry name for UNIONFS filesystem */
+#define MNTTAB_TYPE_UNIONFS "union"
+
+/* Mount-table entry name for UMAPFS (uid/gid mapping) filesystem */
+#define MNTTAB_TYPE_UMAPFS "umap"
+
+/*
+ * Name of mount table file name.
+ */
+/* #undef MNTTAB_FILE_NAME */
+
+/* Name of mount type to hide amd mount from df(1) */
+#define HIDE_MOUNT_TYPE "nfs"
+
+/*
+ * Names of various mount table option strings.
+ */
+
+/* Mount Table option string: Read only */
+/* #undef MNTTAB_OPT_RO */
+
+/* Mount Table option string: Read/write */
+/* #undef MNTTAB_OPT_RW */
+
+/* Mount Table option string: Read/write with quotas */
+/* #undef MNTTAB_OPT_RQ */
+
+/* Mount Table option string: Check quotas */
+/* #undef MNTTAB_OPT_QUOTA */
+
+/* Mount Table option string: Don't check quotas */
+/* #undef MNTTAB_OPT_NOQUOTA */
+
+/* Mount Table option string: action to taken on error */
+/* #undef MNTTAB_OPT_ONERROR */
+
+/* Mount Table option string: min. time between inconsistencies */
+/* #undef MNTTAB_OPT_TOOSOON */
+
+/* Mount Table option string: Soft mount */
+/* #undef MNTTAB_OPT_SOFT */
+
+/* Mount Table option string: spongy mount */
+/* #undef MNTTAB_OPT_SPONGY */
+
+/* Mount Table option string: Hard mount */
+/* #undef MNTTAB_OPT_HARD */
+
+/* Mount Table option string: Set uid allowed */
+/* #undef MNTTAB_OPT_SUID */
+
+/* Mount Table option string: Set uid not allowed */
+/* #undef MNTTAB_OPT_NOSUID */
+
+/* Mount Table option string: SysV-compatible gid on create */
+/* #undef MNTTAB_OPT_GRPID */
+
+/* Mount Table option string: Change mount options */
+/* #undef MNTTAB_OPT_REMOUNT */
+
+/* Mount Table option string: Disallow mounts on subdirs */
+/* #undef MNTTAB_OPT_NOSUB */
+
+/* Mount Table option string: Do multi-component lookup */
+/* #undef MNTTAB_OPT_MULTI */
+
+/* Mount Table option string: Allow NFS ops to be interrupted */
+/* #undef MNTTAB_OPT_INTR */
+
+/* Mount Table option string: Don't allow interrupted ops */
+/* #undef MNTTAB_OPT_NOINTR */
+
+/* Mount Table option string: NFS server IP port number */
+/* #undef MNTTAB_OPT_PORT */
+
+/* Mount Table option string: Secure (AUTH_DES) mounting */
+/* #undef MNTTAB_OPT_SECURE */
+
+/* Mount Table option string: Secure (AUTH_Kerb) mounting */
+/* #undef MNTTAB_OPT_KERB */
+
+/* Mount Table option string: Max NFS read size (bytes) */
+/* #undef MNTTAB_OPT_RSIZE */
+
+/* Mount Table option string: Max NFS write size (bytes) */
+/* #undef MNTTAB_OPT_WSIZE */
+
+/* Mount Table option string: NFS timeout (1/10 sec) */
+/* #undef MNTTAB_OPT_TIMEO */
+
+/* Mount Table option string: Max retransmissions (soft mnts) */
+/* #undef MNTTAB_OPT_RETRANS */
+
+/* Mount Table option string: Attr cache timeout (sec) */
+/* #undef MNTTAB_OPT_ACTIMEO */
+
+/* Mount Table option string: Min attr cache timeout (files) */
+/* #undef MNTTAB_OPT_ACREGMIN */
+
+/* Mount Table option string: Max attr cache timeout (files) */
+/* #undef MNTTAB_OPT_ACREGMAX */
+
+/* Mount Table option string: Min attr cache timeout (dirs) */
+/* #undef MNTTAB_OPT_ACDIRMIN */
+
+/* Mount Table option string: Max attr cache timeout (dirs) */
+/* #undef MNTTAB_OPT_ACDIRMAX */
+
+/* Mount Table option string: Don't cache attributes at all */
+/* #undef MNTTAB_OPT_NOAC */
+
+/* Mount Table option string: No close-to-open consistency */
+/* #undef MNTTAB_OPT_NOCTO */
+
+/* Mount Table option string: Do mount retries in background */
+/* #undef MNTTAB_OPT_BG */
+
+/* Mount Table option string: Do mount retries in foreground */
+/* #undef MNTTAB_OPT_FG */
+
+/* Mount Table option string: Number of mount retries */
+/* #undef MNTTAB_OPT_RETRY */
+
+/* Mount Table option string: Device id of mounted fs */
+/* #undef MNTTAB_OPT_DEV */
+
+/* Mount Table option string: Filesystem id of mounted fs */
+/* #undef MNTTAB_OPT_FSID */
+
+/* Mount Table option string: Get static pathconf for mount */
+/* #undef MNTTAB_OPT_POSIX */
+
+/* Mount Table option string: Automount map */
+/* #undef MNTTAB_OPT_MAP */
+
+/* Mount Table option string: Automount direct map mount */
+/* #undef MNTTAB_OPT_DIRECT */
+
+/* Mount Table option string: Automount indirect map mount */
+/* #undef MNTTAB_OPT_INDIRECT */
+
+/* Mount Table option string: Local locking (no lock manager) */
+/* #undef MNTTAB_OPT_LLOCK */
+
+/* Mount Table option string: Ignore this entry */
+/* #undef MNTTAB_OPT_IGNORE */
+
+/* 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: protocol version number indicator */
+/* #undef MNTTAB_OPT_VERS */
+
+/* Mount Table option string: protocol network_id indicator */
+/* #undef MNTTAB_OPT_PROTO */
+
+/* Mount Table option string: Synchronous local directory ops */
+/* #undef MNTTAB_OPT_SYNCDIR */
+
+/* Mount Table option string: Do no allow setting sec attrs */
+/* #undef MNTTAB_OPT_NOSETSEC */
+
+/* Mount Table option string: set symlink cache time-to-live */
+/* #undef MNTTAB_OPT_SYMTTL */
+
+/* Mount Table option string: compress */
+/* #undef MNTTAB_OPT_COMPRESS */
+
+/* Mount Table option string: paging threshold */
+/* #undef MNTTAB_OPT_PGTHRESH */
+
+/* Mount Table option string: max groups */
+/* #undef MNTTAB_OPT_MAXGROUPS */
+
+/*
+ * Generic mount(2) options (hex numbers)
+ */
+
+/* asynchronous filesystem access */
+/* #undef MNT2_GEN_OPT_ASYNC */
+
+/* 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 */
+
+/* journaling filesystem (AIX's UFS/FFS) */
+/* #undef MNT2_GEN_OPT_JFS */
+
+/* old BSD group-id on create */
+/* #undef MNT2_GEN_OPT_GRPID */
+
+/* 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 */
+
+/* not a device */
+#define MNT2_GEN_OPT_NODEV 0x10
+
+/* no exec calls allowed */
+#define MNT2_GEN_OPT_NOEXEC 0x4
+
+/* not a device */
+/* #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 */
+
+/* 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) */
+/* #undef MNT2_GEN_OPT_SYNCHRONOUS */
+
+/* Mount with Sys 5-specific semantics */
+/* #undef MNT2_GEN_OPT_SYS5 */
+
+/* Union mount */
+/* #undef MNT2_GEN_OPT_UNION */
+
+/*
+ * NFS-specific mount(2) options (hex numbers)
+ */
+
+/* hide mount type from df(1) */
+/* #undef MNT2_NFS_OPT_AUTO */
+
+/* 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 */
+
+/* set dead server retry thresh */
+/* #undef MNT2_NFS_OPT_DEADTHRESH */
+
+/* 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
+
+/* System V-style gid inheritance */
+/* #undef MNT2_NFS_OPT_GRPID */
+
+/* Has authenticator */
+/* #undef MNT2_NFS_OPT_HASAUTH */
+
+/* provide name of server's fs to system */
+/* #undef MNT2_NFS_OPT_FSNAME */
+
+/* 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 */
+
+/* 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 */
+
+/* Get lease for lookup */
+/* #undef MNT2_NFS_OPT_NQLOOKLEASE */
+
+/* Use Nqnfs protocol */
+/* #undef MNT2_NFS_OPT_NQNFS */
+
+/* static pathconf kludge info */
+/* #undef MNT2_NFS_OPT_POSIX */
+
+/* Rcv socket lock */
+/* #undef MNT2_NFS_OPT_RCVLOCK */
+
+/* Do lookup with readdir (nqnfs) */
+/* #undef MNT2_NFS_OPT_RDIRALOOK */
+
+/* set read ahead */
+/* #undef MNT2_NFS_OPT_READAHEAD */
+
+/* Allocate a reserved port */
+#define MNT2_NFS_OPT_RESVPORT 0x8000
+
+/* set number of request retrys */
+#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 initial timeout */
+#define MNT2_NFS_OPT_TIMEO 0x8
+
+/* use TCP for mounts */
+/* #undef MNT2_NFS_OPT_TCP */
+
+/* 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
+
+/* set symlink cache time-to-live */
+/* #undef MNT2_NFS_OPT_SYMTTL */
+
+/* paging threshold */
+/* #undef MNT2_NFS_OPT_PGTHRESH */
+
+/*
+ * CDFS-specific mount(2) options (hex numbers)
+ */
+
+/* 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 */
+
+/*
+ * Existence of fields in structures.
+ */
+
+/* does mntent_t have mnt_cnode field? */
+/* #undef HAVE_FIELD_MNTENT_T_MNT_CNODE */
+
+/* does mntent_t have mnt_time field? */
+/* #undef HAVE_FIELD_MNTENT_T_MNT_TIME */
+
+/* does mntent_t have mnt_time field and is of type "char *" ? */
+/* #undef HAVE_FIELD_MNTENT_T_MNT_TIME_STRING */
+
+/* does mntent_t have mnt_ro field? */
+/* #undef HAVE_FIELD_MNTENT_T_MNT_RO */
+
+/* does cdfs_args_t have flags field? */
+#define HAVE_FIELD_CDFS_ARGS_T_FLAGS 1
+
+/* does cdfs_args_t have fspec field? */
+#define HAVE_FIELD_CDFS_ARGS_T_FSPEC 1
+
+/* does cdfs_args_t have iso_flags field? */
+/* #undef HAVE_FIELD_CDFS_ARGS_T_ISO_FLAGS */
+
+/* does cdfs_args_t have iso_pgthresh field? */
+/* #undef HAVE_FIELD_CDFS_ARGS_T_ISO_PGTHRESH */
+
+/* does cdfs_args_t have norrip field? */
+/* #undef HAVE_FIELD_CDFS_ARGS_T_NORRIP */
+
+/* does cdfs_args_t have ssector field? */
+#define HAVE_FIELD_CDFS_ARGS_T_SSECTOR 1
+
+/* does pcfs_args_t have dsttime field? */
+/* #undef HAVE_FIELD_PCFS_ARGS_T_DSTTIME */
+
+/* does pcfs_args_t have fspec field? */
+#define HAVE_FIELD_PCFS_ARGS_T_FSPEC 1
+
+/* does pcfs_args_t have gid field? */
+#define HAVE_FIELD_PCFS_ARGS_T_GID 1
+
+/* does pcfs_args_t have mask field? */
+#define HAVE_FIELD_PCFS_ARGS_T_MASK 1
+
+/* does pcfs_args_t have secondswest field? */
+/* #undef HAVE_FIELD_PCFS_ARGS_T_SECONDSWEST */
+
+/* does pcfs_args_t have uid field? */
+#define HAVE_FIELD_PCFS_ARGS_T_UID 1
+
+/* does ufs_args_t have flags field? */
+/* #undef HAVE_FIELD_UFS_ARGS_T_FLAGS */
+
+/* does ufs_args_t have fspec field? */
+#define HAVE_FIELD_UFS_ARGS_T_FSPEC 1
+
+/* does efs_args_t have flags field? */
+/* #undef HAVE_FIELD_EFS_ARGS_T_FLAGS */
+
+/* does efs_args_t have fspec field? */
+/* #undef HAVE_FIELD_EFS_ARGS_T_FSPEC */
+
+/* does xfs_args_t have flags field? */
+/* #undef HAVE_FIELD_XFS_ARGS_T_FLAGS */
+
+/* does xfs_args_t have fspec field? */
+/* #undef HAVE_FIELD_XFS_ARGS_T_FSPEC */
+
+/* does ufs_ars_t have ufs_flags field? */
+/* #undef HAVE_FIELD_UFS_ARGS_T_UFS_FLAGS */
+
+/* does ufs_ars_t have ufs_pgthresh field? */
+/* #undef HAVE_FIELD_UFS_ARGS_T_UFS_PGTHRESH */
+
+/* does struct fhstatus have an fhs_fh field? */
+/* #undef HAVE_FIELD_STRUCT_FHSTATUS_FHS_FH */
+
+/* does struct statfs have an f_fstypename field? */
+#define HAVE_FIELD_STRUCT_STATFS_F_FSTYPENAME 1
+
+/* does struct nfs_args have an acdirmin field? */
+#define HAVE_FIELD_NFS_ARGS_T_ACDIRMIN 1
+
+/* does struct nfs_args have an acregmin field? */
+#define HAVE_FIELD_NFS_ARGS_T_ACREGMIN 1
+
+/* does struct nfs_args have an fh_len field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_FH_LEN */
+
+/* does struct nfs_args have an fhsize field? */
+#define HAVE_FIELD_NFS_ARGS_T_FHSIZE 1
+
+/* does struct nfs_args have an gfs_flags field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_GFS_FLAGS */
+
+/* does struct nfs_args have an optstr field? */
+/* #undef HAVE_FIELD_NFS_ARGS_T_OPTSTR */
+
+/* does struct nfs_args have a proto field? */
+#define HAVE_FIELD_NFS_ARGS_T_PROTO 1
+
+/* does struct nfs_args have a socket type field? */
+#define HAVE_FIELD_NFS_ARGS_T_SOTYPE 1
+
+/* does struct nfs_args have a version field? */
+#define HAVE_FIELD_NFS_ARGS_T_VERSION 1
+
+/* does struct ifreq have field ifr_addr? */
+#define HAVE_FIELD_STRUCT_IFREQ_IFR_ADDR 1
+
+/* does struct sockaddr have field sa_len? */
+#define HAVE_FIELD_STRUCT_SOCKADDR_SA_LEN 1
+
+/* does struct autofs_args have an addr field? */
+/* #undef HAVE_FIELD_AUTOFS_ARGS_T_ADDR */
+
+/* does umntrequest have an rdevid field? */
+/* #undef HAVE_FIELD_UMNTREQUEST_RDEVID */
+
+
+/* should signal handlers be reinstalled? */
+/* #undef REINSTALL_SIGNAL_HANDLER */
+
+
+/**************************************************************************/
+/*** Everything above this line is part of the "TOP" of acconfig.h. ***/
+/**************************************************************************/
+
+
+/* Define 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 empty if the keyword does not work. */
+/* #undef const */
+
+/* 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 `int' if <sys/types.h> doesn't define. */
+/* #undef gid_t */
+
+/* Define if you support file names longer than 14 characters. */
+#define HAVE_LONG_FILE_NAMES 1
+
+/* Define if system calls automatically restart after interruption
+ by a signal. */
+#define HAVE_RESTARTABLE_SYSCALLS 1
+
+/* Define if your struct stat has st_rdev. */
+#define HAVE_ST_RDEV 1
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#define HAVE_SYS_WAIT_H 1
+
+/* Define if you have <vfork.h>. */
+/* #undef HAVE_VFORK_H */
+
+/* Define if you have the wait3 system call. */
+#define HAVE_WAIT3 1
+
+/* Define as __inline if that's what the C compiler calls it. */
+/* #undef inline */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef mode_t */
+
+/* Define if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef pid_t */
+
+/* Define if you need to in order for stat and other things to work. */
+/* #undef _POSIX_SOURCE */
+
+/* Define as the return type of signal handlers (int or void). */
+#define RETSIGTYPE void
+
+/* Define to `unsigned' if <sys/types.h> doesn't define. */
+/* #undef size_t */
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+/* #undef STAT_MACROS_BROKEN */
+
+/* Define if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Define if you can safely include both <sys/time.h> and <time.h>. */
+#define TIME_WITH_SYS_TIME 1
+
+/* Define if your <sys/time.h> declares struct tm. */
+/* #undef TM_IN_SYS_TIME */
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+/* #undef uid_t */
+
+/* Define vfork as fork if vfork does not work. */
+/* #undef vfork */
+
+/* Define if your processor stores words with the most significant
+ byte first (like Motorola and SPARC, unlike Intel and VAX). */
+/* #undef WORDS_BIGENDIAN */
+#include <machine/endian.h>
+#if BYTE_ORDER == BIG_ENDIAN
+#define WORDS_BIGENDIAN
+#endif
+
+/* Define if lex declares yytext as a char * by default, not a char[]. */
+#define YYTEXT_POINTER 1
+
+/* Turn off general debugging by default */
+/* #undef DEBUG */
+
+/* Turn off memory debugging by default */
+/* #undef DEBUG_MEM */
+
+/* Enable "amq -M" remote mount code (insecure due to IP spoofing) */
+/* #undef ENABLE_AMQ_MOUNT */
+
+/* Define package name (must be defined by configure.in) */
+#define PACKAGE "am-utils"
+
+/* Define version of package (must be defined by configure.in) */
+#define VERSION "6.0"
+
+/* We pick some parameters from our local config file */
+#include "config_local.h"
+
+/* Define name of host machine's cpu (eg. sparc) */
+/* #define HOST_CPU "i386" */
+
+/* Define name of host machine's architecture (eg. sun4) */
+/* #define HOST_ARCH "i386" */
+
+/* Define name of host machine's vendor (eg. sun) */
+#define HOST_VENDOR "unknown"
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+/* #define HOST_OS "freebsd3.0" */
+
+/* Define only name of host machine OS (eg. solaris2) */
+/* #define HOST_OS_NAME "freebsd3" */
+
+/* Define only version of host machine (eg. 2.5.1) */
+/* #define HOST_OS_VERSION "3.0" */
+
+/* Define name of host */
+/* #define HOST_NAME "dragon.nuxi.com" */
+
+/* Define user name */
+/* #define USER_NAME "obrien" */
+
+/* Define configuration date */
+/* #define CONFIG_DATE "Fri Aug 21 19:35:55 PDT 1998" */
+
+/* what type of network transport type is in use? TLI or sockets? */
+/* #undef HAVE_TRANSPORT_TYPE_TLI */
+
+/* Define to `long' if <sys/types.h> doesn't define time_t */
+/* #undef time_t */
+
+/* Define to "void *" if compiler can handle, otherwise "char *" */
+#define voidp void *
+
+/* 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
+
+/* define name of am-utils' NFS protocol header */
+#define AMU_NFS_PROTOCOL_HEADER "./conf/nfs_prot/nfs_prot_freebsd3.h"
+
+/* Define a type for the nfs_args structure */
+#define nfs_args_t struct nfs_args
+
+/* Define the field name for the filehandle within nfs_args_t */
+#define NFS_FH_FIELD fh
+
+/* Define if plain fhandle type exists */
+#define HAVE_FHANDLE 1
+
+/* Define the type of the 3rd argument ('in') to svc_getargs() */
+#define SVC_IN_ARG_TYPE caddr_t
+
+/* Define to the type of xdr procedure type */
+#define XDRPROC_T_TYPE xdrproc_t
+
+/* Define if mount table is on file, undefine if in kernel */
+/* #undef MOUNT_TABLE_ON_FILE */
+
+/* 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_mount_data in one of the standard nfs headers */
+/* #undef HAVE_STRUCT_NFS_MOUNT_DATA */
+
+/* Define if have struct nfs_gfs_mount in one of the standard nfs headers */
+/* #undef HAVE_STRUCT_NFS_GFS_MOUNT */
+
+/* Type of the 3rd argument to yp_order() */
+#define YP_ORDER_OUTORDER_TYPE int
+
+/* Type of the 6th argument to recvfrom() */
+#define RECVFROM_FROMLEN_TYPE int
+
+/* Type of the 5rd argument to authunix_create() */
+#define AUTH_CREATE_GIDLIST_TYPE gid_t
+
+/* 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 const char *
+
+/* Define a type for the pcfs_args structure */
+#define pcfs_args_t struct msdosfs_args
+
+/* 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 tmpfs_args structure */
+/* #undef tmpfs_args_t */
+
+/* Define a type for the ufs_args structure */
+#define ufs_args_t struct ufs_args
+
+/* Define a type for the efs_args structure */
+/* #undef efs_args_t */
+
+/* Define a type for the xfs_args structure */
+/* #undef xfs_args_t */
+
+/* Define a type for the lofs_args structure */
+/* #undef lofs_args_t */
+
+/* Define a type for the cdfs_args structure */
+#define cdfs_args_t struct iso_args
+
+/* Define a type for the mfs_args structure */
+/* #undef mfs_args_t */
+
+/* Define a type for the rfs_args structure */
+/* #undef rfs_args_t */
+
+/* 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 if must use NFS "noconn" option */
+/* #undef USE_UNCONNECTED_NFS_SOCKETS */
+/* define if must NOT use NFS "noconn" option */
+#define USE_CONNECTED_NFS_SOCKETS 1
+
+/* Define if you have the __seterr_reply function. */
+/* #undef HAVE___SETERR_REPLY */
+
+/* Define if you have the _seterr_reply function. */
+#define HAVE__SETERR_REPLY 1
+
+/* Define if you have the bcmp function. */
+#define HAVE_BCMP 1
+
+/* Define if you have the bcopy function. */
+#define HAVE_BCOPY 1
+
+/* Define if you have the bzero function. */
+#define HAVE_BZERO 1
+
+/* Define if you have the clnt_create function. */
+#define HAVE_CLNT_CREATE 1
+
+/* Define if you have the clnt_create_timed function. */
+/* #undef HAVE_CLNT_CREATE_TIMED */
+
+/* Define if you have the clnt_spcreateerror function. */
+#define HAVE_CLNT_SPCREATEERROR 1
+
+/* Define if you have the clnt_sperrno function. */
+#define HAVE_CLNT_SPERRNO 1
+
+/* Define if you have the cnodeid function. */
+/* #undef HAVE_CNODEID */
+
+/* Define if you have the dbm_open function. */
+#define HAVE_DBM_OPEN 1
+
+/* Define if you have the dg_mount function. */
+/* #undef HAVE_DG_MOUNT */
+
+/* Define if you have the fgets function. */
+#define HAVE_FGETS 1
+
+/* Define if you have the flock function. */
+#define HAVE_FLOCK 1
+
+/* Define if you have the fsmount function. */
+/* #undef HAVE_FSMOUNT */
+
+/* Define if you have the get_myaddress function. */
+#define HAVE_GET_MYADDRESS 1
+
+/* Define if you have the getccent function. */
+/* #undef HAVE_GETCCENT */
+
+/* Define if you have the getcwd function. */
+#define HAVE_GETCWD 1
+
+/* Define if you have the getdomainname function. */
+#define HAVE_GETDOMAINNAME 1
+
+/* Define if you have the getdtablesize function. */
+#define HAVE_GETDTABLESIZE 1
+
+/* Define if you have the gethostname function. */
+#define HAVE_GETHOSTNAME 1
+
+/* Define if you have the getmntinfo function. */
+#define HAVE_GETMNTINFO 1
+
+/* Define if you have the getmountent function. */
+/* #undef HAVE_GETMOUNTENT */
+
+/* Define if you have the getpagesize function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define if you have the getpwnam function. */
+#define HAVE_GETPWNAM 1
+
+/* Define if you have the gettimeofday function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define if you have the hasmntopt function. */
+/* #undef HAVE_HASMNTOPT */
+
+/* Define if you have the hes_init function. */
+/* #undef HAVE_HES_INIT */
+
+/* Define if you have the hesiod_init function. */
+/* #undef HAVE_HESIOD_INIT */
+
+/* Define if you have the hesiod_reload function. */
+/* #undef HAVE_HESIOD_RELOAD */
+
+/* Define if you have the hesiod_to_bind function. */
+/* #undef HAVE_HESIOD_TO_BIND */
+
+/* Define if you have the ldap_open function. */
+/* #undef HAVE_LDAP_OPEN */
+
+/* Define if you have the memcmp function. */
+#define HAVE_MEMCMP 1
+
+/* Define if you have the memcpy function. */
+#define HAVE_MEMCPY 1
+
+/* Define if you have the memmove function. */
+#define HAVE_MEMMOVE 1
+
+/* Define if you have the memset function. */
+#define HAVE_MEMSET 1
+
+/* Define if you have the mkdir function. */
+#define HAVE_MKDIR 1
+
+/* Define if you have the mkstemp function. */
+#define HAVE_MKSTEMP 1
+
+/* Define if you have the mntctl function. */
+/* #undef HAVE_MNTCTL */
+
+/* Define if you have the mount function. */
+#define HAVE_MOUNT 1
+
+/* Define if you have the mountsyscall function. */
+/* #undef HAVE_MOUNTSYSCALL */
+
+/* Define if you have the nis_domain_of function. */
+/* #undef HAVE_NIS_DOMAIN_OF */
+
+/* Define if you have the opendir function. */
+#define HAVE_OPENDIR 1
+
+/* Define if you have the plock function. */
+/* #undef HAVE_PLOCK */
+
+/* Define if you have the regcomp function. */
+#define HAVE_REGCOMP 1
+
+/* Define if you have the regexec function. */
+#define HAVE_REGEXEC 1
+
+/* Define if you have the rmdir function. */
+#define HAVE_RMDIR 1
+
+/* Define if you have the select function. */
+#define HAVE_SELECT 1
+
+/* Define if you have the seteuid function. */
+#define HAVE_SETEUID 1
+
+/* Define if you have the setresuid function. */
+/* #undef HAVE_SETRESUID */
+
+/* Define if you have the setsid function. */
+#define HAVE_SETSID 1
+
+/* Define if you have the sigaction function. */
+#define HAVE_SIGACTION 1
+
+/* Define if you have the signal function. */
+#define HAVE_SIGNAL 1
+
+/* Define if you have the socket function. */
+#define HAVE_SOCKET 1
+
+/* Define if you have the strcasecmp function. */
+#define HAVE_STRCASECMP 1
+
+/* Define if you have the strchr function. */
+#define HAVE_STRCHR 1
+
+/* Define if you have the strcspn function. */
+#define HAVE_STRCSPN 1
+
+/* Define if you have the strdup function. */
+#define HAVE_STRDUP 1
+
+/* Define if you have the strerror function. */
+#define HAVE_STRERROR 1
+
+/* Define if you have the strspn function. */
+#define HAVE_STRSPN 1
+
+/* Define if you have the strstr function. */
+#define HAVE_STRSTR 1
+
+/* Define if you have the svc_getreq function. */
+#define HAVE_SVC_GETREQ 1
+
+/* Define if you have the svc_getreqset function. */
+#define HAVE_SVC_GETREQSET 1
+
+/* Define if you have the sysfs function. */
+/* #undef HAVE_SYSFS */
+
+/* Define if you have the syslog function. */
+#define HAVE_SYSLOG 1
+
+/* Define if you have the ualarm function. */
+#define HAVE_UALARM 1
+
+/* Define if you have the umount function. */
+/* #undef HAVE_UMOUNT */
+
+/* Define if you have the uname function. */
+#define HAVE_UNAME 1
+
+/* Define if you have the unmount function. */
+#define HAVE_UNMOUNT 1
+
+/* Define if you have the uvmount function. */
+/* #undef HAVE_UVMOUNT */
+
+/* Define if you have the vfork function. */
+#define HAVE_VFORK 1
+
+/* Define if you have the vfsmount function. */
+/* #undef HAVE_VFSMOUNT */
+
+/* Define if you have the vmount function. */
+/* #undef HAVE_VMOUNT */
+
+/* Define if you have the wait3 function. */
+#define HAVE_WAIT3 1
+
+/* Define if you have the waitpid function. */
+#define HAVE_WAITPID 1
+
+/* Define if you have the xdr_attrstat function. */
+#define HAVE_XDR_ATTRSTAT 1
+
+/* Define if you have the xdr_createargs function. */
+#define HAVE_XDR_CREATEARGS 1
+
+/* Define if you have the xdr_dirlist function. */
+#define HAVE_XDR_DIRLIST 1
+
+/* Define if you have the xdr_diropargs function. */
+#define HAVE_XDR_DIROPARGS 1
+
+/* Define if you have the xdr_diropokres function. */
+#define HAVE_XDR_DIROPOKRES 1
+
+/* Define if you have the xdr_diropres function. */
+#define HAVE_XDR_DIROPRES 1
+
+/* Define if you have the xdr_dirpath function. */
+#define HAVE_XDR_DIRPATH 1
+
+/* Define if you have the xdr_entry function. */
+#define HAVE_XDR_ENTRY 1
+
+/* Define if you have the xdr_exportnode function. */
+#define HAVE_XDR_EXPORTNODE 1
+
+/* Define if you have the xdr_exports function. */
+#define HAVE_XDR_EXPORTS 1
+
+/* Define if you have the xdr_fattr function. */
+#define HAVE_XDR_FATTR 1
+
+/* Define if you have the xdr_fhandle function. */
+#define HAVE_XDR_FHANDLE 1
+
+/* Define if you have the xdr_fhstatus function. */
+#define HAVE_XDR_FHSTATUS 1
+
+/* Define if you have the xdr_filename function. */
+#define HAVE_XDR_FILENAME 1
+
+/* Define if you have the xdr_ftype function. */
+#define HAVE_XDR_FTYPE 1
+
+/* Define if you have the xdr_groupnode function. */
+#define HAVE_XDR_GROUPNODE 1
+
+/* Define if you have the xdr_groups function. */
+#define HAVE_XDR_GROUPS 1
+
+/* Define if you have the xdr_linkargs function. */
+#define HAVE_XDR_LINKARGS 1
+
+/* Define if you have the xdr_mntrequest function. */
+/* #undef HAVE_XDR_MNTREQUEST */
+
+/* Define if you have the xdr_mntres function. */
+/* #undef HAVE_XDR_MNTRES */
+
+/* Define if you have the xdr_mountbody function. */
+#define HAVE_XDR_MOUNTBODY 1
+
+/* Define if you have the xdr_mountlist function. */
+#define HAVE_XDR_MOUNTLIST 1
+
+/* Define if you have the xdr_mountres3 function. */
+/* #undef HAVE_XDR_MOUNTRES3 */
+
+/* Define if you have the xdr_name function. */
+#define HAVE_XDR_NAME 1
+
+/* Define if you have the xdr_nfs_fh function. */
+#define HAVE_XDR_NFS_FH 1
+
+/* Define if you have the xdr_nfscookie function. */
+#define HAVE_XDR_NFSCOOKIE 1
+
+/* Define if you have the xdr_nfspath function. */
+#define HAVE_XDR_NFSPATH 1
+
+/* Define if you have the xdr_nfsstat function. */
+#define HAVE_XDR_NFSSTAT 1
+
+/* Define if you have the xdr_nfstime function. */
+#define HAVE_XDR_NFSTIME 1
+
+/* Define if you have the xdr_pointer function. */
+#define HAVE_XDR_POINTER 1
+
+/* Define if you have the xdr_readargs function. */
+#define HAVE_XDR_READARGS 1
+
+/* Define if you have the xdr_readdirargs function. */
+#define HAVE_XDR_READDIRARGS 1
+
+/* Define if you have the xdr_readdirres function. */
+#define HAVE_XDR_READDIRRES 1
+
+/* Define if you have the xdr_readlinkres function. */
+#define HAVE_XDR_READLINKRES 1
+
+/* Define if you have the xdr_readokres function. */
+#define HAVE_XDR_READOKRES 1
+
+/* Define if you have the xdr_readres function. */
+#define HAVE_XDR_READRES 1
+
+/* Define if you have the xdr_renameargs function. */
+#define HAVE_XDR_RENAMEARGS 1
+
+/* Define if you have the xdr_sattr function. */
+#define HAVE_XDR_SATTR 1
+
+/* Define if you have the xdr_sattrargs function. */
+#define HAVE_XDR_SATTRARGS 1
+
+/* Define if you have the xdr_statfsokres function. */
+#define HAVE_XDR_STATFSOKRES 1
+
+/* Define if you have the xdr_statfsres function. */
+#define HAVE_XDR_STATFSRES 1
+
+/* Define if you have the xdr_symlinkargs function. */
+#define HAVE_XDR_SYMLINKARGS 1
+
+/* Define if you have the xdr_umntrequest function. */
+/* #undef HAVE_XDR_UMNTREQUEST */
+
+/* Define if you have the xdr_umntres function. */
+/* #undef HAVE_XDR_UMNTRES */
+
+/* Define if you have the xdr_writeargs function. */
+#define HAVE_XDR_WRITEARGS 1
+
+/* Define if you have the yp_all function. */
+/* #undef HAVE_YP_ALL */
+
+/* Define if you have the yp_get_default_domain function. */
+#define HAVE_YP_GET_DEFAULT_DOMAIN 1
+
+/* Define if you have the <arpa/inet.h> header file. */
+#define HAVE_ARPA_INET_H 1
+
+/* Define if you have the <arpa/nameser.h> header file. */
+#define HAVE_ARPA_NAMESER_H 1
+
+/* Define if you have the <assert.h> header file. */
+#define HAVE_ASSERT_H 1
+
+/* Define if you have the <bsd/rpc/rpc.h> header file. */
+/* #undef HAVE_BSD_RPC_RPC_H */
+
+/* Define if you have the <cdfs/cdfs_mount.h> header file. */
+/* #undef HAVE_CDFS_CDFS_MOUNT_H */
+
+/* Define if you have the <cdfs/cdfsmount.h> header file. */
+/* #undef HAVE_CDFS_CDFSMOUNT_H */
+
+/* Define if you have the <cluster.h> header file. */
+/* #undef HAVE_CLUSTER_H */
+
+/* Define if you have the <ctype.h> header file. */
+#define HAVE_CTYPE_H 1
+
+/* Define if you have the <dirent.h> header file. */
+#define HAVE_DIRENT_H 1
+
+/* Define if you have the <errno.h> header file. */
+#define HAVE_ERRNO_H 1
+
+/* Define if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define if you have the <grp.h> header file. */
+#define HAVE_GRP_H 1
+
+/* Define if you have the <hesiod.h> header file. */
+/* #undef HAVE_HESIOD_H */
+
+/* Define if you have the <hsfs/hsfs.h> header file. */
+/* #undef HAVE_HSFS_HSFS_H */
+
+/* Define if you have the <isofs/cd9660/cd9660_mount.h> header file. */
+#define HAVE_ISOFS_CD9660_CD9660_MOUNT_H 1
+
+/* Define if you have the <lber.h> header file. */
+/* #undef HAVE_LBER_H */
+
+/* Define if you have the <ldap.h> header file. */
+/* #undef HAVE_LDAP_H */
+
+/* Define if you have the <libgen.h> header file. */
+/* #undef HAVE_LIBGEN_H */
+
+/* Define if you have the <linux/auto_fs.h> header file. */
+/* #undef HAVE_LINUX_AUTO_FS_H */
+
+/* Define if you have the <linux/fs.h> header file. */
+/* #undef HAVE_LINUX_FS_H */
+
+/* Define if you have the <linux/nfs.h> header file. */
+/* #undef HAVE_LINUX_NFS_H */
+
+/* Define if you have the <linux/nfs_mount.h> header file. */
+/* #undef HAVE_LINUX_NFS_MOUNT_H */
+
+/* Define if you have the <machine/endian.h> header file. */
+#define HAVE_MACHINE_ENDIAN_H 1
+
+/* Define if you have the <malloc.h> header file. */
+/* #undef HAVE_MALLOC_H */
+
+/* Define if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define if you have the <mntent.h> header file. */
+/* #undef HAVE_MNTENT_H */
+
+/* Define if you have the <mnttab.h> header file. */
+/* #undef HAVE_MNTTAB_H */
+
+/* Define if you have the <mount.h> header file. */
+/* #undef HAVE_MOUNT_H */
+
+/* Define if you have the <msdosfs/msdosfsmount.h> header file. */
+#define HAVE_MSDOSFS_MSDOSFSMOUNT_H 1
+
+/* Define if you have the <ndbm.h> header file. */
+#define HAVE_NDBM_H 1
+
+/* Define if you have the <ndir.h> header file. */
+/* #undef HAVE_NDIR_H */
+
+/* Define if you have the <net/errno.h> header file. */
+/* #undef HAVE_NET_ERRNO_H */
+
+/* Define if you have the <net/if.h> header file. */
+#define HAVE_NET_IF_H 1
+
+/* Define if you have the <net/if_var.h> header file. */
+#define HAVE_NET_IF_VAR_H 1
+
+/* Define if you have the <net/route.h> header file. */
+#define HAVE_NET_ROUTE_H 1
+
+/* Define if you have the <netconfig.h> header file. */
+/* #undef HAVE_NETCONFIG_H */
+
+/* Define if you have the <netdb.h> header file. */
+#define HAVE_NETDB_H 1
+
+/* Define if you have the <netdir.h> header file. */
+/* #undef HAVE_NETDIR_H */
+
+/* Define if you have the <netinet/if_ether.h> header file. */
+#define HAVE_NETINET_IF_ETHER_H 1
+
+/* Define if you have the <netinet/in.h> header file. */
+#define HAVE_NETINET_IN_H 1
+
+/* Define if you have the <nfs/export.h> header file. */
+/* #undef HAVE_NFS_EXPORT_H */
+
+/* Define if you have the <nfs/mount.h> header file. */
+/* #undef HAVE_NFS_MOUNT_H */
+
+/* Define if you have the <nfs/nfs.h> header file. */
+#define HAVE_NFS_NFS_H 1
+
+/* Define if you have the <nfs/nfs_clnt.h> header file. */
+/* #undef HAVE_NFS_NFS_CLNT_H */
+
+/* Define if you have the <nfs/nfs_gfs.h> header file. */
+/* #undef HAVE_NFS_NFS_GFS_H */
+
+/* Define if you have the <nfs/nfs_mount.h> header file. */
+/* #undef HAVE_NFS_NFS_MOUNT_H */
+
+/* Define if you have the <nfs/nfsproto.h> header file. */
+#define HAVE_NFS_NFSPROTO_H 1
+
+/* Define if you have the <nfs/nfsv2.h> header file. */
+#define HAVE_NFS_NFSV2_H 1
+
+/* Define if you have the <nfs/pathconf.h> header file. */
+/* #undef HAVE_NFS_PATHCONF_H */
+
+/* Define if you have the <nfs/rpcv2.h> header file. */
+#define HAVE_NFS_RPCV2_H 1
+
+/* Define if you have the <nsswitch.h> header file. */
+/* #undef HAVE_NSSWITCH_H */
+
+/* Define if you have the <pwd.h> header file. */
+#define HAVE_PWD_H 1
+
+/* Define if you have the <regex.h> header file. */
+#define HAVE_REGEX_H 1
+
+/* Define if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define if you have the <rpc/auth_des.h> header file. */
+#define HAVE_RPC_AUTH_DES_H 1
+
+/* Define if you have the <rpc/pmap_clnt.h> header file. */
+#define HAVE_RPC_PMAP_CLNT_H 1
+
+/* Define if you have the <rpc/pmap_prot.h> header file. */
+#define HAVE_RPC_PMAP_PROT_H 1
+
+/* Define if you have the <rpc/rpc.h> header file. */
+#define HAVE_RPC_RPC_H 1
+
+/* Define if you have the <rpc/types.h> header file. */
+#define HAVE_RPC_TYPES_H 1
+
+/* Define if you have the <rpc/xdr.h> header file. */
+#define HAVE_RPC_XDR_H 1
+
+/* Define if you have the <rpcsvc/mount.h> header file. */
+#define HAVE_RPCSVC_MOUNT_H 1
+
+/* Define if you have the <rpcsvc/mountv3.h> header file. */
+/* #undef HAVE_RPCSVC_MOUNTV3_H */
+
+/* Define if you have the <rpcsvc/nfs_prot.h> header file. */
+#define HAVE_RPCSVC_NFS_PROT_H 1
+
+/* Define if you have the <rpcsvc/nis.h> header file. */
+#define HAVE_RPCSVC_NIS_H 1
+
+/* Define if you have the <rpcsvc/yp_prot.h> header file. */
+#define HAVE_RPCSVC_YP_PROT_H 1
+
+/* Define if you have the <rpcsvc/ypclnt.h> header file. */
+#define HAVE_RPCSVC_YPCLNT_H 1
+
+/* Define if you have the <setjmp.h> header file. */
+#define HAVE_SETJMP_H 1
+
+/* Define if you have the <signal.h> header file. */
+#define HAVE_SIGNAL_H 1
+
+/* Define if you have the <socketbits.h> header file. */
+/* #undef HAVE_SOCKETBITS_H */
+
+/* Define if you have the <statbuf.h> header file. */
+/* #undef HAVE_STATBUF_H */
+
+/* Define if you have the <stdarg.h> header file. */
+#define HAVE_STDARG_H 1
+
+/* Define if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define if you have the <sys/config.h> header file. */
+/* #undef HAVE_SYS_CONFIG_H */
+
+/* Define if you have the <sys/dg_mount.h> header file. */
+/* #undef HAVE_SYS_DG_MOUNT_H */
+
+/* Define if you have the <sys/dir.h> header file. */
+/* #undef HAVE_SYS_DIR_H */
+
+/* Define if you have the <sys/errno.h> header file. */
+#define HAVE_SYS_ERRNO_H 1
+
+/* Define if you have the <sys/file.h> header file. */
+#define HAVE_SYS_FILE_H 1
+
+/* Define if you have the <sys/fs/autofs.h> header file. */
+/* #undef HAVE_SYS_FS_AUTOFS_H */
+
+/* Define if you have the <sys/fs/autofs_prot.h> header file. */
+/* #undef HAVE_SYS_FS_AUTOFS_PROT_H */
+
+/* Define if you have the <sys/fs/cachefs_fs.h> header file. */
+/* #undef HAVE_SYS_FS_CACHEFS_FS_H */
+
+/* Define if you have the <sys/fs/efs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_EFS_CLNT_H */
+
+/* Define if you have the <sys/fs/nfs.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_H */
+
+/* Define if you have the <sys/fs/nfs/mount.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_MOUNT_H */
+
+/* Define if you have the <sys/fs/nfs/nfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_NFS_CLNT_H */
+
+/* Define if you have the <sys/fs/nfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_NFS_CLNT_H */
+
+/* Define if you have the <sys/fs/pc_fs.h> header file. */
+/* #undef HAVE_SYS_FS_PC_FS_H */
+
+/* Define if you have the <sys/fs/tmp.h> header file. */
+/* #undef HAVE_SYS_FS_TMP_H */
+
+/* Define if you have the <sys/fs/ufs_mount.h> header file. */
+/* #undef HAVE_SYS_FS_UFS_MOUNT_H */
+
+/* Define if you have the <sys/fs/xfs_clnt.h> header file. */
+/* #undef HAVE_SYS_FS_XFS_CLNT_H */
+
+/* Define if you have the <sys/fs_types.h> header file. */
+/* #undef HAVE_SYS_FS_TYPES_H */
+
+/* Define if you have the <sys/fsid.h> header file. */
+/* #undef HAVE_SYS_FSID_H */
+
+/* Define if you have the <sys/fstyp.h> header file. */
+/* #undef HAVE_SYS_FSTYP_H */
+
+/* Define if you have the <sys/ioctl.h> header file. */
+#define HAVE_SYS_IOCTL_H 1
+
+/* Define if you have the <sys/lock.h> header file. */
+#define HAVE_SYS_LOCK_H 1
+
+/* Define if you have the <sys/machine.h> header file. */
+/* #undef HAVE_SYS_MACHINE_H */
+
+/* Define if you have the <sys/mbuf.h> header file. */
+#define HAVE_SYS_MBUF_H 1
+
+/* Define if you have the <sys/mntctl.h> header file. */
+/* #undef HAVE_SYS_MNTCTL_H */
+
+/* Define if you have the <sys/mntent.h> header file. */
+/* #undef HAVE_SYS_MNTENT_H */
+
+/* Define if you have the <sys/mnttab.h> header file. */
+/* #undef HAVE_SYS_MNTTAB_H */
+
+/* Define if you have the <sys/mount.h> header file. */
+#define HAVE_SYS_MOUNT_H 1
+
+/* Define if you have the <sys/ndir.h> header file. */
+/* #undef HAVE_SYS_NDIR_H */
+
+/* Define if you have the <sys/netconfig.h> header file. */
+/* #undef HAVE_SYS_NETCONFIG_H */
+
+/* Define if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define if you have the <sys/pathconf.h> header file. */
+/* #undef HAVE_SYS_PATHCONF_H */
+
+/* Define if you have the <sys/proc.h> header file. */
+#define HAVE_SYS_PROC_H 1
+
+/* Define if you have the <sys/resource.h> header file. */
+#define HAVE_SYS_RESOURCE_H 1
+
+/* Define if you have the <sys/sema.h> header file. */
+/* #undef HAVE_SYS_SEMA_H */
+
+/* Define if you have the <sys/signal.h> header file. */
+#define HAVE_SYS_SIGNAL_H 1
+
+/* Define if you have the <sys/socket.h> header file. */
+#define HAVE_SYS_SOCKET_H 1
+
+/* Define if you have the <sys/sockio.h> header file. */
+#define HAVE_SYS_SOCKIO_H 1
+
+/* Define if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define if you have the <sys/statfs.h> header file. */
+/* #undef HAVE_SYS_STATFS_H */
+
+/* Define if you have the <sys/syscall.h> header file. */
+#define HAVE_SYS_SYSCALL_H 1
+
+/* Define if you have the <sys/syslimits.h> header file. */
+#define HAVE_SYS_SYSLIMITS_H 1
+
+/* Define if you have the <sys/syslog.h> header file. */
+#define HAVE_SYS_SYSLOG_H 1
+
+/* Define if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define if you have the <sys/tiuser.h> header file. */
+/* #undef HAVE_SYS_TIUSER_H */
+
+/* Define if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define if you have the <sys/ucred.h> header file. */
+#define HAVE_SYS_UCRED_H 1
+
+/* Define if you have the <sys/uio.h> header file. */
+#define HAVE_SYS_UIO_H 1
+
+/* Define if you have the <sys/utsname.h> header file. */
+#define HAVE_SYS_UTSNAME_H 1
+
+/* Define if you have the <sys/vfs.h> header file. */
+/* #undef HAVE_SYS_VFS_H */
+
+/* Define if you have the <sys/vmount.h> header file. */
+/* #undef HAVE_SYS_VMOUNT_H */
+
+/* Define if you have the <sys/vnode.h> header file. */
+#define HAVE_SYS_VNODE_H 1
+
+/* Define if you have the <syslog.h> header file. */
+#define HAVE_SYSLOG_H 1
+
+/* Define if you have the <time.h> header file. */
+#define HAVE_TIME_H 1
+
+/* Define if you have the <tiuser.h> header file. */
+/* #undef HAVE_TIUSER_H */
+
+/* Define if you have the <tmpfs/tmp.h> header file. */
+/* #undef HAVE_TMPFS_TMP_H */
+
+/* Define if you have the <ufs/ufs_mount.h> header file. */
+#define HAVE_UFS_UFS_MOUNT_H 1
+
+/* Define if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define if you have the <varargs.h> header file. */
+#define HAVE_VARARGS_H 1
+
+/* Define if you have the gdbm library (-lgdbm). */
+/* #undef HAVE_LIBGDBM */
+
+/* Define if you have the malloc library (-lmalloc). */
+/* #undef HAVE_LIBMALLOC */
+
+/* Define if you have the mapmalloc library (-lmapmalloc). */
+/* #undef HAVE_LIBMAPMALLOC */
+
+/* Define if you have the rpc library (-lrpc). */
+/* #undef HAVE_LIBRPC */
+
+/* Define if you have the rpcsvc library (-lrpcsvc). */
+#define HAVE_LIBRPCSVC 1
+
+/**************************************************************************/
+/*** Everything below this line is part of the "BOTTOM" of acconfig.h. ***/
+/**************************************************************************/
+
+/*
+ * Existence of external definitions.
+ */
+
+/* does sys_errlist[] exist? */
+#define HAVE_EXTERN_SYS_ERRLIST 1
+
+/* does optarg exist? */
+#define HAVE_EXTERN_OPTARG 1
+
+/* does clnt_sperrno() exist? */
+#define HAVE_EXTERN_CLNT_SPERRNO 1
+
+/* does free() exist? */
+#define HAVE_EXTERN_FREE 1
+
+/* does get_myaddress() exist? */
+#define HAVE_EXTERN_GET_MYADDRESS 1
+
+/* does getccent() (hpux) exist? */
+/* #undef HAVE_EXTERN_GETCCENT */
+
+/* does getdomainname() exist? */
+#define HAVE_EXTERN_GETDOMAINNAME 1
+
+/* does gethostname() exist? */
+#define HAVE_EXTERN_GETHOSTNAME 1
+
+/* does getlogin() exist? */
+#define HAVE_EXTERN_GETLOGIN 1
+
+/* does gettablesize() exist? */
+/* #undef HAVE_EXTERN_GETTABLESIZE */
+
+/* does getpagesize() exist? */
+#define HAVE_EXTERN_GETPAGESIZE 1
+
+/* does innetgr() exist? */
+/* #undef HAVE_EXTERN_INNETGR */
+
+/* does mkstemp() exist? */
+#define HAVE_EXTERN_MKSTEMP 1
+
+/* does sbrk() exist? */
+#define HAVE_EXTERN_SBRK 1
+
+/* does strcasecmp() exist? */
+#define HAVE_EXTERN_STRCASECMP 1
+
+/* does strdup() exist? */
+#define HAVE_EXTERN_STRDUP 1
+
+/* does strstr() exist? */
+#define HAVE_EXTERN_STRSTR 1
+
+/* does usleep() exist? */
+#define HAVE_EXTERN_USLEEP 1
+
+/* does wait3() exist? */
+#define HAVE_EXTERN_WAIT3 1
+
+/* does xdr_opaque_auth() exist? */
+#define HAVE_EXTERN_XDR_OPAQUE_AUTH 1
+
+/****************************************************************************/
+/*** 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..84663f0
--- /dev/null
+++ b/usr.sbin/amd/include/newvers.sh
@@ -0,0 +1,33 @@
+# $NetBSD: mkconf,v 1.1.1.1 1997/07/24 21:20:12 christos Exp $
+# $Id: newvers.sh,v 1.1 1998/08/27 08:09:41 obrien Exp $
+# mkconf
+# Generate local configuration parameters for amd
+#
+cat << __EOF
+
+/* Define name of host machine's cpu (eg. sparc) */
+/* #define HOST_CPU "`uname -p`" */
+#define HOST_CPU "`uname -m`"
+
+/* Define name of host machine's architecture (eg. sun4) */
+#define HOST_ARCH "`uname -m`"
+
+/* Define name and version of host machine (eg. solaris2.5.1) */
+#define HOST_OS "`uname -s | tr '[A-Z]' '[a-z]'``uname -r`"
+
+/* Define only name of host machine OS (eg. solaris2) */
+#define HOST_OS_NAME "`uname -s | 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`"
+
+/* 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..d2f2699
--- /dev/null
+++ b/usr.sbin/amd/libamu/Makefile
@@ -0,0 +1,34 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.1 1998/08/27 08:09:41 obrien Exp $
+
+.PATH: ${.CURDIR}/../../../contrib/amd/libamu \
+ ${.CURDIR}/../../../contrib/amd/conf/transp \
+ ${.CURDIR}/../../../contrib/amd/conf/mtab \
+ ${.CURDIR}/../../../contrib/amd/conf/umount
+
+LIB= amu
+
+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
+
+CFLAGS+= -I${.CURDIR}/../../../contrib/amd/libamu
+
+NOMAN= noman
+
+# Generated at compile time (replaces supplied xdr_func.c)
+SRCS+= nfs_prot_x.c
+CLEANFILES+= nfs_prot_x.c
+
+nfs_prot_x.c: ${NFS_PROT_X}
+ ${RPCCOM} -c -DWANT_NFS3 ${NFS_PROT_X} -o ${.TARGET}
+
+install:
+
+.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..3d12886
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/Makefile
@@ -0,0 +1,16 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id$
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/mk-amd-map
+
+PROG= mk-amd-map
+MAN8= mk-amd-map.8
+
+SRCS= mk-amd-map.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/pawd/Makefile b/usr.sbin/amd/pawd/Makefile
new file mode 100644
index 0000000..c696e25
--- /dev/null
+++ b/usr.sbin/amd/pawd/Makefile
@@ -0,0 +1,23 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.1 1998/08/27 08:09:41 obrien Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/amq
+
+BINDIR= /usr/bin
+
+PROG= pawd
+MAN1= pawd.1
+
+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..7665ca9
--- /dev/null
+++ b/usr.sbin/amd/scripts/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.15 1998/08/30 19:59:49 gpalmer Exp $
+
+.PATH: ${.CURDIR}/../../../contrib/amd/scripts
+
+MAN5= 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..e29cf9f
--- /dev/null
+++ b/usr.sbin/amd/wire-test/Makefile
@@ -0,0 +1,19 @@
+# ex:ts=8
+#
+# Makefile for amd
+# This file is under a "BSD" copyright (c) by David O'Brien 1998
+#
+# $Id: Makefile,v 1.1 1998/08/27 08:09:42 obrien Exp $
+#
+
+.PATH: ${.CURDIR}/../../../contrib/amd/wire-test
+
+PROG= wire-test
+MAN8= wire-test.8
+
+SRCS= wire-test.c
+
+DPADD+= ${LIBAMU}
+LDADD+= ${LIBAMU}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apm/Makefile b/usr.sbin/apm/Makefile
new file mode 100644
index 0000000..7f04f9b
--- /dev/null
+++ b/usr.sbin/apm/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= apm
+LINKS= ${BINDIR}/apm ${BINDIR}/zzz
+MAN8= apm.8
+MLINKS= apm.8 zzz.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apm/apm.8 b/usr.sbin/apm/apm.8
new file mode 100644
index 0000000..81b9234
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,116 @@
+.\" 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
+.\" use.
+.Dd November 1, 1994
+.Dt APM 8
+.Os
+.Sh NAME
+.Nm apm, zzz
+.Nd control the APM BIOS and display its information
+.Sh SYNOPSIS
+.Nm apm
+.Op Fl ablstzZ
+.Op Fl d Ar 1|0
+.Op Fl r Ar delta
+.Pp
+.Nm zzz
+.Sh DESCRIPTION
+.Nm Apm
+controls the Intel / Microsoft APM (Advanced Power Management) BIOS and
+displays the current status of APM on laptop PCs.
+.Nm Zzz
+suspends the system by controlling APM.
+.Pp
+The following options are available for
+.Nm apm
+(no options are available for
+.Nm zzz
+).
+If no options are specified,
+.Nm apm
+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
+Disable/enable suspending of the display separately from a normal suspend
+using the values
+.Ar 1
+or
+.Ar 0
+respectively. This argument seems to not work on many different
+laptops, including the Libretto 30CT and 50CT.
+.It Fl l
+Display the remaining battery percentage. If your laptop does not
+support this function, 255 is displayed.
+.It Fl r
+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 resume 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 apm 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 apm .
+On such systems,
+.Nm apm
+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 apm
+or
+.Nm zzz .
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apmconf 8
+.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..bf6184f
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,388 @@
+/*
+ * 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)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: apm.c,v 1.13 1998/02/20 07:17:46 hosokawa Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <machine/apm_bios.h>
+#include <time.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 */
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: apm [-ablstzZ] [-d 1|0] [-r delta]",
+ " zzz");
+ exit(1);
+}
+
+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;
+}
+
+int
+bcd2int(int bcd)
+{
+ int retval = 0;
+
+ if (bcd > 0x9999)
+ return -1;
+
+ while (bcd) {
+ retval = retval * 10 + ((bcd & 0xf000) >> 12);
+ bcd = (bcd & 0xfff) << 4;
+ }
+ return retval;
+}
+
+void
+apm_suspend(int fd)
+{
+ if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
+ err(1, NULL);
+}
+
+void
+apm_standby(int fd)
+{
+ if (ioctl(fd, APMIO_STANDBY, NULL) == -1)
+ err(1, NULL);
+}
+
+void
+apm_getinfo(int fd, apm_info_t aip)
+{
+ if (ioctl(fd, APMIO_GETINFO, aip) == -1)
+ err(1, NULL);
+}
+
+void
+print_all_info(int fd, apm_info_t aip)
+{
+ struct apm_bios_arg args;
+ int apmerr;
+
+ printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
+ printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
+ printf("AC Line status: ");
+ if (aip->ai_acline == 255)
+ printf("unknown");
+ else if (aip->ai_acline > 1)
+ printf("invalid value (0x%x)", aip->ai_acline);
+ else {
+ char messages[][10] = {"off-line", "on-line"};
+ printf("%s", messages[aip->ai_acline]);
+ }
+ printf("\n");
+ printf("Battery status: ");
+ if (aip->ai_batt_stat == 255)
+ printf("unknown");
+ else if (aip->ai_batt_stat > 3)
+ printf("invalid value (0x%x)", aip->ai_batt_stat);
+ else {
+ char messages[][10] = {"high", "low", "critical", "charging"};
+ printf("%s", messages[aip->ai_batt_stat]);
+ }
+ printf("\n");
+ printf("Remaining battery life: ");
+ if (aip->ai_batt_life == 255)
+ printf("unknown");
+ else if (aip->ai_batt_life <= 100)
+ printf("%d%%", aip->ai_batt_life);
+ else
+ printf("invalid value (0x%x)", aip->ai_batt_life);
+ printf("\n");
+ printf("Remaining battery time: ");
+ if (aip->ai_batt_time == -1)
+ printf("unknown");
+ else {
+ int t, h, m, s;
+
+ t = aip->ai_batt_time;
+ s = t % 60;
+ t /= 60;
+ m = t % 60;
+ t /= 60;
+ h = t;
+ printf("%2d:%02d:%02d", h, m, s);
+ }
+ printf("\n");
+ if (aip->ai_infoversion >= 1) {
+ printf("Number of batteries: ");
+ if (aip->ai_batteries == (u_int) -1)
+ printf("unknown\n");
+ else
+ printf("%d\n", aip->ai_batteries);
+ }
+
+ /*
+ * 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)) {
+ err(1,"Get resume timer");
+ } else {
+ apmerr = APMERR(args.eax);
+ if (apmerr == 0x0d || apmerr == 0x86)
+ printf("Resume timer: disabled\n");
+ else if (apmerr)
+ fprintf(stderr,
+ "Failed to get the resume timer: APM error0x%x\n",
+ 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);
+ tm = *localtime(&t);
+ strftime(buf, sizeof(buf), "%c", &tm);
+ printf("Resume timer: %s\n", buf);
+ }
+ }
+
+ /*
+ * 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 Capacities:\n", aip->ai_capabilities);
+ 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 :-).
+ */
+void
+apm_display(int fd, int newstate)
+{
+ if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
+ err(1, NULL);
+}
+
+
+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 sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
+ int display = 0, batt_life = 0, ac_status = 0, standby = 0;
+ int batt_time = 0, delta = 0;
+ char *cmdname;
+
+
+ if ((cmdname = strrchr(argv[0], '/')) != NULL)
+ cmdname++;
+ else
+ cmdname = argv[0];
+
+ if (strcmp(cmdname, "zzz") == 0) {
+ sleep = 1;
+ all_info = 0;
+ goto finish_option;
+ }
+ while ((c = getopt(argc, argv, "ablRr: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 = *optarg - '0';
+ if (display < 0 || display > 1) {
+ warnx("argument of option '-%c' is invalid", c);
+ usage();
+ }
+ display++;
+ 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 't':
+ batt_time = 1;
+ all_info = 0;
+ break;
+ case 'z':
+ sleep = 1;
+ all_info = 0;
+ break;
+ case 'Z':
+ standby = 1;
+ all_info = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ }
+finish_option:
+ fd = open(APMDEV, O_RDWR);
+ if (fd == -1) {
+ warn("can't open %s", APMDEV);
+ return 1;
+ }
+ if (delta)
+ apm_set_timer(fd, delta);
+ if (sleep)
+ 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);
+ 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)
+ apm_display(fd, display - 1);
+ }
+ close(fd);
+ return 0;
+}
diff --git a/usr.sbin/apmconf/Makefile b/usr.sbin/apmconf/Makefile
new file mode 100644
index 0000000..b451367
--- /dev/null
+++ b/usr.sbin/apmconf/Makefile
@@ -0,0 +1,5 @@
+
+PROG= apmconf
+MAN8= apmconf.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apmconf/apmconf.8 b/usr.sbin/apmconf/apmconf.8
new file mode 100644
index 0000000..9a13fc8
--- /dev/null
+++ b/usr.sbin/apmconf/apmconf.8
@@ -0,0 +1,58 @@
+.\" 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
+.\" use.
+.Dd November 1, 1994
+.Dt APMCONF 8
+.Os
+.Sh NAME
+.Nm apmconf
+.Nd configure APM BIOS driver
+.Sh SYNOPSIS
+.Nm apmconf
+.Op Fl e
+.Op Fl d
+.Op Fl h
+.Op Fl t
+.Sh DESCRIPTION
+.Nm Apmconf
+is used to configure the APM (Advanced Power Management) BIOS driver
+.Xr apm 4
+on laptop PCs.
+.Pp
+The following options are available.
+.Bl -tag -width indent
+.It Fl e
+Enable power management.
+.It Fl d
+Disable power management.
+.El
+.Pp
+These options enable/disable power management functions provided by
+.Xr apm 4 .
+.Bl -tag -width indent
+.It Fl h
+Enable HLT instruction in kernel context switch routine.
+.It Fl t
+Disable HLT instruction in kernel context switch routine.
+.El
+.Pp
+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 t
+is necessary to prevent the system from reducing its peak performance.
+See
+.Xr apm 4
+for details.
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8 ,
+.Xr zzz 8
+.Sh AUTHORS
+.An Tatsumi Hosokawa Aq hosokawa@jp.FreeBSD.org
diff --git a/usr.sbin/apmconf/apmconf.c b/usr.sbin/apmconf/apmconf.c
new file mode 100644
index 0000000..cdeda50
--- /dev/null
+++ b/usr.sbin/apmconf/apmconf.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 1994 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)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: apmconf.c,v 1.7 1997/09/02 06:37:48 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/apm_bios.h>
+
+#define APMDEV "/dev/apm"
+
+static int enable = 0, disable = 0;
+static int haltcpu = 0, nothaltcpu = 0;
+static int main_argc;
+static char **main_argv;
+
+static void
+parse_option(void)
+{
+ int i, option;
+ enum {OPT_NONE, OPT_ENABLE, OPT_DISABLE, OPT_HALTCPU, OPT_NOTHALTCPU} mode;
+
+ for (i = 1; i < main_argc; i++) {
+ option = 0;
+ mode = OPT_NONE;
+ if (main_argv[i][0] == '-') {
+ switch (main_argv[i][1]) {
+ case 'e':
+ mode = OPT_ENABLE;
+ option = 0;
+ break;
+ case 'd':
+ mode = OPT_DISABLE;
+ option = 0;
+ break;
+ case 'h':
+ mode = OPT_HALTCPU;
+ option = 0;
+ break;
+ case 't':
+ mode = OPT_NOTHALTCPU;
+ option = 0;
+ break;
+ default:
+ errx(1, "unknown option '%s'", main_argv[i]);
+ }
+ }
+ if (option) {
+ if (i == main_argc - 1)
+ errx(1, "option '%s' needs arguments", main_argv[i]);
+ optarg = main_argv[++i];
+ }
+
+ switch (mode) {
+ case OPT_ENABLE:
+ enable = 1;
+ break;
+ case OPT_DISABLE:
+ disable = 1;
+ break;
+ case OPT_HALTCPU:
+ haltcpu = 1;
+ break;
+ case OPT_NOTHALTCPU:
+ nothaltcpu = 1;
+ break;
+ case OPT_NONE:
+ break;
+ }
+ }
+}
+
+static void
+enable_apm(int dh)
+{
+ if (ioctl(dh, APMIO_ENABLE, NULL) == -1)
+ errx(1, "can't ioctl APMIO_ENABLE");
+}
+
+static void
+disable_apm(int dh)
+{
+ if (ioctl(dh, APMIO_DISABLE, NULL) == -1)
+ errx(1, "can't ioctl APMIO_DISABLE");
+}
+
+static void
+haltcpu_apm(int dh)
+{
+ if (ioctl(dh, APMIO_HALTCPU, NULL) == -1)
+ errx(1, "can't ioctl APMIO_HALTCPU");
+}
+
+static void
+nothaltcpu_apm(int dh)
+{
+ if (ioctl(dh, APMIO_NOTHALTCPU, NULL) == -1)
+ errx(1, "can't ioctl APMIO_NOTHALTCPU");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int dh;
+
+ main_argc = argc;
+ main_argv = argv;
+ if ((dh = open(APMDEV, O_RDWR)) == -1)
+ errx(1, "can't open '%s'", APMDEV);
+ parse_option();
+
+ /* disable operation is executed first */
+ if (disable) {
+ disable_apm(dh);
+ }
+ if (haltcpu) {
+ haltcpu_apm(dh);
+ }
+ if (nothaltcpu) {
+ nothaltcpu_apm(dh);
+ }
+ /* enable operation is executed last */
+ if (enable) {
+ enable_apm(dh);
+ }
+ return 0;
+}
diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile
new file mode 100644
index 0000000..dc57b13
--- /dev/null
+++ b/usr.sbin/arp/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.2 (Berkeley) 4/18/94
+
+PROG= arp
+MAN8= arp.8
+MAN4= arp.4
+COPTS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4
new file mode 100644
index 0000000..92caed3
--- /dev/null
+++ b/usr.sbin/arp/arp.4
@@ -0,0 +1,142 @@
+.\" 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
+.\"
+.Dd April 18, 1994
+.Dt ARP 4
+.Os BSD 4
+.Sh NAME
+.Nm arp
+.Nd Address Resolution Protocol
+.Sh SYNOPSIS
+.Em "pseudo-device ether"
+.Sh DESCRIPTION
+The Address Resolution Protocol (ARP) is a protocol used to dynamically
+map between Internet host addresses and 10Mb/s Ethernet addresses.
+It is used by all the 10Mb/s Ethernet interface drivers.
+It is not specific to Internet protocols or to 10Mb/s Ethernet,
+but this implementation currently supports only that combination.
+.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
+.Li EHOSTDOWN
+for a non-responding destination host, and
+.Li 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).
+.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.
+.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.
+.Sh SEE ALSO
+.Xr inet 4 ,
+.Xr route 4 ,
+.Xr arp 8 ,
+.Xr ifconfig 8 ,
+.Xr route 8
+.sp
+.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..23f629b
--- /dev/null
+++ b/usr.sbin/arp/arp.8
@@ -0,0 +1,146 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt ARP 8
+.Os BSD 4.3
+.Sh NAME
+.Nm arp
+.Nd address resolution display and control
+.Sh SYNOPSIS
+.Nm arp
+.Op Fl n
+.Ar hostname
+.Nm arp
+.Op Fl n
+.Fl a
+.Nm arp
+.Fl d Ar hostname
+.Op Ar proxy
+.Nm arp
+.Fl d
+.Fl a
+.Nm arp
+.Fl s Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Nm arp
+.Fl S Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Nm arp
+.Fl f Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+program 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 Ds
+.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. May be combined with the
+.Fl a
+flag to delete all entries.
+.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
+.Ar temp
+is given in the command.
+If the word
+.Ar pub
+is given, the entry will be "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 ether_addr can be given as ``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
+ether_addr will be used.
+.It Fl S Ar hostname ether_addr
+Is just like
+.Fl s
+except any existing arp entry for this host will be deleted first.
+.It Fl f
+Causes 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 -filled -offset indent -compact
+.Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Ed
+.Pp
+with argument meanings as given above.
+.El
+.Sh SEE ALSO
+.Xr inet 3 ,
+.Xr arp 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command 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..6326fb7
--- /dev/null
+++ b/usr.sbin/arp/arp.c
@@ -0,0 +1,732 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
+#endif
+static const char rcsid[] =
+ "$Id: arp.c,v 1.14 1998/01/16 17:38:51 bde Exp $";
+#endif /* not lint */
+
+/*
+ * 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 <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+void search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm));
+void print_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
+void nuke_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm);
+int delete(char *host, char *info);
+void ether_print(u_char *cp);
+void usage(void);
+int set(int argc, char **argv);
+int get(char *host);
+int file(char *name);
+void getsocket(void);
+int my_ether_aton(char *a, u_char *n);
+int rtmsg(int cmd);
+int get_ether_addr(u_long ipaddr, u_char *hwaddr);
+
+static int pid;
+static int nflag; /* no reverse dns lookups */
+static int aflag; /* do it for all entries */
+static int s = -1;
+
+/* 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(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, func = 0;
+ int rtn = 0;
+
+ pid = getpid();
+ while ((ch = getopt(argc, argv, "andfsS")) != -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 '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!func)
+ func = F_GET;
+ 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 > 5)
+ usage();
+ if (func == F_REPLACE)
+ (void) delete(argv[0], NULL);
+ rtn = set(argc, argv) ? 1 : 0;
+ break;
+ case F_DELETE:
+ if (aflag) {
+ if (argc != 0)
+ usage();
+ search(0, nuke_entry);
+ } else {
+ if (argc < 1 || argc > 2)
+ usage();
+ rtn = delete(argv[0], argv[1]);
+ }
+ break;
+ case F_FILESET:
+ if (argc != 1)
+ usage();
+ rtn = file(argv[0]);
+ break;
+ }
+
+ return(rtn);
+}
+
+/*
+ * Process a file to set standard arp entries
+ */
+int
+file(char *name)
+{
+ FILE *fp;
+ int i, retval;
+ char line[100], arg[5][50], *args[5];
+
+ if ((fp = fopen(name, "r")) == NULL)
+ errx(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) {
+ i = sscanf(line, "%s %s %s %s %s", 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);
+}
+
+void
+getsocket(void)
+{
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ err(1, "socket");
+ }
+}
+
+struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
+struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
+struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+int expire_time, flags, export_only, doing_proxy, found_entry;
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual arp entry
+ */
+int
+set(int argc, char **argv)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ u_char *ea;
+ char *host = argv[0], *eaddr = argv[1];
+
+ getsocket();
+ argc -= 2;
+ argv += 2;
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+ doing_proxy = flags = export_only = 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], "pub", 3) == 0) {
+ flags |= RTF_ANNOUNCE;
+ doing_proxy = SIN_PROXY;
+ } else if (strncmp(argv[0], "trail", 5) == 0) {
+ printf("%s: Sending trailers is no longer supported\n",
+ host);
+ }
+ argv++;
+ }
+ ea = (u_char *)LLADDR(&sdl_m);
+ if (doing_proxy && !strcmp(eaddr, "auto")) {
+ if (!get_ether_addr(sin->sin_addr.s_addr, ea)) {
+ return (1);
+ }
+ sdl_m.sdl_alen = 6;
+ } else {
+ if (my_ether_aton(eaddr, ea) == 0)
+ sdl_m.sdl_alen = 6;
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_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;
+ }
+ if (doing_proxy == 0) {
+ printf("set: can only proxy for %s\n", host);
+ return (1);
+ }
+ if (sin_m.sin_other & SIN_PROXY) {
+ printf("set: proxy entry exists for non 802 device\n");
+ return(1);
+ }
+ sin_m.sin_other = SIN_PROXY;
+ export_only = 1;
+ goto tryagain;
+ }
+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 arp entry
+ */
+int
+get(char *host)
+{
+ struct hostent *hp;
+ struct sockaddr_inarp *sin = &sin_m;
+
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host)))
+ errx(1, "%s: %s", host, hstrerror(h_errno));
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+ search(sin->sin_addr.s_addr, print_entry);
+ if (found_entry == 0) {
+ printf("%s (%s) -- no entry\n",
+ host, inet_ntoa(sin->sin_addr));
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Delete an arp entry
+ */
+int
+delete(char *host, char *info)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *sin = &sin_m;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ struct sockaddr_dl *sdl;
+
+ if (info && strncmp(info, "pro", 3) )
+ export_only = 1;
+ getsocket();
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_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 delete;
+ }
+ }
+ if (sin_m.sin_other & SIN_PROXY) {
+ fprintf(stderr, "delete: can't locate %s\n",host);
+ return (1);
+ } else {
+ sin_m.sin_other = SIN_PROXY;
+ goto tryagain;
+ }
+delete:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot locate %s\n", host);
+ return (1);
+ }
+ if (rtmsg(RTM_DELETE) == 0) {
+ printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Search the arp table and do some action on matching entries
+ */
+void
+search(u_long addr, void (*action)(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm))
+{
+ int mib[6];
+ size_t needed;
+ char *lim, *buf, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_inarp *sin;
+ struct sockaddr_dl *sdl;
+ extern int h_errno;
+
+ 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)
+ errx(1, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of routing table");
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin + 1);
+ if (addr) {
+ if (addr != sin->sin_addr.s_addr)
+ continue;
+ found_entry = 1;
+ }
+ (*action)(sdl, sin, rtm);
+ }
+}
+
+/*
+ * Display an arp entry
+ */
+void
+print_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm)
+{
+ char *host;
+ extern int h_errno;
+ struct hostent *hp;
+ int seg;
+
+ if (nflag == 0)
+ hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
+ sizeof sin->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(sin->sin_addr));
+ if (sdl->sdl_alen)
+ ether_print(LLADDR(sdl));
+ else
+ printf("(incomplete)");
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ printf(" permanent");
+ if (sin->sin_other & SIN_PROXY)
+ printf(" published (proxy only)");
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sin = (struct sockaddr_inarp *)
+ (sdl->sdl_len + (char *)sdl);
+ if (sin->sin_addr.s_addr == 0xffffffff)
+ printf(" published");
+ if (sin->sin_len != 8)
+ printf("(wierd)");
+ }
+ switch(sdl->sdl_type) {
+ case IFT_ETHER:
+ printf(" [ethernet]");
+ break;
+ case IFT_ISO88025:
+ printf(" [token-ring]");
+ break;
+ default:
+ }
+ if (sdl->sdl_rcf != NULL) {
+ printf(" rt=%x", ntohs(sdl->sdl_rcf));
+ for (seg = 0; seg < ((((ntohs(sdl->sdl_rcf) & 0x1f00) >> 8) - 2 ) / 2); seg++)
+ printf(":%x", ntohs(sdl->sdl_route[seg]));
+ }
+
+ printf("\n");
+
+}
+
+/*
+ * Nuke an arp entry
+ */
+void
+nuke_entry(struct sockaddr_dl *sdl,
+ struct sockaddr_inarp *sin, struct rt_msghdr *rtm)
+{
+ char ip[20];
+
+ snprintf(ip, sizeof(ip), "%s", inet_ntoa(sin->sin_addr));
+ delete(ip, NULL);
+}
+
+void
+ether_print(u_char *cp)
+{
+ printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+}
+
+int
+my_ether_aton(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) {
+ warnx("invalid Ethernet address '%s'", a);
+ return (1);
+ }
+ for (i=0; i<6; i++)
+ n[i] = o[i];
+ return (0);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: arp [-n] hostname",
+ " arp [-n] -a",
+ " arp -d hostname [proxy]",
+ " arp -d -a",
+ " arp -s hostname ether_addr [temp] [pub]",
+ " arp -S hostname ether_addr [temp] [pub]",
+ " arp -f filename");
+ exit(1);
+}
+
+int
+rtmsg(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:
+ 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);
+ sin_m.sin_other = 0;
+ if (doing_proxy) {
+ if (export_only)
+ sin_m.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 (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+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 (-1);
+ }
+ }
+ 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 (0);
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+int
+get_ether_addr(u_long ipaddr, u_char *hwaddr)
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ u_long ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ err(1, "socket");
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+ warnx("ioctl(SIOCGIFCONF)");
+ close(s);
+ return 0;
+ }
+
+ /*
+ * 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; ) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = ((struct sockaddr_in *)
+ &ifr->ifr_addr)->sin_addr.s_addr;
+ 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(s, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
+ IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ goto nextif;
+ /*
+ * Get its netmask and check that it's on
+ * the right subnet.
+ */
+ if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ goto nextif;
+ break;
+ }
+nextif:
+ ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ if (ifr >= ifend) {
+ close(s);
+ return 0;
+ }
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifr;
+ for (ifr = ifc.ifc_req; ifr < ifend; ) {
+ if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
+ && ifr->ifr_addr.sa_family == AF_LINK) {
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
+ close (s);
+ printf("using interface %s for proxy with address ",
+ ifp->ifr_name);
+ ether_print(hwaddr);
+ printf("\n");
+ return dla->sdl_alen;
+ }
+ ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+ return 0;
+}
diff --git a/usr.sbin/atm/Makefile b/usr.sbin/atm/Makefile
new file mode 100644
index 0000000..a32385f
--- /dev/null
+++ b/usr.sbin/atm/Makefile
@@ -0,0 +1,33 @@
+#
+#
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+#
+#
+
+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..bcc3406
--- /dev/null
+++ b/usr.sbin/atm/Makefile.inc
@@ -0,0 +1,30 @@
+#
+#
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.5 1998/07/10 16:01:58 jpt Exp $
+#
+#
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/atm/atmarpd/Makefile b/usr.sbin/atm/atmarpd/Makefile
new file mode 100644
index 0000000..013e119
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/Makefile
@@ -0,0 +1,39 @@
+#
+#
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.2 1998/09/15 19:16:33 phk Exp $
+#
+#
+
+PROG= atmarpd
+SRCS= atmarpd.c atmarp_config.c atmarp_log.c atmarp_scsp.c \
+ atmarp_subr.c atmarp_timer.c
+MAN8= atmarpd.8
+
+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..5a4b78d
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_config.c
@@ -0,0 +1,130 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarp_config.c,v 1.5 1998/08/13 20:11:11 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: configuration support
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarp_config.c,v 1.5 1998/08/13 20:11:11 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+
+/*
+ * 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;
+ Atm_addr_nsap *anp;
+
+ /*
+ * Get an ATMARP interface block
+ */
+ aip = (Atmarp_intf *)UM_ALLOC(sizeof(Atmarp_intf));
+ if (!aip)
+ atmarp_mem_err("atmarp_cfg_netif: sizeof(Atmarp_intf)");
+ UM_ZERO(aip, 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)
+ UM_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..8de5415
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_log.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarp_log.c,v 1.1 1998/07/24 17:11:51 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: logging routines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarp_log.c,v 1.1 1998/07/24 17:11:51 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.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 <libatm.h>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#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..e063821
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_scsp.c
@@ -0,0 +1,792 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarp_scsp.c,v 1.6 1998/08/13 20:11:11 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: SCSP/ATMARP interface code
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarp_scsp.c,v 1.6 1998/08/13 20:11:11 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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 <libatm.h>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+
+/*
+ * 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 = (Scsp_if_msg *)UM_ALLOC(len);
+ if (!smp) {
+ atmarp_mem_err("atmarp_scsp_cache: len");
+ }
+ UM_ZERO(smp, 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
+ */
+cache_done:
+ if (smp)
+ UM_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 = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!rsp) {
+ atmarp_mem_err("atmarp_scsp_solicit: sizeof(Scsp_if_msg)");
+ }
+ UM_ZERO(rsp, 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);
+ UM_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 i, len, rc = 0;
+ Atmarp_intf *aip = aap->aa_intf;
+ Scsp_if_msg *smp = (Scsp_if_msg *)0;
+ Scsp_atmarp_msg *sap;
+
+ /*
+ * Make sure the connection to SCSP is active
+ */
+ if (aip->ai_state == AI_STATE_NULL) {
+ return(0);
+ }
+
+ /*
+ * Get memory for the cache message
+ */
+ smp = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!smp) {
+ atmarp_mem_err("atmarp_scsp_update: sizeof(Scsp_if_msg)");
+ }
+ UM_ZERO(smp, 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);
+
+ UM_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 = (Atmarp *)UM_ALLOC(sizeof(Atmarp));
+ if (!aap)
+ atmarp_mem_err("atmarp_scsp_update_in: sizeof(Atmarp)");
+ UM_ZERO(aap, 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;
+ 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 = UM_ALLOC(msg_hdr.sh_len);
+ if (!buff)
+ atmarp_mem_err("atmarp_scsp_read: msg_hdr.sh_len");
+ UM_COPY(&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) {
+ 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
+ */
+ rc = 0;
+ break;
+ default:
+ atmarp_log(LOG_ERR, "Unexpected SCSP message received");
+ return(EOPNOTSUPP);
+ }
+
+ UM_FREE(buff);
+ return(rc);
+
+read_fail:
+ if (buff) {
+ UM_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) {
+ UM_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
+ */
+ UM_ZERO(&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;
+ UM_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);
+ UM_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) {
+ UM_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..3cc20e0
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_subr.c
@@ -0,0 +1,962 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarp_subr.c,v 1.6 1998/08/13 20:11:11 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: misc. subroutines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarp_subr.c,v 1.6 1998/08/13 20:11:11 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <sys/sockio.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 <netatm/uni/uniip_var.h>
+
+#include <libatm.h>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+
+/*
+ * 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;
+ int buf_len = sizeof(struct air_asrv_rsp);
+ 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, buf_len);
+ if (buf_len < 0)
+ 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);
+ UM_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, len, mtu, rc, sel;
+ 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
+ */
+ UM_ZERO(&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 (len <= 0) {
+ 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
+ */
+ UM_ZERO(&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 (len <= 0) {
+ 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);
+ UM_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 = (Atmarp *)UM_ALLOC(sizeof(Atmarp));
+ if (!aap) {
+ atmarp_mem_err("atmarp_if_ready: sizeof(Atmarp)");
+ }
+ UM_ZERO(aap, 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;
+ UM_COPY(&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
+ */
+ UM_FREE(netif_rsp);
+ UM_FREE(intf_rsp);
+
+ return(1);
+
+if_ready_fail:
+ if (netif_rsp)
+ UM_FREE(netif_rsp);
+ if (intf_rsp)
+ UM_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 = (Atmarp *)UM_ALLOC(sizeof(Atmarp));
+ if (!aap) {
+ errno = ENOMEM;
+ return((Atmarp *)0);
+ }
+ UM_ZERO(aap, sizeof(Atmarp));
+ aap->aa_intf = aip;
+
+ /*
+ * Copy fields from the kernel entry to the new entry
+ */
+ ipp = (struct sockaddr_in *)&cp->aap_arp_addr;
+ UM_COPY(&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;
+ UM_COPY(&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
+ */
+ UM_ZERO(&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, len, rc;
+ 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 (len < 0) {
+ 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
+ */
+ UM_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: 0x%0x\n", (u_long)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 0x%0x\n",
+ (u_long)aip);
+ fprintf(df, "\tai_next: 0x%0x\n",
+ (u_long)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 0x%0x\n", (u_long)aap);
+ fprintf(df, "\taa_next: 0x%0x\n", (u_long)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: %d (0x%x)\n", aap->aa_seq,
+ aap->aa_seq);
+ fprintf(df, "\taa_intf: 0x%0x\n", (u_long)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
+ */
+ UM_ZERO(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..f9c41cc
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarp_timer.c
@@ -0,0 +1,223 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarp_timer.c,v 1.2 1998/08/13 20:11:12 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: timer routines
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarp_timer.c,v 1.2 1998/08/13 20:11:12 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.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>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+
+/*
+ * 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
+ */
+ msg = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!msg) {
+ }
+ UM_ZERO(msg, sizeof(Scsp_if_msg));
+
+ /*
+ * 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);
+ UM_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..e8dfa4b
--- /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.
+ *
+ * @(#) $Id: atmarp_var.h,v 1.6 1998/08/13 20:11:12 johnc Exp $
+ *
+ */
+
+/*
+ * 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 __P((char *));
+
+/* atmarp_log.c */
+#if __STDC__
+extern void atmarp_log __P((const int, const char *, ...));
+#else
+extern void atmarp_log __P((int, char *, va_alist));
+#endif
+extern void atmarp_mem_err __P((char *));
+
+/* atmarp_scsp.c */
+extern int atmarp_scsp_cache __P((Atmarp_intf *, Scsp_if_msg *));
+extern int atmarp_scsp_update __P((Atmarp *, int));
+extern int atmarp_scsp_update_in __P((Atmarp_intf *,
+ Scsp_if_msg *));
+extern int atmarp_scsp_read __P((Atmarp_intf *));
+extern int atmarp_scsp_out __P((Atmarp_intf *, char *, int));
+extern int atmarp_scsp_connect __P((Atmarp_intf *));
+extern void atmarp_scsp_close __P((Atmarp_intf *));
+extern int atmarp_scsp_disconnect __P((Atmarp_intf *));
+
+/* atmarp_subr.c */
+extern Atmarp_intf *atmarp_find_intf_sock __P((int));
+extern Atmarp_intf *atmarp_find_intf_name __P((char *));
+extern void atmarp_clear_marks __P(());
+extern int atmarp_is_server __P((Atmarp_intf *));
+extern int atmarp_if_ready __P((Atmarp_intf *));
+extern Atmarp * atmarp_copy_cache_entry __P((struct air_arp_rsp *));
+extern void atmarp_get_updated_cache __P(());
+extern void atmarp_process_cache_entry __P((struct air_arp_rsp *));
+extern void print_atmarp_intf __P((FILE *, Atmarp_intf *));
+extern void print_atmarp_cache __P((FILE *, Atmarp *));
+extern void dump_atmarp_cache __P((FILE *, Atmarp_intf *));
+extern void atmarp_sigint __P((int));
+
+/* atmarp_timer.c */
+extern void atmarp_cache_timeout __P((Harp_timer *));
+extern void atmarp_perm_timeout __P((Harp_timer *));
+extern void atmarp_keepalive_timeout __P((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..7aabddf
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.8
@@ -0,0 +1,129 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $Id: atmarpd.8,v 1.1 1998/09/15 08:23:15 phk Exp $
+.\"
+.\"
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH ATMARPD 8 "1998-08-04" "HARP"
+
+.SH NAME
+atmarpd \- ATMARP/SCSP interface daemon
+.SH SYNOPSIS
+.B atmarpd
+[\fB-l\fP <log file>]
+[\fB-d\fP]
+<netif> ...
+
+.SH DESCRIPTION
+\fIAtmarpd\fP provides an interface between the ATMARP server in the
+kernel and the SCSP daemon for the Host ATM Research Platform
+(HARP) networking software.
+\fIAtmarpd\fP reads the ATMARP cache from the kernel periodically
+and passes any updated entries to \fIscspd\fP so they will be
+propagated to remote servers.
+It also accepts updated entries that remote servers have sent to
+\fIscspd\fP and, if they are
+new or more up to date than current entries, installs them
+in the kernel's ATMARP cache.
+Both \fIatmarpd\fP and \fIscspd\fP must be running before any ATMARP
+cache synchronization can take place.
+
+When \fIatmarpd\fP starts, it parses its command line and puts
+itself into the background.
+
+The command-line options are:
+.IP "\fB-l\fP <log file>" 15
+Specifies that \fIatmarpd\fP is to write log messages to the
+file named <log file> rather than to the system log.
+.IP "\fB-d\fP" 15
+Specifies that \fIatmarpd\fP is to be run in debug mode.
+In debug mode, \fIatmarpd\fP is not put into the background.
+Log messages are written to standard output instead of to
+the log file.
+.IP "<netif>" 15
+Specifies 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, \fIatmarpd\fP
+will provide an interface to \fIscspd\fP for the servers on all the
+specified interfaces.
+
+.SH SIGNAL PROCESSING
+The following signals can be used to control \fIatmarpd\fP:
+
+.IP \fBSIGINT\fP 10
+Dump debugging information to a file.
+When it receives a SIGINT signal, \fIatmarpd\fP dumps a summary of
+its control blocks to a text file (see "\fBFILES\fB").
+
+.SH FILES
+
+.IP "/tmp/atmarpd.<pid>.<seq>.out"
+Debugging information dump file name.
+\fIAtmarpd\fP writes a summary of its control blocks to this file
+when it receives a SIGINT signal.
+<pid> is the process ID of the daemon and <seq> is a sequence
+number which is incremented every time a dump is taken.
+
+.SH "SEE ALSO"
+\fIatm\fP (8);
+\fIscspd\fP (8);
+RFC 1577, \fIClassical IP and ARP over ATM\fP;
+RFC 2225, \fIClassical IP and ARP over ATM\fP;
+RFC 2334, \fIServer Cache Synchronization Protocol (SCSP)\fP;
+draft-ietf-ion-scsp-atmarpd-00.txt, \fIA Distributed ATMARP Service
+Using SCSP\fP.
+
+
+.SH BUGS
+Results are unpredictable if multiple instantiations of
+\fIatmarpd\fP are run simulatneously for a given network interface.
+
+Please report any bugs to harp-bugs@magic.net.
+
+.SH COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+
+.SH AUTHORS
+John Cavanaugh, Network Computing Services, Inc.
+.br
+Mike Spengler, Network Computing Services, Inc.
+.br
+Joe Thomas, Network Computing Services, Inc.
+.fi
+.SH ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/atmarpd/atmarpd.c b/usr.sbin/atm/atmarpd/atmarpd.c
new file mode 100644
index 0000000..72ebb52
--- /dev/null
+++ b/usr.sbin/atm/atmarpd/atmarpd.c
@@ -0,0 +1,409 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: atmarpd.c,v 1.5 1998/08/13 20:11:13 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP-ATMARP server interface: main line code
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: atmarpd.c,v 1.5 1998/08/13 20:11:13 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ttycom.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>
+#include "../scspd/scsp_msg.h"
+#include "../scspd/scsp_if.h"
+#include "../scspd/scsp_var.h"
+#include "atmarp_var.h"
+
+
+/*
+ * 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], '/'))
+ 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("/dev/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
+ */
+ rc = (int)signal(SIGINT, atmarp_sigint);
+ if (rc == -1) {
+ 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
+ *
+ */
+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..ce36b5a
--- /dev/null
+++ b/usr.sbin/atm/scspd/Makefile
@@ -0,0 +1,44 @@
+#
+#
+# ===================================
+# HARP | Host ATM Research Platform
+# ===================================
+#
+#
+# This Host ATM Research Platform ("HARP") file (the "Software") is
+# made available by Network Computing Services, Inc. ("NetworkCS")
+# "AS IS". NetworkCS does not provide maintenance, improvements or
+# support of any kind.
+#
+# NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+# INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+# AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+# SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+# In no event shall NetworkCS be responsible for any damages, including
+# but not limited to consequential damages, arising from or relating to
+# any use of the Software or related support.
+#
+# Copyright 1994-1998 Network Computing Services, Inc.
+#
+# Copies of this Software may be made, however, the above copyright
+# notice must be reproduced on all copies.
+#
+# @(#) $Id: Makefile,v 1.4 1998/09/15 19:01:46 jkh Exp $
+#
+#
+
+PROG= scspd
+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
+MAN8= scspd.8
+
+CFLAGS+= -I${.CURDIR}/../../../sys -I${.OBJDIR}
+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..b9d2b51
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_cafsm.c
@@ -0,0 +1,1448 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_cafsm.c,v 1.7 1998/08/21 18:08:23 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Cache Alignment finite state machine
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_cafsm.c,v 1.7 1998/08/21 18:08:23 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * CA FSM actions
+ */
+#define CA_ACTION_CNT 20
+int scsp_ca_act_00 __P((Scsp_dcs *, void *));
+int scsp_ca_act_01 __P((Scsp_dcs *, void *));
+int scsp_ca_act_02 __P((Scsp_dcs *, void *));
+int scsp_ca_act_03 __P((Scsp_dcs *, void *));
+int scsp_ca_act_04 __P((Scsp_dcs *, void *));
+int scsp_ca_act_05 __P((Scsp_dcs *, void *));
+int scsp_ca_act_06 __P((Scsp_dcs *, void *));
+int scsp_ca_act_07 __P((Scsp_dcs *, void *));
+int scsp_ca_act_08 __P((Scsp_dcs *, void *));
+int scsp_ca_act_09 __P((Scsp_dcs *, void *));
+int scsp_ca_act_10 __P((Scsp_dcs *, void *));
+int scsp_ca_act_11 __P((Scsp_dcs *, void *));
+int scsp_ca_act_12 __P((Scsp_dcs *, void *));
+int scsp_ca_act_13 __P((Scsp_dcs *, void *));
+int scsp_ca_act_14 __P((Scsp_dcs *, void *));
+int scsp_ca_act_15 __P((Scsp_dcs *, void *));
+int scsp_ca_act_16 __P((Scsp_dcs *, void *));
+int scsp_ca_act_17 __P((Scsp_dcs *, void *));
+int scsp_ca_act_18 __P((Scsp_dcs *, void *));
+int scsp_ca_act_19 __P((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_csu_msg *csusp;
+ 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);
+ UM_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 = (Scsp_cse *)UM_ALLOC(
+ sizeof(Scsp_cse));
+ if (!csep)
+ scsp_mem_err("scsp_ca_act_11: sizeof(Scsp_cse)");
+ UM_ZERO(csep, 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);
+ UM_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);
+ UM_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 = (Scsp_cse *)UM_ALLOC(sizeof(Scsp_cse));
+ if (!csep) {
+ scsp_mem_err("scsp_ca_act_14: sizeof(Scsp_cse)");
+ }
+ UM_ZERO(csep, 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);
+ UM_FREE(csep);
+ if (csep1) {
+ UNLINK(csep1, Scsp_cse,
+ dcsp->sd_ca_csas,
+ sc_next);
+ UM_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;
+{
+ int found, rc = 0;
+ 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 = (Scsp_csa *)UM_ALLOC(sizeof(Scsp_csa));
+ if (!csap) {
+ scsp_mem_err("scsp_ca_act_19: sizeof(Scsp_csa)");
+ }
+ UM_ZERO(csap, 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..99eca7a
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config.c
@@ -0,0 +1,1160 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_config.c,v 1.3 1998/08/13 20:11:14 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Configuration file processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_config.c,v 1.3 1998/08/13 20:11:14 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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 = (Scsp_dcs *)UM_ALLOC(sizeof(Scsp_dcs));
+ if (!dcsp) {
+ scsp_mem_err("start_dcs: sizeof(Scsp_dcs)");
+ }
+ UM_ZERO(dcsp, 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
+ */
+ UM_ZERO(&addr, sizeof(addr));
+ addr.address_format = T_ATM_ABSENT;
+ UM_ZERO(&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;
+ UM_COPY(&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);
+ UM_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 = (Scsp_server *)UM_ALLOC(sizeof(Scsp_server));
+ if (!ssp) {
+ scsp_log(LOG_ERR, "unable to allocate server entry");
+ exit(1);
+ }
+ UM_ZERO(ssp, sizeof(Scsp_server));
+ 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..7b77ae6
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_lex.c
@@ -0,0 +1,528 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_config_lex.c,v 1.3 1998/08/13 20:11:14 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Parse a configuration file into tokens
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_config_lex.c,v 1.3 1998/08/13 20:11:14 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+#include "scsp_config_parse.h"
+
+/*
+ * 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[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;
+ UM_ZERO(token_buffer, sizeof(token_buffer));
+ UM_ZERO(&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
+ */
+ UM_ZERO(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..4c02de28
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_config_parse.y
@@ -0,0 +1,410 @@
+%{
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_config_parse.y,v 1.2 1998/07/24 17:12:14 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * YACC input for configuration file processing
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_config_parse.y,v 1.2 1998/07/24 17:12:14 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+%}
+
+
+/*
+ * 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);
+ UM_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);
+ UM_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);
+ UM_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);
+ UM_FREE($2);
+ UM_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);
+ UM_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);
+ UM_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);
+}
+
+
+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..2953804
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_hfsm.c
@@ -0,0 +1,580 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_hfsm.c,v 1.4 1998/07/16 15:59:25 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * HELLO finite state machine
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_hfsm.c,v 1.4 1998/07/16 15:59:25 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.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 <libatm.h>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * HELLO FSM actions
+ */
+#define HELLO_ACTION_CNT 7
+int scsp_hello_act_00 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_01 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_02 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_03 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_04 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_05 __P((Scsp_dcs *, Scsp_msg *));
+int scsp_hello_act_06 __P((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;
+ struct in_addr addr;
+
+ /*
+ * 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, 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
+ */
+ rc = 0;
+ 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..1930990
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_if.c
@@ -0,0 +1,655 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_if.c,v 1.5 1998/08/13 20:11:14 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Interface to client server protocol
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_if.c,v 1.5 1998/08/13 20:11:14 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * SCSP client server interface FSM actions
+ */
+#define SCSP_CIFSM_ACTION_CNT 11
+int scsp_client_act_00
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_01
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_02
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_03
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_04
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_05
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_06
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_07
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_08
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_09
+ __P((Scsp_dcs *, Scsp_msg *, Scsp_if_msg *));
+int scsp_client_act_10
+ __P((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, rc;
+ 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);
+
+act_05_fail:
+ for (csep = dcsp->sd_ca_csas; csep; csep = ncsep) {
+ ncsep = csep->sc_next;
+ UNLINK(csep, Scsp_cse, dcsp->sd_ca_csas, sc_next);
+ UM_FREE(csep);
+ }
+
+ dcsp->sd_client_state = SCSP_CIFSM_NULL;
+
+ return(rc);
+}
+
+
+/*
+ * 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 = (Scsp_csa *)UM_ALLOC(sizeof(Scsp_csa));
+ if (!csap) {
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_csa)");
+ }
+ acp = (Scsp_atmarp_csa *)UM_ALLOC(sizeof(Scsp_atmarp_csa));
+ if (!acp) {
+ scsp_mem_err("scsp_client_act_07: sizeof(Scsp_atmarp_csa)");
+ }
+ UM_ZERO(csap, sizeof(Scsp_csa));
+ UM_ZERO(acp, 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 = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!csip) {
+ 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
+ */
+ UM_ZERO(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;
+ }
+ }
+
+ UM_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 = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!cuip) {
+ 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
+ */
+ UM_ZERO(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;
+ }
+ }
+
+ UM_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..dde3407
--- /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.
+ *
+ * @(#) $Id: scsp_if.h,v 1.2 1998/07/16 15:59:33 johnc Exp $
+ *
+ */
+
+/*
+ * 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..21d0a07
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_input.c
@@ -0,0 +1,1103 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_input.c,v 1.3 1998/08/13 20:11:15 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Input packet processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_input.c,v 1.3 1998/08/13 20:11:15 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+static int scsp_parse_atmarp __P((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
+ */
+ UM_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
+ */
+ UM_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
+ */
+ UM_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;
+ UM_FREE(exp);
+ }
+
+ /*
+ * Free the message structure
+ */
+ UM_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
+ */
+ UM_COPY(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, *odp;
+ 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 = (Scsp_ext *)UM_ALLOC(len);
+ if (!exp) {
+ goto ext_invalid;
+ }
+ UM_ZERO(exp, len);
+
+ /*
+ * 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) {
+ UM_COPY((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) {
+ UM_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, *odp;
+ struct scsp_ncsa *scp;
+ Scsp_csa *csap;
+
+ /*
+ * 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 = (Scsp_csa *)UM_ALLOC(len);
+ if (!csap) {
+ goto csa_invalid;
+ }
+ UM_ZERO(csap, len);
+
+ /*
+ * 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));
+ UM_COPY(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 = (Scsp_ca *)UM_ALLOC(sizeof(Scsp_ca));
+ if (!cap) {
+ goto ca_invalid;
+ }
+ UM_ZERO(cap, sizeof(Scsp_ca));
+
+ /*
+ * 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 i, len, proc_len;
+ struct scsp_atmarp_ncsa *sacp;
+ Scsp_atmarp_csa *acsp;
+
+ /*
+ * 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 = (Scsp_atmarp_csa *)UM_ALLOC(sizeof(Scsp_atmarp_csa));
+ if (!acsp) {
+ goto acs_invalid;
+ }
+ UM_ZERO(acsp, sizeof(Scsp_atmarp_csa));
+
+ /*
+ * 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)) {
+ 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;
+ UM_COPY(&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)) {
+ 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;
+ UM_COPY(&buff[proc_len], (char *)acsp->sa_ssa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather source IP address
+ */
+ if (len = sacp->sa_spln) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ UM_COPY(&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)) {
+ 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;
+ UM_COPY(&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)) {
+ 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;
+ UM_COPY(&buff[proc_len], (char *)acsp->sa_tsa.address,
+ len);
+ proc_len += len;
+ }
+
+ /*
+ * Verify/gather target IP address
+ */
+ if (len = sacp->sa_tpln) {
+ if (len != sizeof(struct in_addr))
+ goto acs_invalid;
+ if (pdu_len < proc_len + len)
+ goto acs_invalid;
+ UM_COPY(&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)
+ UM_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 = (Scsp_csu_msg *)UM_ALLOC(sizeof(Scsp_csu_msg));
+ if (!csup) {
+ goto csu_invalid;
+ }
+ UM_ZERO(csup, sizeof(Scsp_csu_msg));
+
+ /*
+ * 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 = (Scsp_hello *)UM_ALLOC(sizeof(Scsp_hello));
+ if (!hp) {
+ goto hello_invalid;
+ }
+ UM_ZERO(hp, sizeof(Scsp_hello));
+
+ /*
+ * 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 = (Scsp_id *)UM_ALLOC(sizeof(Scsp_id));
+ if (!idp) {
+ goto hello_invalid;
+ }
+ UM_ZERO(idp, sizeof(Scsp_id));
+ len = scsp_parse_id(buff,
+ hp->hello_mcp.rid.id_len,
+ idp);
+ if (len == 0) {
+ UM_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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!msg) {
+ goto ignore;
+ }
+ UM_ZERO(msg, sizeof(Scsp_msg));
+
+ /*
+ * 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..9088078
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_log.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.
+ *
+ * @(#) $Id: scsp_log.c,v 1.2 1998/07/12 20:49:36 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP logging routines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_log.c,v 1.2 1998/07/12 20:49:36 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#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
+ */
+ UM_ZERO(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
+ */
+ UM_COPY(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..bed2918
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.c
@@ -0,0 +1,611 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_msg.c,v 1.6 1998/08/21 18:08:24 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP message-handling routines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_msg.c,v 1.6 1998/08/21 18:08:24 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+/*
+ * 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);
+ UM_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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!ca_msg) {
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_msg)");
+ }
+ cap = (Scsp_ca *)UM_ALLOC(sizeof(Scsp_ca));
+ if (!cap) {
+ scsp_mem_err("scsp_send_ca: sizeof(Scsp_ca)");
+ }
+ UM_ZERO(ca_msg, sizeof(Scsp_msg));
+ UM_ZERO(cap, 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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!csus_msg) {
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_msg)");
+ }
+ csusp = (Scsp_csu_msg *)UM_ALLOC(sizeof(Scsp_csu_msg));
+ if (!csusp) {
+ scsp_mem_err("scsp_send_csus: sizeof(Scsp_csu_msg)");
+ }
+ UM_ZERO(csus_msg, sizeof(Scsp_msg));
+ UM_ZERO(csusp, 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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!csu_msg) {
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_msg)");
+ }
+ csup = (Scsp_csu_msg *)UM_ALLOC(sizeof(Scsp_csu_msg));
+ if (!csup) {
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_msg)");
+ }
+ UM_ZERO(csu_msg, sizeof(Scsp_msg));
+ UM_ZERO(csup, sizeof(Scsp_csu_msg));
+
+ /*
+ * Get memory for a CSU Req retransmission queue entry
+ */
+ rxp = (Scsp_csu_rexmt *)UM_ALLOC(sizeof(Scsp_csu_rexmt));
+ if (!rxp) {
+ scsp_mem_err("scsp_send_csu_req: sizeof(Scsp_csu_rexmt)");
+ }
+ UM_ZERO(rxp, 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);
+ }
+ UM_FREE(csu_msg);
+ UM_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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!csu_msg) {
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_msg)");
+ }
+ csup = (Scsp_csu_msg *)UM_ALLOC(sizeof(Scsp_csu_msg));
+ if (!csup) {
+ scsp_mem_err("scsp_send_csu_reply: sizeof(Scsp_csu_msg)");
+ }
+ UM_ZERO(csu_msg, sizeof(Scsp_msg));
+ UM_ZERO(csup, 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) {
+ UM_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 = (Scsp_msg *)UM_ALLOC(sizeof(Scsp_msg));
+ if (!hello) {
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_msg)");
+ }
+ UM_ZERO(hello, sizeof(Scsp_msg));
+ hp = (Scsp_hello *)UM_ALLOC(sizeof(Scsp_hello));
+ if (!hp) {
+ scsp_mem_err("scsp_send_hello: sizeof(Scsp_hello)");
+ }
+ UM_ZERO(hp, 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..3ee3bf5
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_msg.h
@@ -0,0 +1,462 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_msg.h,v 1.1 1998/07/10 15:29:02 johnc Exp $
+ *
+ */
+
+/*
+ * 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) { \
+ UM_FREE((c)->atmarp_data); \
+ } \
+ UM_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..10ee493
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_output.c
@@ -0,0 +1,934 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_output.c,v 1.2 1998/07/12 20:49:45 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Output packet processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_output.c,v 1.2 1998/07/12 20:49:45 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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);
+ UM_COPY(&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
+ */
+ UM_COPY(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 *idp, *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;
+{
+ int len;
+ 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);
+ UM_COPY((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, rc;
+ 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 */
+ UM_COPY(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 */
+ UM_COPY(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 */
+ UM_COPY(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);
+ UM_COPY(&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 */
+ UM_COPY(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 */
+ UM_COPY(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 */
+ UM_COPY(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);
+ UM_COPY(&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 *idp, *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);
+ UM_COPY(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 i, len, proc_len;
+ struct scsp_nhello *shp;
+ Scsp_id *idp;
+ 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 = (char *)UM_ALLOC(buff_len);
+ if (!buff) {
+ scsp_mem_err("scsp_format_msg: dcsp->sd_server->ss_mtu");
+ }
+ UM_ZERO(buff, buff_len);
+ *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 = (char *)UM_ALLOC(e_buff_len);
+ if (!buff) {
+ scsp_mem_err("scsp_format_msg: e_buff_len");
+ }
+ UM_ZERO(e_buff, 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) {
+ UM_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);
+ UM_COPY(e_buff, buff + len, e_len);
+ UM_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)
+ UM_FREE(buff);
+ if (e_buff)
+ UM_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);
+ UM_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..4655077
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_print.c
@@ -0,0 +1,1315 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_print.c,v 1.5 1998/08/13 20:11:16 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Print routines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_print.c,v 1.5 1998/08/13 20:11:16 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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: 0x%x\n", indent,
+ (u_long)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: 0x%x\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: 0x%x\n", indent,
+ (u_long)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.: %d (0x%x)\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.: %d\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 (0x%x):\n", indent, n,
+ (u_long)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: %d (0x%08x)\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 0x%x\n",
+ (u_long)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%x\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 0x%x\n", (u_long)pp);
+
+ /*
+ * Print the fields of the control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: 0x%x\n", indent,
+ (u_long)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 0x%x\n", (u_long)ssp);
+
+ /*
+ * Print the fields of the client control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext: 0x%x\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%x\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%x\n", indent,
+ ssp->ss_sgid);
+ fprintf(fp, "%sFamily ID: 0x%x\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: 0x%x\n", indent,
+ (u_long)csep->sc_next);
+ fprintf(fp, "%sCSA sequence no.: %d (0x%x)\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: 0x%x\n", indent,
+ (u_long)rxp->sr_next);
+ fprintf(fp, "%sDCS address: 0x%x\n", indent,
+ (u_long)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 0x%x\n", (u_long)dcsp);
+
+ /*
+ * Print the fields of the DCS control block
+ */
+ inc_indent();
+ fprintf(fp, "%sNext DCS block: 0x%x\n", indent,
+ (u_long)dcsp->sd_next);
+ fprintf(fp, "%sServer control block: 0x%x\n", indent,
+ (u_long)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%x\n", indent,
+ dcsp->sd_ca_seq);
+ fprintf(fp, "%sCA Rexmit Int: %d\n", indent,
+ dcsp->sd_ca_rexmt_int);
+ fprintf(fp, "%sCA Retransmit Msg: 0x%x\n", indent,
+ (u_long)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, "0x%x\n", (u_long) 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, "0x%x\n", dcsp->sd_crl);
+ }
+ fprintf(fp, "%sCSUS Rexmit Msg: 0x%x\n", indent,
+ (u_long)dcsp->sd_csus_rexmt_msg);
+ fprintf(fp, "%sCSA Hop count: %d\n", indent,
+ dcsp->sd_hops);
+ fprintf(fp, "%sCSAs Pending ACK: 0x%x\n", indent,
+ (u_long)dcsp->sd_csu_ack_pend);
+ fprintf(fp, "%sCSAs ACKed: 0x%x\n", indent,
+ (u_long)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, "0x%x\n", (u_long)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 0x%x\n",
+ indent,
+ (u_long)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 0x%x\n", indent,
+ (u_long)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 0x%x\n",
+ indent, (u_long)csap);
+ 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_csu_rexmt *rxp;
+ Scsp_pending *pp;
+ FILE *df;
+ char fname[64];
+ static int dump_no = 0;
+
+ /*
+ * Build a file name
+ */
+ UM_ZERO(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..83f41cd
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_socket.c
@@ -0,0 +1,1349 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_socket.c,v 1.6 1998/08/21 18:08:24 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP socket management routines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_socket.c,v 1.6 1998/08/21 18:08:24 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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;
+
+ /*
+ * 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
+ */
+ UM_ZERO(&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;
+ UM_COPY(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;
+ }
+
+ 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
+ */
+ UM_ZERO(&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;
+ UM_COPY(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;
+ }
+
+ 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;
+ UM_COPY(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;
+ struct scsp_nhdr msg_hdr, *mhp;
+
+ /*
+ * Get a buffer to hold the entire message
+ */
+ len = ssp->ss_mtu;
+ buff = (char *)UM_ALLOC(len);
+ if (!buff) {
+ 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");
+ }
+ }
+ UM_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)
+ UM_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 = (Scsp_pending *) UM_ALLOC(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, rc;
+ 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 = (char *)UM_ALLOC(msg_hdr.sh_len);
+ if (!buff) {
+ 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)
+ UM_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);
+ }
+
+ UM_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 = (Scsp_if_msg *)UM_ALLOC(sizeof(Scsp_if_msg));
+ if (!msg) {
+ scsp_mem_err("scsp_send_cache_ind: sizeof(Scsp_if_msg)");
+ }
+ UM_ZERO(msg, sizeof(Scsp_if_msg));
+
+ /*
+ * Fill out the message
+ */
+ msg->si_type = SCSP_CACHE_IND;
+ msg->si_rc = 0;
+ 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);
+ UM_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_dcs *dcsp;
+ 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);
+ UM_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;
+ }
+
+ UM_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;
+ UM_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);
+ UM_FREE(psp);
+ if (msg)
+ UM_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..c14195b
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_subr.c
@@ -0,0 +1,1123 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_subr.c,v 1.5 1998/08/13 20:11:16 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP subroutines
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_subr.c,v 1.5 1998/08/13 20:11:16 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.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 <libatm.h>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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;
+ int buf_len = sizeof(struct air_asrv_rsp);
+ 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, buf_len);
+ if (buf_len < 0)
+ 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);
+ UM_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 = (Scsp_cse *)UM_ALLOC(sizeof(Scsp_cse));
+ if (!dupp) {
+ scsp_mem_err("scsp_dup_cse: sizeof(Scsp_cse)");
+ }
+
+ /*
+ * Copy data to the duplicate
+ */
+ UM_COPY(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 = (Scsp_csa *)UM_ALLOC(sizeof(Scsp_csa));
+ if (!dupp) {
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_csa)");
+ }
+
+ /*
+ * Copy data to the duplicate
+ */
+ UM_COPY(csap, dupp, sizeof(Scsp_csa));
+ dupp->next = (Scsp_csa *)0;
+
+ /*
+ * Copy protocol-specific data, if it's present
+ */
+ if (csap->atmarp_data) {
+ adp = (Scsp_atmarp_csa *)UM_ALLOC(sizeof(Scsp_atmarp_csa));
+ if (!adp) {
+ scsp_mem_err("scsp_dup_csa: sizeof(Scsp_atmarp_csa)");
+ }
+ UM_COPY(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 = (Scsp_csa *)UM_ALLOC(sizeof(Scsp_csa));
+ if (!csap) {
+ scsp_mem_err("scsp_cse2csas: sizeof(Scsp_csa)");
+ }
+ UM_ZERO(csap, 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 = (Scsp_cse *)UM_ALLOC(sizeof(Scsp_cse));
+ if (!csep) {
+ scsp_mem_err("scsp_atmarp2cse: sizeof(Scsp_cse)");
+ }
+ UM_ZERO(csep, 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);
+ UM_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);
+ UM_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;
+ UM_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;
+ UM_FREE(rxp);
+ }
+
+ /*
+ * Free the DCS block
+ */
+ UM_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);
+ UM_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;
+ UM_FREE(csep);
+ }
+ }
+
+ /*
+ * Free the server block
+ */
+ UM_FREE(ssp->ss_name);
+ UM_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, len, mtu, rc, sel;
+ 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;
+ struct sockaddr_in subnet_mask;
+ 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
+ */
+ UM_ZERO(&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 (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
+ */
+ UM_ZERO(&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 (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
+ */
+ UM_ZERO(&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 (len <= 0) {
+ rc = EIO;
+ goto server_info_done;
+ }
+ cfg_rsp = (struct air_cfg_rsp *)air.air_buf_addr;
+
+ /*
+ * Update the server entry
+ */
+ UM_COPY(&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)
+ UM_FREE(netif_rsp);
+ if (intf_rsp)
+ UM_FREE(intf_rsp);
+ if (cfg_rsp)
+ UM_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);
+ UM_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:
+ /*
+ * 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 = (Scsp_cse *)UM_ALLOC(sizeof(Scsp_cse));
+ if (!csep) {
+ scsp_mem_err("scsp_update_cache: sizeof(Scsp_cse)");
+ }
+ UM_ZERO(csep, 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);
+ UM_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..0ec6169d
--- /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.
+ *
+ * @(#) $Id: scsp_timer.c,v 1.2 1998/07/16 15:59:50 johnc Exp $
+ *
+ */
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * Timer processing
+ *
+ */
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scsp_timer.c,v 1.2 1998/07/16 15:59:50 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <syslog.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 <libatm.h>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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..ba383d5
--- /dev/null
+++ b/usr.sbin/atm/scspd/scsp_var.h
@@ -0,0 +1,434 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scsp_var.h,v 1.5 1998/08/13 20:11:17 johnc Exp $
+ *
+ */
+
+/*
+ * 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 __P((Scsp_dcs *, int, void *));
+
+/* scsp_config.c */
+extern int scsp_config __P((char *));
+
+/* scsp_hfsm.c */
+extern int scsp_hfsm __P((Scsp_dcs *, int, Scsp_msg *));
+
+/* scsp_if.c */
+extern int scsp_cfsm __P((Scsp_dcs *, int, Scsp_msg *,
+ Scsp_if_msg *));
+
+/* scsp_input.c */
+extern void scsp_free_msg __P((Scsp_msg *));
+extern Scsp_msg *scsp_parse_msg __P((char *, int));
+
+/* scsp_log.c */
+#if __STDC__
+extern void scsp_log __P((const int, const char *, ...));
+extern void scsp_trace __P((const char *, ...));
+#else
+extern void scsp_log __P((int, char *, va_alist));
+extern void scsp_trace __P((const char *, va_alist));
+#endif
+extern void scsp_open_trace __P(());
+extern void scsp_trace_msg __P((Scsp_dcs *, Scsp_msg *, int));
+extern void scsp_mem_err __P((char *));
+
+/* scsp_msg.c */
+extern void scsp_csus_ack __P((Scsp_dcs *, Scsp_msg *));
+extern int scsp_send_ca __P((Scsp_dcs *));
+extern int scsp_send_csus __P((Scsp_dcs *));
+extern int scsp_send_csu_req __P((Scsp_dcs *, Scsp_csa *));
+extern int scsp_send_csu_reply __P((Scsp_dcs *, Scsp_csa *));
+extern int scsp_send_hello __P((Scsp_dcs *));
+
+/* scsp_output.c */
+extern int scsp_format_msg __P((Scsp_dcs *, Scsp_msg *, char **));
+extern int scsp_send_msg __P((Scsp_dcs *, Scsp_msg *));
+
+/* scsp_print.c */
+extern char *format_hfsm_state __P((int));
+extern char *format_hfsm_event __P((int));
+extern char *format_cafsm_state __P((int));
+extern char *format_cafsm_event __P((int));
+extern char *format_cifsm_state __P((int));
+extern char *format_cifsm_event __P((int));
+extern void print_scsp_cse __P((FILE *, Scsp_cse *));
+extern void print_scsp_msg __P((FILE *, Scsp_msg *));
+extern void print_scsp_if_msg __P((FILE *, Scsp_if_msg *));
+extern void print_scsp_pending __P((FILE *, Scsp_pending *));
+extern void print_scsp_server __P((FILE *, Scsp_server *));
+extern void print_scsp_dcs __P((FILE *, Scsp_dcs *));
+extern void print_scsp_dump __P(());
+
+/* scsp_socket.c */
+extern Scsp_dcs * scsp_find_dcs __P((int));
+extern Scsp_server * scsp_find_server __P((int));
+extern int scsp_dcs_connect __P((Scsp_dcs *));
+extern int scsp_dcs_listen __P((Scsp_server *));
+extern Scsp_dcs * scsp_dcs_accept __P((Scsp_server *));
+extern int scsp_dcs_read __P((Scsp_dcs *));
+extern int scsp_server_listen __P(());
+extern int scsp_server_accept __P((int));
+extern Scsp_if_msg * scsp_if_sock_read __P((int));
+extern int scsp_server_read __P((Scsp_server *));
+extern int scsp_pending_read __P((Scsp_pending *));
+
+/* scsp_subr.c */
+extern int scsp_hash __P((Scsp_ckey *));
+extern int scsp_cmp_id __P((Scsp_id *, Scsp_id *));
+extern int scsp_cmp_key __P((Scsp_ckey *, Scsp_ckey *));
+extern int scsp_is_atmarp_server __P((char *));
+extern Scsp_cse * scsp_dup_cse __P((Scsp_cse *));
+extern Scsp_csa * scsp_dup_csa __P((Scsp_csa *));
+extern Scsp_csa * scsp_cse2csas __P((Scsp_cse *));
+extern void scsp_dcs_cleanup __P((Scsp_dcs *));
+extern void scsp_dcs_delete __P((Scsp_dcs *));
+extern void scsp_server_shutdown __P((Scsp_server *));
+extern void scsp_server_delete __P((Scsp_server *));
+extern int scsp_get_server_info __P((Scsp_server *));
+extern void scsp_process_ca __P((Scsp_dcs *, Scsp_ca *));
+extern int scsp_propagate_csa __P(( Scsp_dcs *,
+ Scsp_csa *));
+extern void scsp_update_cache __P(( Scsp_dcs *,
+ Scsp_csa *));
+extern void scsp_reconfigure __P(());
+
+/* scsp_timer.c */
+extern void scsp_open_timeout __P((Harp_timer *));
+extern void scsp_hello_timeout __P((Harp_timer *));
+extern void scsp_hello_rcv_timeout __P((Harp_timer *));
+extern void scsp_ca_retran_timeout __P((Harp_timer *));
+extern void scsp_csus_retran_timeout __P((Harp_timer *));
+extern void scsp_csu_req_retran_timeout __P((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..1a9b0ab
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.8
@@ -0,0 +1,443 @@
+.\"
+.\" ===================================
+.\" HARP | Host ATM Research Platform
+.\" ===================================
+.\"
+.\"
+.\" This Host ATM Research Platform ("HARP") file (the "Software") is
+.\" made available by Network Computing Services, Inc. ("NetworkCS")
+.\" "AS IS". NetworkCS does not provide maintenance, improvements or
+.\" support of any kind.
+.\"
+.\" NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+.\" INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+.\" AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+.\" SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+.\" In no event shall NetworkCS be responsible for any damages, including
+.\" but not limited to consequential damages, arising from or relating to
+.\" any use of the Software or related support.
+.\"
+.\" Copyright 1994-1998 Network Computing Services, Inc.
+.\"
+.\" Copies of this Software may be made, however, the above copyright
+.\" notice must be reproduced on all copies.
+.\"
+.\" @(#) $Id: scspd.1,v 1.2 1998/08/26 21:39:38 johnc Exp $
+.\"
+.\"
+.de EX \"Begin example
+.ne 5
+.if n .sp 1
+.if t .sp .5
+.nf
+.in +.5i
+..
+.de EE
+.fi
+.in -.5i
+.if n .sp 1
+.if t .sp .5
+..
+.TH SCSPD 8 "1998-08-21" "HARP"
+
+.SH NAME
+scspd \- SCSP daemon
+.SH SYNOPSIS
+.B scspd
+[\fB-f\fP <cfg-file>]
+[\fB-d\fP]
+[\fB-T\fP<options>]
+
+.SH DESCRIPTION
+\fIScspd\fP is an implementation of the Server Cache Synchronization
+Protocol (SCSP) for the Host ATM Research Platform (HARP)
+networking software.
+\fIScspd\fP 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 \fIscspd\fP only supports ATMARP.
+.PP
+By using \fIscspd\fP and \fIatmarpd\fP, 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 \fIscspd\fP and \fIatmarpd\fP must be running before any ATMARP
+cache synchronization can take place.
+.PP
+\fIScspd\fP implements SCSP as specified in RFC 2334, \fIServer Cache
+Synchronization Protocol (SCSP)\fP and
+draft-ietf-ion-scspd-atmarpd-00.txt, \fIA Distributed ATMARP Service
+using SCSP\fP.
+
+When \fIscspd\fP 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:
+
+\fBClient server\fP or \fBlocal server\fP means the server running on
+the same host as \fIscspd\fP whose cache is to be synchronized with that
+of one or more remote servers.
+When the word \fBserver\fP is used alone, it means "client server".
+
+\fBRemote server\fP means a server running on some host other than
+the one where \fIscspd\fP is running.
+
+\fBDirectly Connected Server\fP (DCS) means a remote server that
+\fIscspd\fP communicates with directly.
+The remote server will also be running an implementation of SCSP.
+
+\fBCache Alignment\fP (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:
+.IP "\fB-f\fP <cfg-file>" 15
+Specifies the name of the configuration file.
+If this option is not specified, \fIscspd\fP looks for the
+file /etc/scspd.conf.
+.IP "\fB-d\fP" 15
+Specifies that \fIscspd\fP 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.
+.IP "\fB-T\fP<options>" 15
+Specifies that \fIscspd\fP will trace specified events and messages
+as it executes.
+The \fB-T\fP flag is followed by one or more of the following
+options:
+.in +4
+.ti -4
+\fBc\fP\ -\ trace \fIscspd\fP's CA Finite State Machine (FSM),
+.ti -4
+\fBh\fP\ -\ trace \fIscspd\fP's Hello FSM,
+.ti -4
+\fBi\fP\ -\ trace \fIscspd\fP's Client Interface FSM,
+.ti -4
+\fBC\fP\ -\ trace CA, CSUS, CSU Request, and CSU Reply messages,
+.ti -4
+\fBH\fP\ -\ trace Hello messages,
+.ti -4
+\fBI\fP\ -\ trace interface messages to and from \fIscspd\fP's
+clients.
+.in -4
+.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 \fIscspd\fP.
+RFC 2334, \fIServer Cache
+Synchronization Protocol (SCSP)\fP and
+draft-ietf-ion-scspd-atmarpd-00.txt, \fIA Distributed ATMARP Service
+using SCSP\fP
+will be valuable in understanding how to configure \fIscspd\fP.
+
+A configuration statement other than a comment is terminated by a
+semicolon.
+Some statements contain blocks, delimited by braces ("{" and "}").
+Configuration statement keywords are not case-sensitive,
+but some parameters (e.g. interface names) are.
+Configuration statments can span multiple lines.
+
+.SS Comments
+Three types of comments are allowed:
+
+\fB# comments\fP:
+any characters from "#" to the end of the line are ignored.
+
+\fBC comments\fP:
+any characters between "/*" and "*/" are ignored.
+
+\fBC++ comments\fP:
+any characters from "//" to the end of the line are ignored.
+
+.SS "Statements"
+The configuration statements recognized by \fIscspd\fP are:
+
+Server <name> {
+.in +5
+Protocol <protocol ID>;
+.br
+Netif <if_name>;
+.br
+ServerGroupID <ID>;
+.br
+FamilyID <ID>;
+.br
+DCS {
+.in +5
+ATMaddr <ATM address>;
+.br
+ID <host>;
+.br
+CAReXmitInt <int>;
+.br
+CSUSReXmitInt <int>;
+.br
+CSUReXmitInt <int>;
+.br
+CSUReXmitMax <cnt>;
+.br
+HelloDead <cnt>;
+.br
+HelloInt <int>;
+.br
+Hops <cnt>;
+.in -5
+};
+.in -5
+};
+.sp
+Log {
+.in +5
+File <file name>;
+.br
+Syslog;
+.in -5
+};
+.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 "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:
+.in +5
+0x47.0005.80.ffe100.0000.f21a.0170.0020481a0170.00
+.in -5
+
+.SS "Server Statement"
+The \fBserver\fP statement specifies a client server whose cache
+to be synchronized with the caches of other servers
+running on remote hosts.
+There will be one \fBserver\fP statement in the configuration file
+for each client server whose cache is to be synchronized by \fIscspd\fP.
+The format of the \fBserver\fP statement is:
+.ti +5
+\fBServer <name> { <statements> };\fP
+
+A name must be specified on the \fBserver\fP statement, but it is
+not used by \fIscspd\fP.
+It is expected to give a brief description of the server's purpose.
+
+The \fBserver\fP statement has several sub-statements
+that specify the details of the \fIscspd\fP's configuration.
+They are:
+.IP "\fBProtocol ATMARP;\fP" 5
+The only protocol supported by the current version of \fIscspd\fP
+is ATMARP.
+The \fBprotocol\fP statement must always be specified.
+.IP "\fBNetif <intf>;\fP" 5
+The \fBnetif\fP statement specifies the name of the ATM network
+interface on which a client server is providing service.
+The \fBnetif\fP statement must always be specified.
+.IP "\fBServerGroupID <ID>;\fP" 5
+The \fBServerGroupID\fP statement specifies an identifier for the
+group of servers being synchronized by \fIscspd\fP.
+The 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
+\fBserver\fP statement.
+The \fBServerGroupID\fP statement must always be specified.
+.IP "\fBFamilyID <ID>;\fP" 5
+The \fBfamilyID\fP 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 ID is specified as a decimal number in the range 0 - 65,535.
+The family ID is currently not used by \fIscspd\fP.
+
+.SS "DCS Statement"
+The \fBDCS\fP statement is a sub-statement of the \fBserver\fP statement
+that specifies the characteristics of a Directly Connected Server (DCS).
+The \fBserver\fP statement will have one \fBDCS\fP statement for
+each DCS that \fIscspd\fP is to exchange information with.
+The \fBDCS\fP statement has a number of sub-statements that specify the
+details of the configuration for the DCS. They are:
+.IP "\fBATMaddr <ATM address>;\fP" 5
+The \fBATMaddr\fP statement specifies the ATM address of the DCS.
+The \fBATMaddr\fP statement must always be specified.
+.IP "\fBID <host>;\fP" 5
+The \fBID\fP 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 \fBID\fP statement must always be specified.
+.IP "\fBCAReXmitInt <int>;\fP" 5
+The \fBCAReXmitInt\fP 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
+CAReXmitInt seconds, the message will be retransmitted.
+The default value for \fBCAReXmitInt\fP is 3 seconds.
+.IP "\fBCSUSReXmitInt <int>;\fP" 5
+The \fBCSUSReXmitInt\fP 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 CSUSReXmitInt seconds will be requested
+again by another CSUS message.
+The default value for \fBCSUSReXmitInt\fP is 3 seconds.
+Be careful not to confuse \fBCSUSReXmitInt\fP and \fBCSUReXmitInt\fP.
+.IP "\fBCSUReXmitInt <int>;\fP" 5
+The \fBCSUReXmitInt\fP 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 CSUReXmitInt seconds will
+be retransmitted.
+The default value for \fBCSUReXmitInt\fP is 2 seconds.
+Be careful not to confuse \fBCSUReXmitInt\fP and \fBCSUSReXmitInt\fP.
+.IP "\fBCSUReXmitMax <cnt>;\fP" 5
+The \fBCSUReXmitMax\fP 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 \fBCSUReXmitMax\fP is 5.
+.IP "\fBHelloDead <cnt>;\fP" 5
+The \fBHelloDead\fP statement specifies the Hello Dead Factor that
+will be sent to the DCS in Hello messages.
+A "DCS down" condition will be detected when nothing is received from
+a DCS in HelloDead * HelloInt seconds.
+The default value for \fBHelloDead\fP is 3.
+.IP "\fBHelloInt <int>;\fP" 5
+The \fBHelloInt\fP statement specifies the Hello Interval that
+will be sent to the DCS in Hello messages.
+The default value for \fBHelloInt\fP is 3 seconds.
+.IP "\fBHops <cnt>;\fP" 5
+The \fBHops\fP 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 \fBHops\fP is 3.
+
+.SS "Log Statement"
+The \fBlog\fP statement specifies how \fIscspd\fP is to log
+information about its operation.
+\fIScspd\fP can write log information to a file, to the system log,
+or both.
+.IP "\fBFile <file name>;\fP" 5
+The \fBfile\fP statement specifies that \fIscspd\fP 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.
+.IP "\fBSyslog;\fP" 5
+The \fBsyslog\fP statement specifies that \fIscspd\fP is to write
+its log messages to the syslog facility.
+\fIScspd\fP writes its messages to syslog with a facility code
+of LOG_DAEMON.
+
+.in -5
+If no \fBlog\fP statement is specified, \fIscspd\fP writes log
+messages to the system log.
+If both \fBfile\fP and \fBsyslog\fP are specified, \fIscspd\fP will
+write log messages to both the named file and the system log.
+
+.SS Examples
+
+An example of a simple configuration file for \fIscspd\fP might be:
+.in +5
+server atmarp_ni0 {
+.in +5
+protocol ATMARP;
+.br
+netif ni0;
+.br
+ServerGroupID 23;
+.br
+DCS {
+.in +5
+.br
+ID 10.1.1.2;
+.br
+ATMaddr 0x47.0005.80.ffdc00.0000.0002.0001.002048061de7.00;
+.br
+hops 2;
+.in -5
+};
+.in -5
+};
+.in -5
+
+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 \fIscspd\fP:
+
+.IP \fBSIGHUP\fP 10
+Reread the configuration file and restart \fIscspd\fP.
+
+.IP \fBSIGINT\fP 10
+Dump debugging information to a file.
+When it receives a SIGINT signal, \fIscspd\fP dumps a summary of
+its control blocks to a text file (see "\fBFILES\fP").
+
+.SH FILES
+
+.IP "/etc/scspd.conf"
+\fIScspd\fP default configuration file name.
+A different file name can be specified with the \fB-f\fP command-line
+option.
+
+.IP "/tmp/scspd.<pid>.<seq>.out"
+Debugging information dump file name.
+\fIScspd\fP writes a summary of its control blocks to this file
+when it receives a SIGINT signal.
+<pid> is the process ID of the daemon and <seq> is a sequence
+number which is incremented every time a dump is taken.
+
+.IP "/tmp/scspd.<pid>.trace"
+Trace file.
+\fIScspd\fP writes trace information to this file if the \fB-T\fP
+option is specified on the command line.
+
+.SH "SEE ALSO"
+\fIatm\fP (8);
+\fIatmarpd\fP (8);
+RFC 2334, \fIServer Cache Synchronization Protocol (SCSP)\fP;
+draft-ietf-ion-scsp-atmarpd-00.txt, \fIA Distributed ATMARP Service
+Using SCSP\fP.
+
+
+.SH BUGS
+If \fIscspd\fP terminates and is restarted, there will be a period of
+instability while previously-synchronized cache entries time out and are
+refreshed.
+
+Please report any bugs to harp-bugs@magic.net.
+
+.SH COPYRIGHT
+Copyright (c) 1994-1998, Network Computing Services, Inc.
+
+.SH AUTHORS
+John Cavanaugh, Network Computing Services, Inc.
+.br
+Mike Spengler, Network Computing Services, Inc.
+.br
+Joe Thomas, Network Computing Services, Inc.
+.fi
+.SH ACKNOWLEDGMENTS
+This software was developed with the support of the Defense
+Advanced Research Projects Agency (DARPA).
diff --git a/usr.sbin/atm/scspd/scspd.c b/usr.sbin/atm/scspd/scspd.c
new file mode 100644
index 0000000..74fe868
--- /dev/null
+++ b/usr.sbin/atm/scspd/scspd.c
@@ -0,0 +1,545 @@
+/*
+ *
+ * ===================================
+ * HARP | Host ATM Research Platform
+ * ===================================
+ *
+ *
+ * This Host ATM Research Platform ("HARP") file (the "Software") is
+ * made available by Network Computing Services, Inc. ("NetworkCS")
+ * "AS IS". NetworkCS does not provide maintenance, improvements or
+ * support of any kind.
+ *
+ * NETWORKCS MAKES NO WARRANTIES OR REPRESENTATIONS, EXPRESS OR IMPLIED,
+ * INCLUDING, BUT NOT LIMITED TO, IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE, AS TO ANY ELEMENT OF THE
+ * SOFTWARE OR ANY SUPPORT PROVIDED IN CONNECTION WITH THIS SOFTWARE.
+ * In no event shall NetworkCS be responsible for any damages, including
+ * but not limited to consequential damages, arising from or relating to
+ * any use of the Software or related support.
+ *
+ * Copyright 1994-1998 Network Computing Services, Inc.
+ *
+ * Copies of this Software may be made, however, the above copyright
+ * notice must be reproduced on all copies.
+ *
+ * @(#) $Id: scspd.c,v 1.6 1998/08/21 18:08:25 johnc Exp $
+ *
+ */
+
+
+/*
+ * Server Cache Synchronization Protocol (SCSP) Support
+ * ----------------------------------------------------
+ *
+ * SCSP server daemon main line code
+ *
+ */
+
+
+#ifndef lint
+static char *RCSid = "@(#) $Id: scspd.c,v 1.6 1998/08/21 18:08:25 johnc Exp $";
+#endif
+
+#include <sys/types.h>
+#include <sys/param.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <sys/ttycom.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>
+#include "scsp_msg.h"
+#include "scsp_if.h"
+#include "scsp_var.h"
+
+
+/*
+ * 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], '/'))
+ 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("/dev/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
+ */
+ rc = (int)signal(SIGHUP, scsp_sighup);
+ if (rc == -1) {
+ scsp_log(LOG_ERR, "SIGHUP signal setup failed");
+ exit(1);
+ }
+
+ rc = (int)signal(SIGINT, scsp_sigint);
+ if (rc == -1) {
+ 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
+ *
+ */
+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)) {
+ 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)) {
+ rc = scsp_server_read(ssp);
+ } else if (dcsp = scsp_find_dcs(i)) {
+ rc = scsp_dcs_read(dcsp);
+ }
+ }
+ }
+ }
+}
diff --git a/usr.sbin/bad144/Makefile b/usr.sbin/bad144/Makefile
new file mode 100644
index 0000000..457fda7
--- /dev/null
+++ b/usr.sbin/bad144/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= bad144
+SRCS= bad144.c dkcksum.c
+MAN8= bad144.8
+MLINKS= bad144.8 ../bad144.8
+MANSUBDIR=/${MACHINE_ARCH}
+.PATH: ${.CURDIR}/../../sbin/disklabel
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bad144/bad144.8 b/usr.sbin/bad144/bad144.8
new file mode 100644
index 0000000..d1418cb
--- /dev/null
+++ b/usr.sbin/bad144/bad144.8
@@ -0,0 +1,191 @@
+.\" Copyright (c) 1980, 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)bad144.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd May 13, 1995
+.Dt BAD144 8
+.Os BSD 4
+.Sh NAME
+.Nm bad144
+.Nd read/write dec standard 144 bad sector information
+.Sh SYNOPSIS
+.Nm bad144
+.Op Fl c
+.Op Fl f
+.Op Fl v
+.Ar disk
+.Oo
+.Ar sno
+.Op Ar bad ...
+.Oc
+.Nm bad144
+.Fl a
+.Op Fl c
+.Op Fl f
+.Op Fl v
+.Ar disk
+.Op Ar bad ...
+.Nm bad144
+.Op Fl s
+.Op Fl v
+.Ar disk
+.Sh DESCRIPTION
+.Nm Bad144
+can be used to inspect the information stored on a disk that is used by
+the disk drivers to implement bad sector forwarding.
+.Pp
+Available options:
+.Pp
+.Bl -tag -width Ds
+.It Fl a
+The argument list consists of new bad sectors to be added to an existing
+list.
+The new sectors are sorted into the list,
+which must have been in order.
+Replacement sectors are moved to accommodate the additions;
+the new replacement sectors are cleared.
+.It Fl c
+Forces an attempt to copy the old sector to the replacement,
+and may be useful when replacing an unreliable sector.
+.It Fl f
+For a RP06, RM03, RM05, Fujitsu Eagle,
+or
+.Tn SMD
+disk on a Massbus, the
+.Fl f
+option may be used to mark the new bad sectors as ``bad''
+by reformatting them as unusable sectors.
+This option is
+.Em required unless
+the sectors have already been marked bad,
+or the system will not be notified that it should use the replacement sector.
+This option may be used while running multiuser; it is no longer necessary
+to perform format operations while running single-user.
+.It Fl s
+The entire disk is scanned for bad blocks.
+.It Fl v
+The entire process is described as it happens in gory detail if
+.Fl v
+(verbose) is given.
+.El
+.Pp
+The format of
+the information is specified by
+.Tn DEC
+standard 144, as follows.
+The bad sector information is located in the first 5 even numbered sectors
+of the last track of the disk pack. There are five identical copies of
+the information, described by the
+.Ar dkbad
+structure.
+.Pp
+Replacement sectors are allocated starting with the first sector before
+the bad sector information and working backwards towards the beginning
+of the disk. A maximum of 126 bad sectors are supported. The position
+of the bad sector in the bad sector table determines the replacement
+sector to which it corresponds.
+The bad sectors must be listed in ascending order.
+.Pp
+The bad sector information and replacement sectors are conventionally
+only accessible through the ``c'' file system partition of the disk. If
+that partition is used for a file system, the user is responsible for
+making sure that it does not overlap the bad sector information or any
+replacement sectors.
+Thus, one track plus 126 sectors must be reserved to allow use
+of all of the possible bad sector replacements.
+.Pp
+The bad sector structure is as follows:
+.Bd -literal
+struct dkbad {
+ long bt_csn; /* cartridge serial number */
+ u_short bt_mbz; /* unused; should be 0 */
+ u_short bt_flag; /* -1 => alignment cartridge */
+ struct bt_bad {
+ u_short bt_cyl; /* bad sector cylinder number */
+ u_short bt_trksec; /* track and sector number */
+ } bt_bad[126];
+};
+.Ed
+.Pp
+Unused slots in the
+.Ar bt_bad
+array are filled with all bits set, a putatively
+illegal value.
+.Pp
+.Nm Bad144
+is invoked by giving a device name (e.g. hk0, hp1, etc.).
+With no optional arguments
+it reads the first sector of the last track
+of the corresponding disk and prints out the bad sector information.
+It issues a warning if the bad sectors are out of order.
+.Nm Bad144
+may also be invoked with a serial number for the pack and a list
+of bad sectors.
+It will write the supplied information into all copies
+of the bad-sector file, replacing any previous information.
+Note, however, that
+.Nm bad144
+does not arrange for the specified sectors to be marked bad in this case.
+This procedure should only be used to restore known bad sector information which
+was destroyed.
+.Pp
+It is no longer necessary to reboot to allow the kernel
+to reread the bad-sector table from the drive.
+.Sh SEE ALSO
+.Xr badsect 8
+.Sh BUGS
+It should be possible to format disks on-line under
+.Tn UNIX .
+.Pp
+It should be possible to mark bad sectors on drives of all type.
+.Pp
+On an 11/750,
+the standard bootstrap drivers used to boot the system do
+not understand bad sectors,
+handle
+.Tn ECC
+errors, or the special
+.Tn SSE
+(skip sector) errors of RM80-type disks.
+This means that none of these errors can occur when reading the file
+.Pa /kernel
+to boot. Sectors 0-15 of the disk drive
+must also not have any of these errors.
+.Pp
+The drivers which write a system core image on disk after a crash do not
+handle errors; thus the crash dump area must be free of errors and bad
+sectors.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.1 .
diff --git a/usr.sbin/bad144/bad144.c b/usr.sbin/bad144/bad144.c
new file mode 100644
index 0000000..ded783e
--- /dev/null
+++ b/usr.sbin/bad144/bad144.c
@@ -0,0 +1,760 @@
+/*
+ * Copyright (c) 1980, 1986, 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1986, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)bad144.c 8.2 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] =
+ "$Id: bad144.c,v 1.16 1997/12/12 17:58:31 bde Exp $";
+#endif /* not lint */
+
+/*
+ * bad144
+ *
+ * This program prints and/or initializes a bad block record for a pack,
+ * in the format used by the DEC standard 144.
+ * It can also add bad sector(s) to the record, moving the sector
+ * replacements as necessary.
+ *
+ * It is preferable to write the bad information with a standard formatter,
+ * but this program will do.
+ *
+ * RP06 sectors are marked as bad by inverting the format bit in the
+ * header; on other drives the valid-sector bit is cleared.
+ */
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define RETRIES 10 /* number of retries on reading old sectors */
+
+int fflag, add, copy, verbose, nflag, sflag;
+int dups;
+int badfile = -1; /* copy of badsector table to use, -1 if any */
+#define MAXSECSIZE 1024
+struct dkbad curbad, oldbad;
+#define DKBAD_MAGIC 0x4321
+
+daddr_t size;
+struct disklabel *dp;
+char *name;
+
+u_short dkcksum __P((struct disklabel *lp));
+
+int main __P((int argc, char *argv[]));
+void blkzero __P((int f, daddr_t sn));
+int compare __P((const void *cvb1, const void *cvb2));
+daddr_t badsn __P((struct bt_bad *bt));
+daddr_t getold __P((int f, struct dkbad *bad));
+int blkcopy __P((int f, daddr_t s1, daddr_t s2));
+void shift __P((int f, int new, int old));
+int checkold __P((struct dkbad *oldbad));
+static void usage __P((void));
+
+void
+bad_scan(argc, argv, dp, f, bstart, bend)
+ int *argc;
+ char ***argv;
+ struct disklabel *dp;
+ int f;
+ daddr_t bstart,bend;
+{
+ int curr_sec, n;
+ int spc = dp->d_secpercyl;
+ int ss = dp->d_secsize;
+ int trk = dp->d_nsectors;
+ int i;
+ char **nargv,*buf;
+ int nargc;
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ nargv = (char **)malloc(sizeof *nargv * DKBAD_MAXBAD);
+ if (!nargv)
+ errx(20,"malloc failed");
+ i = 1;
+ n = ioctl(f,DIOCSBADSCAN,&i);
+ if (n < 0)
+ warn("couldn't set disk in \"badscan\" mode");
+ nargc = *argc;
+ memcpy(nargv,*argv,nargc * sizeof nargv[0]);
+
+ buf = alloca((unsigned)(trk*ss));
+ if (buf == (char *)NULL)
+ errx(20,"alloca failed");
+
+ /* scan the entire disk a sector at a time. Because of all the
+ * clustering in the kernel, we cannot scan a track at a time,
+ * If we do, we may have to read twice over the block to find
+ * exactly which one failed, and it may not fail second time.
+ */
+ for (curr_sec = bstart; curr_sec < size; curr_sec++) {
+
+ if (verbose) {
+ if ((curr_sec % spc) == 0)
+ printf("\r%7d of %7u blocks (%3u%%)",
+ curr_sec,bend,(curr_sec*100/bend));
+ }
+
+ if (lseek(f, (off_t)ss * curr_sec, SEEK_SET) < 0)
+ err(4, "lseek");
+
+ if ((n = read(f, buf, ss)) != ss) {
+ if (verbose)
+ printf("\rBlock: %7d will be marked BAD.\n",
+ curr_sec);
+ else
+ warnx("found bad sector: %d", curr_sec);
+ sprintf(buf,"%d",curr_sec);
+ nargv[nargc++] = strdup(buf);
+ if (nargc >= DKBAD_MAXBAD)
+ errx(1, "too many bad sectors, can only handle %d per slice",
+ DKBAD_MAXBAD);
+ }
+ }
+ fprintf(stderr, "\n");
+ nargv[nargc] = 0;
+ *argc = nargc;
+ *argv = &nargv[0];
+ i = 0;
+ n = ioctl(f,DIOCSBADSCAN,&i);
+ if (n < 0)
+ warn("couldn't reset disk from \"badscan\" mode");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct bt_bad *bt;
+ daddr_t sn, bn[DKBAD_MAXBAD];
+ int i, f, nbad, new, bad, errs;
+ daddr_t bstart, bend;
+ char label[BBSIZE];
+
+ argc--, argv++;
+ while (argc > 0 && **argv == '-') {
+ (*argv)++;
+ while (**argv) {
+ switch (**argv) {
+#if vax
+ case 'f':
+ fflag++;
+ break;
+#endif
+ case 'a':
+ add++;
+ break;
+ case 'c':
+ copy++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ nflag++;
+ verbose++;
+ break;
+ case 's': /* scan partition */
+ sflag++;
+ add++;
+ break;
+ default:
+ if (**argv >= '0' && **argv <= '4') {
+ badfile = **argv - '0';
+ break;
+ }
+ usage();
+ }
+ (*argv)++;
+ }
+ argc--, argv++;
+ }
+ if (argc < 1)
+ usage();
+ if (argv[0][0] != '/')
+ (void)sprintf(label, "%sr%s%c", _PATH_DEV, argv[0],
+ 'a' + RAW_PART);
+ else
+ strcpy(label, argv[0]);
+ name = strdup(label);
+ f = open(name, !sflag && argc == 1? O_RDONLY : O_RDWR);
+ if (f < 0)
+ err(4, "%s", name);
+ if (read(f, label, sizeof(label)) < 0)
+ err(4, "read");
+ for (dp = (struct disklabel *)(label + LABELOFFSET);
+ dp < (struct disklabel *)
+ (label + sizeof(label) - sizeof(struct disklabel));
+ dp = (struct disklabel *)((char *)dp + 64))
+ if (dp->d_magic == DISKMAGIC && dp->d_magic2 == DISKMAGIC)
+ break;
+ if (dp->d_magic != DISKMAGIC || dp->d_magic2 != DISKMAGIC)
+ errx(1, "bad pack magic number (pack is unlabeled)");
+ if (dp->d_secsize > MAXSECSIZE || dp->d_secsize <= 0)
+ errx(7, "disk sector size too large/small (%u)", dp->d_secsize);
+ size = dp->d_secperunit;
+
+ /*
+ * bstart is 0 since we should always be doing partition c of a slice
+ * bend is the size of the slice, less the bad block map track
+ * and the DKBAD_MAXBAD replacement blocks
+ */
+ bstart = 0;
+ bend = size - (dp->d_nsectors + DKBAD_MAXBAD);
+
+ if (verbose) {
+ printf("cyl: %u, tracks: %u, secs: %u, "
+ "sec/cyl: %u, start: %d, end: %d\n",
+ dp->d_ncylinders, dp->d_ntracks, dp->d_nsectors,
+ dp->d_secpercyl, bstart, bend);
+ }
+
+ argc--;
+ argv++;
+
+ if (sflag)
+ bad_scan(&argc,&argv,dp,f,bstart,bend);
+
+ if (argc == 0) {
+ sn = getold(f, &oldbad);
+ printf("bad block information at sector %d in %s:\n",
+ sn, name);
+ printf("cartridge serial number: %d(10)\n", oldbad.bt_csn);
+ switch (oldbad.bt_flag) {
+
+ case (u_short)-1:
+ printf("alignment cartridge\n");
+ break;
+
+ case DKBAD_MAGIC:
+ break;
+
+ default:
+ printf("bt_flag=%x(16)?\n", oldbad.bt_flag);
+ break;
+ }
+ bt = oldbad.bt_bad;
+ for (i = 0; i < DKBAD_MAXBAD; i++) {
+ bad = (bt->bt_cyl<<16) + bt->bt_trksec;
+ if (bad < 0)
+ break;
+ printf("sn=%d, cn=%d, tn=%d, sn=%d\n", badsn(bt),
+ bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
+ bt++;
+ }
+ (void) checkold(&oldbad);
+ exit(0);
+ }
+ if (add) {
+ /*
+ * Read in the old badsector table.
+ * Verify that it makes sense, and the bad sectors
+ * are in order. Copy the old table to the new one.
+ */
+ (void) getold(f, &oldbad);
+ i = checkold(&oldbad);
+ if (verbose)
+ printf("Had %d bad sectors, adding %d\n", i, argc);
+ if (i + argc > DKBAD_MAXBAD) {
+ printf("bad144: not enough room for %d more sectors\n",
+ argc);
+ printf("limited to %d by information format\n",
+ DKBAD_MAXBAD);
+ exit(1);
+ }
+ curbad = oldbad;
+ } else {
+ curbad.bt_csn = atoi(*argv++);
+ argc--;
+ curbad.bt_mbz = 0;
+ curbad.bt_flag = DKBAD_MAGIC;
+ if (argc > DKBAD_MAXBAD) {
+ printf("bad144: too many bad sectors specified\n");
+ printf("limited to %d by information format\n",
+ DKBAD_MAXBAD);
+ exit(1);
+ }
+ i = 0;
+ }
+ errs = 0;
+ new = argc;
+ while (argc > 0) {
+ sn = atoi(*argv++);
+ argc--;
+ if (sn < 0 || sn >= bend) {
+ printf("%d: out of range [0,%d) for disk %.*s\n",
+ sn, bend, (int)sizeof(dp->d_typename),
+ dp->d_typename);
+ errs++;
+ continue;
+ }
+ bn[i] = sn;
+ curbad.bt_bad[i].bt_cyl = sn / (dp->d_nsectors*dp->d_ntracks);
+ sn %= (dp->d_nsectors*dp->d_ntracks);
+ curbad.bt_bad[i].bt_trksec =
+ ((sn/dp->d_nsectors) << 8) + (sn%dp->d_nsectors);
+ i++;
+ }
+ if (errs)
+ exit(1);
+ nbad = i;
+ while (i < DKBAD_MAXBAD) {
+ curbad.bt_bad[i].bt_trksec = DKBAD_NOTRKSEC;
+ curbad.bt_bad[i].bt_cyl = DKBAD_NOCYL;
+ i++;
+ }
+ if (add) {
+ /*
+ * Sort the new bad sectors into the list.
+ * Then shuffle the replacement sectors so that
+ * the previous bad sectors get the same replacement data.
+ */
+ qsort(curbad.bt_bad, nbad, sizeof (struct bt_bad), compare);
+ if (dups)
+ errx(3,
+ "bad sectors have been duplicated; can't add existing sectors");
+ shift(f, nbad, nbad-new);
+ }
+ if (badfile == -1)
+ i = 0;
+ else
+ i = badfile * 2;
+ for (; i < 10 && i < dp->d_nsectors; i += 2) {
+ if (lseek(f, (off_t)dp->d_secsize * (size - dp->d_nsectors + i),
+ SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("write badsect file at %u\n",
+ size - dp->d_nsectors + i);
+ if (nflag == 0 && write(f, (caddr_t)&curbad, sizeof(curbad)) !=
+ sizeof(curbad)) {
+ char msg[80];
+ (void)sprintf(msg, "write bad sector file %d", i/2);
+ warn("%s", msg);
+ }
+ if (badfile != -1)
+ break;
+ }
+#ifdef vax
+ if (nflag == 0 && fflag)
+ for (i = nbad - new; i < nbad; i++)
+ format(f, bn[i]);
+#endif
+ if (nflag == 0 && (dp->d_flags & D_BADSECT) == 0) {
+ dp->d_flags |= D_BADSECT;
+ dp->d_checksum = 0;
+ dp->d_checksum = dkcksum(dp);
+ if (ioctl(f, DIOCWDINFO, dp) < 0)
+ err(1, "can't write disklabel to enable bad sector handling");
+ }
+#ifdef DIOCSBAD
+ if (nflag == 0 && ioctl(f, DIOCSBAD, (caddr_t)&curbad) < 0)
+ warnx("can't sync bad-sector file; reboot for changes to take effect");
+#endif
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: bad144 [-f] disk [snum [bn ...]]",
+ " bad144 -a [-f] [-c] disk bn ...",
+ " bad144 -s [-v] disk");
+ exit(1);
+}
+
+daddr_t
+getold(f, bad)
+ int f;
+ struct dkbad *bad;
+{
+ register int i;
+ daddr_t sn;
+ char msg[80];
+
+ if (badfile == -1)
+ i = 0;
+ else
+ i = badfile * 2;
+ for (; i < 10 && i < dp->d_nsectors; i += 2) {
+ sn = size - dp->d_nsectors + i;
+ if (lseek(f, dp->d_secsize * (off_t)sn, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (read(f, (char *) bad, dp->d_secsize) == dp->d_secsize) {
+ if (i > 0)
+ printf("Using bad-sector file %d\n", i/2);
+ return(sn);
+ }
+ (void)sprintf(msg, "read bad sector file at sn %d", sn);
+ warn("%s", msg);
+ if (badfile != -1)
+ break;
+ }
+ errx(1, "%s: can't read bad block info", name);
+ /*NOTREACHED*/
+}
+
+int
+checkold(oldbad)
+ struct dkbad *oldbad;
+{
+ register int i;
+ register struct bt_bad *bt;
+ daddr_t sn, lsn=0;
+ int errors = 0, warned = 0;
+
+ if (oldbad->bt_flag != DKBAD_MAGIC) {
+ warnx("%s: bad flag in bad-sector table", name);
+ errors++;
+ }
+ if (oldbad->bt_mbz != 0) {
+ warnx("%s: bad magic number", name);
+ errors++;
+ }
+ bt = oldbad->bt_bad;
+ for (i = 0; i < DKBAD_MAXBAD; i++, bt++) {
+ if (bt->bt_cyl == DKBAD_NOCYL &&
+ bt->bt_trksec == DKBAD_NOTRKSEC)
+ break;
+ if ((bt->bt_cyl >= dp->d_ncylinders) ||
+ ((bt->bt_trksec >> 8) >= dp->d_ntracks) ||
+ ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) {
+ warnx(
+ "cyl/trk/sect out of range in existing entry: sn=%d, cn=%d, tn=%d, sn=%d",
+ badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec & 0xff);
+ errors++;
+ }
+ sn = (bt->bt_cyl * dp->d_ntracks +
+ (bt->bt_trksec >> 8)) *
+ dp->d_nsectors + (bt->bt_trksec & 0xff);
+ if (i > 0 && sn < lsn && !warned) {
+ warnx("bad sector file is out of order");
+ errors++;
+ warned++;
+ }
+ if (i > 0 && sn == lsn) {
+ warnx("bad sector file contains duplicates (sn %d)", sn);
+ errors++;
+ }
+ lsn = sn;
+ }
+ if (errors)
+ exit(1);
+ return (i);
+}
+
+/*
+ * Move the bad sector replacements
+ * to make room for the new bad sectors.
+ * new is the new number of bad sectors, old is the previous count.
+ */
+void
+shift(f, new, old)
+ int f, new, old;
+{
+ daddr_t repl;
+
+ /*
+ * First replacement is last sector of second-to-last track.
+ */
+ repl = size - dp->d_nsectors - 1;
+ new--; old--;
+ while (new >= 0 && new != old) {
+ if (old < 0 ||
+ compare(&curbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) {
+ /*
+ * Insert new replacement here-- copy original
+ * sector if requested and possible,
+ * otherwise write a zero block.
+ */
+ if (!copy ||
+ !blkcopy(f, badsn(&curbad.bt_bad[new]), repl - new))
+ blkzero(f, repl - new);
+ } else {
+ if (blkcopy(f, repl - old, repl - new) == 0)
+ warnx("can't copy replacement sector %d to %d",
+ repl-old, repl-new);
+ old--;
+ }
+ new--;
+ }
+}
+
+/*
+ * Copy disk sector s1 to s2.
+ */
+int
+blkcopy(f, s1, s2)
+ int f;
+ daddr_t s1, s2;
+{
+ register tries, n;
+ char *buf;
+
+ buf = alloca((unsigned)dp->d_secsize);
+ if (buf == (char *)NULL)
+ errx(20, "alloca failed");
+
+ for (tries = 0; tries < RETRIES; tries++) {
+ if (lseek(f, (off_t)dp->d_secsize * s1, SEEK_SET) < 0)
+ err(4, "lseek");
+ if ((n = read(f, buf, dp->d_secsize)) == dp->d_secsize)
+ break;
+ }
+ if (n != dp->d_secsize) {
+ if (n < 0)
+ warn("can't read sector, %d", s1);
+ else
+ warnx("can't read sector, %d", s1);
+ return(0);
+ }
+ if (lseek(f, (off_t)dp->d_secsize * s2, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("copying %d to %d\n", s1, s2);
+ if (nflag == 0 && write(f, buf, dp->d_secsize) != dp->d_secsize) {
+ warn("can't write replacement sector, %d", s2);
+ return(0);
+ }
+ return(1);
+}
+
+
+void
+blkzero(f, sn)
+ int f;
+ daddr_t sn;
+{
+ char *zbuf;
+
+ zbuf = alloca((unsigned)dp->d_secsize);
+ if (zbuf == (char *)NULL)
+ errx(20, "alloca failed");
+
+ memset(zbuf, 0, dp->d_secsize);
+
+ if (lseek(f, (off_t)dp->d_secsize * sn, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("zeroing %d\n", sn);
+ if (nflag == 0 && write(f, zbuf, dp->d_secsize) != dp->d_secsize)
+ warn("can't write replacement sector, %d", sn);
+}
+
+int
+compare(cvb1, cvb2)
+ const void *cvb1, *cvb2;
+{
+ const struct bt_bad *b1 = cvb1, *b2 = cvb2;
+
+ if (b1->bt_cyl > b2->bt_cyl)
+ return(1);
+ if (b1->bt_cyl < b2->bt_cyl)
+ return(-1);
+ if (b1->bt_trksec == b2->bt_trksec)
+ dups++;
+ return (b1->bt_trksec - b2->bt_trksec);
+}
+
+daddr_t
+badsn(bt)
+ register struct bt_bad *bt;
+{
+ return ((bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors
+ + (bt->bt_trksec&0xff));
+}
+
+#ifdef vax
+
+struct rp06hdr {
+ short h_cyl;
+ short h_trksec;
+ short h_key1;
+ short h_key2;
+ char h_data[512];
+#define RP06_FMT 010000 /* 1 == 16 bit, 0 == 18 bit */
+};
+
+/*
+ * Most massbus and unibus drives
+ * have headers of this form
+ */
+struct hpuphdr {
+ u_short hpup_cyl;
+ u_char hpup_sect;
+ u_char hpup_track;
+ char hpup_data[512];
+#define HPUP_OKSECT 0xc000 /* this normally means sector is good */
+#define HPUP_16BIT 0x1000 /* 1 == 16 bit format */
+};
+int rp06format(), hpupformat();
+
+struct formats {
+ char *f_name; /* disk name */
+ int f_bufsize; /* size of sector + header */
+ int f_bic; /* value to bic in hpup_cyl */
+ int (*f_routine)(); /* routine for special handling */
+} formats[] = {
+ { "rp06", sizeof (struct rp06hdr), RP06_FMT, rp06format },
+ { "eagle", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "capricorn", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "rm03", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "rm05", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "9300", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "9766", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { 0, 0, 0, 0 }
+};
+
+/*ARGSUSED*/
+hpupformat(fp, dp, blk, buf, count)
+ struct formats *fp;
+ struct disklabel *dp;
+ daddr_t blk;
+ char *buf;
+ int count;
+{
+ struct hpuphdr *hdr = (struct hpuphdr *)buf;
+ int sect;
+
+ if (count < sizeof(struct hpuphdr)) {
+ hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) |
+ (blk / (dp->d_nsectors * dp->d_ntracks));
+ sect = blk % (dp->d_nsectors * dp->d_ntracks);
+ hdr->hpup_track = (u_char)(sect / dp->d_nsectors);
+ hdr->hpup_sect = (u_char)(sect % dp->d_nsectors);
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+rp06format(fp, dp, blk, buf, count)
+ struct formats *fp;
+ struct disklabel *dp;
+ daddr_t blk;
+ char *buf;
+ int count;
+{
+
+ if (count < sizeof(struct rp06hdr)) {
+ warnx("can't read header on blk %d, can't reformat", blk);
+ return (-1);
+ }
+ return (0);
+}
+
+format(fd, blk)
+ int fd;
+ daddr_t blk;
+{
+ register struct formats *fp;
+ static char *buf;
+ static char bufsize;
+ struct format_op fop;
+ int n;
+
+ for (fp = formats; fp->f_name; fp++)
+ if (strncmp(dp->d_typename, fp->f_name, sizeof(dp->d_typename))
+ == 0 && strlen(fp->f_name) <= sizeof(dp->d_typename))
+ break;
+ if (fp->f_name == 0)
+ errx(2, "don't know how to format %.*s disks",
+ (int)sizeof(dp->d_typename), dp->d_typename);
+ if (buf && bufsize < fp->f_bufsize) {
+ free(buf);
+ buf = NULL;
+ }
+ if (buf == NULL)
+ buf = malloc((unsigned)fp->f_bufsize);
+ if (buf == NULL)
+ errx(3, "can't allocate sector buffer");
+ bufsize = fp->f_bufsize;
+ /*
+ * Here we do the actual formatting. All we really
+ * do is rewrite the sector header and flag the bad sector
+ * according to the format table description. If a special
+ * purpose format routine is specified, we allow it to
+ * process the sector as well.
+ */
+ if (verbose)
+ printf("format blk %d\n", blk);
+ bzero((char *)&fop, sizeof(fop));
+ fop.df_buf = buf;
+ fop.df_count = fp->f_bufsize;
+ fop.df_startblk = blk;
+ bzero(buf, fp->f_bufsize);
+ if (ioctl(fd, DIOCRFORMAT, &fop) < 0)
+ warn("read format");
+ if (fp->f_routine &&
+ (*fp->f_routine)(fp, dp, blk, buf, fop.df_count) != 0)
+ return;
+ if (fp->f_bic) {
+ struct hpuphdr *xp = (struct hpuphdr *)buf;
+
+ xp->hpup_cyl &= ~fp->f_bic;
+ }
+ if (nflag)
+ return;
+ bzero((char *)&fop, sizeof(fop));
+ fop.df_buf = buf;
+ fop.df_count = fp->f_bufsize;
+ fop.df_startblk = blk;
+ if (ioctl(fd, DIOCWFORMAT, &fop) < 0)
+ err(4, "write format");
+ if (fop.df_count != fp->f_bufsize) {
+ char msg[80];
+ (void)sprintf(msg, "write format %d", blk);
+ warn("%s", msg);
+ }
+}
+#endif
diff --git a/usr.sbin/boot0cfg/Makefile b/usr.sbin/boot0cfg/Makefile
new file mode 100644
index 0000000..6f965e3
--- /dev/null
+++ b/usr.sbin/boot0cfg/Makefile
@@ -0,0 +1,6 @@
+# $Id: $
+
+PROG= boot0cfg
+MAN8= boot0cfg.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/boot0cfg/boot0cfg.8 b/usr.sbin/boot0cfg/boot0cfg.8
new file mode 100644
index 0000000..bb73d75
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.8
@@ -0,0 +1,137 @@
+.\" 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.
+.\"
+.\" $Id: boot0cfg.8,v 1.2 1999/02/22 09:36:54 rnordier Exp $
+.\"
+.Dd February 21, 1999
+.Dt BOOT0CFG 8
+.Os
+.Sh NAME
+.Nm boot0cfg
+.Nd boot manager installation/configuration utility
+.Sh SYNOPSIS
+.Nm boot0cfg
+.Op Fl Bv
+.Op Fl b Ar boot0
+.Op Fl d Ar drive
+.Op Fl f Ar file
+.Op Fl o Ar options
+.Op Fl t Ar ticks
+.Ar disk
+.Sh DESCRIPTION
+The FreeBSD
+.Sq boot0
+boot manager permits the operator to select from which disk and which
+slice an i386 machine (PC) is booted.
+.Pp
+Note that what are referred to here as
+.Dq slices
+are typically called
+.Dq partitions
+in non-BSD 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, but without
+affecting the embedded slice table.
+.It Fl v
+Verbose: display information about the slices defined, etc.
+.It Fl b Ar boot0
+Specify which
+.Sq boot0
+image to use. The default is /boot/boot0.
+.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 truncated if it does.
+.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 rather than
+the conventional (CHS) interface, when accessing disk-related BIOS
+services. 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 t Ar ticks
+Set the timeout value to
+.Ar ticks .
+(There are approximately 18.2 ticks per second.)
+.El
+.Sh SEE ALSO
+.Xr boot 8 ,
+.Xr fdisk 8 .
+.Sh DIAGNOSTICS
+Exit status is 0 on success and >0 on error.
+.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..20fdcc3
--- /dev/null
+++ b/usr.sbin/boot0cfg/boot0cfg.c
@@ -0,0 +1,270 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: boot0cfg.c,v 1.2 1999/02/22 09:36:54 rnordier Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/disklabel.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_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 void stropt(const char *, int *, int *);
+static char *mkrdev(const char *);
+static int argtoi(const char *, int, int, int);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ u_int8_t buf[MBRSIZE];
+ struct dos_partition part[4];
+ const char *bpath, *fpath, *disk;
+ ssize_t n;
+ int B_flag, v_flag, o_flag;
+ int d_arg, t_arg;
+ int o_and, o_or;
+ int fd, fd1, up, c, i;
+
+ bpath = "/boot/boot0";
+ fpath = NULL;
+ B_flag = v_flag = o_flag = 0;
+ d_arg = t_arg = -1;
+ o_and = 0xff;
+ o_or = 0;
+ while ((c = getopt(argc, argv, "Bvb:d:f:o: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 'o':
+ stropt(optarg, &o_and, &o_or);
+ o_flag = 1;
+ 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 || o_flag || t_arg != -1;
+ if ((fd = open(disk, up ? O_RDWR : O_RDONLY)) == -1)
+ err(1, "%s", disk);
+ if ((n = read(fd, buf, MBRSIZE)) == -1)
+ err(1, "%s", disk);
+ if (n != MBRSIZE)
+ errx(1, "%s: short read", disk);
+ if (cv2(buf + OFF_MAGIC) != 0xaa55)
+ errx(1, "%s: bad magic", disk);
+ if (fpath) {
+ if ((fd1 = open(fpath, O_WRONLY | O_CREAT | O_TRUNC,
+ 0666)) == -1 ||
+ (n = write(fd1, buf, MBRSIZE)) == -1 || close(fd1))
+ err(1, "%s", fpath);
+ if (n != MBRSIZE)
+ errx(1, "%s: short write", fpath);
+ }
+ memcpy(part, buf + OFF_PTBL, sizeof(part));
+ if (B_flag) {
+ if ((fd1 = open(bpath, O_RDONLY)) == -1 ||
+ (n = read(fd1, buf, MBRSIZE)) == -1 || close(fd1))
+ err(1, "%s", bpath);
+ if (n != MBRSIZE)
+ errx(1, "%s: short read", bpath);
+ if (cv2(buf + OFF_MAGIC) != 0xaa55)
+ errx(1, "%s: bad magic", bpath);
+ memcpy(buf + OFF_PTBL, part, sizeof(part));
+ }
+ if (d_arg != -1)
+ buf[OFF_DRIVE] = d_arg;
+ if (o_flag) {
+ buf[OFF_FLAGS] &= o_and;
+ buf[OFF_FLAGS] |= o_or;
+ }
+ if (t_arg != -1)
+ mk2(buf + OFF_TICKS, t_arg);
+ if (up) {
+ if (lseek(fd, 0, SEEK_SET) == -1 ||
+ (n = write(fd, buf, MBRSIZE)) == -1 || close(fd))
+ err(1, "%s", disk);
+ if (n != MBRSIZE)
+ errx(1, "%s: short write", disk);
+ }
+ if (v_flag) {
+ printf(fmt0);
+ for (i = 0; i < 4; 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");
+ printf("drive=0x%x options=", buf[OFF_DRIVE]);
+ for (i = 0; i < nopt; i++) {
+ if (i)
+ printf(",");
+ if (!(buf[OFF_FLAGS] & 1 << (7 - i)) ^ opttbl[i].def)
+ printf("no");
+ printf("%s", opttbl[i].tok);
+ }
+ printf(" ticks=%u\n", cv2(buf + OFF_TICKS));
+ }
+ return 0;
+}
+
+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);
+}
+
+static char *
+mkrdev(const 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 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;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: boot0cfg [-Bv] [-b boot0] [-d drive] [-f file] [-o options]",
+ " [-t ticks] disk");
+ exit(1);
+}
diff --git a/usr.sbin/bootparamd/Makefile b/usr.sbin/bootparamd/Makefile
new file mode 100644
index 0000000..c4f6198
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile
@@ -0,0 +1,4 @@
+
+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..26c6f1c
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/bootparamd/bootparamd/Makefile b/usr.sbin/bootparamd/bootparamd/Makefile
new file mode 100644
index 0000000..3e45b90
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/Makefile
@@ -0,0 +1,23 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.9 1997/02/22 16:04:21 peter Exp $
+
+PROG= bootparamd
+SRCS= bootparamd.c main.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_svc.c bootparam_prot_xdr.c
+MAN5= bootparams.5
+MAN8= bootparamd.8
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\" -I.
+CLEANFILES= ${GENSRCS}
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_svc.c: ${RPCSRC}
+ rpcgen -m -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/bootparamd/README b/usr.sbin/bootparamd/bootparamd/README
new file mode 100644
index 0000000..85a2a54
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/README
@@ -0,0 +1,75 @@
+
+
+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-adress
+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..7e85edb
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.8
@@ -0,0 +1,48 @@
+.\" @(#)bootparamd.8
+.Dd November 8, 1989
+.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
+.Nm Bootparamd
+is a server process that provides information to diskless 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 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 can not handle long hostnames won't fail during boot.
+.Sh OPTIONS
+.Bl -tag -width Fl
+.It Fl d
+Display the debugging information.
+.It Fl s
+Log the debugging information with syslog.
+.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
+.El
+.Sh BUGS
+You may find the syslog 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..2c5da81
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.c
@@ -0,0 +1,342 @@
+/*
+
+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[] =
+ "$Id: bootparamd.c,v 1.8 1997/10/19 10:42:39 joerg Exp $";
+#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(whoami)
+bp_whoami_arg *whoami;
+{
+ 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(getfile)
+bp_getfile_arg *getfile;
+{
+ 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..97b561e
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparams.5
@@ -0,0 +1,79 @@
+.\"
+.\" 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
+.\" $Id: bootparams.5,v 1.7 1997/09/04 11:49:26 charnier Exp $
+.\"
+.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
+diskless
+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
+.Nm root
+and (optionally)
+.Nm swap
+areas.
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+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 "dummy" requests the pathname for
+its logical "root" it will be given the pathname
+.Dq Pa "host:/export/dummy/root"
+as the response to its
+.Tn RPC
+request. The "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..7484bec
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/main.c
@@ -0,0 +1,119 @@
+/*
+
+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[] =
+ "$Id: main.c,v 1.7 1997/09/04 11:49:28 charnier Exp $";
+#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;
+ int i;
+ 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( *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", argv[i]);
+ }
+ }
+ 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..1d4fa9d
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/Makefile
@@ -0,0 +1,22 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.8 1997/02/22 16:04:27 peter Exp $
+
+PROG= callbootd
+SRCS= callbootd.c ${GENSRCS}
+GENSRCS=bootparam_prot.h bootparam_prot_clnt.c bootparam_prot_xdr.c
+NOMAN=
+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..f92fb5f
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/callbootd.c
@@ -0,0 +1,198 @@
+/*
+
+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[] =
+ "$Id: callbootd.c,v 1.6 1997/09/04 11:50:37 charnier Exp $";
+#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>
+
+
+/* #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 *));
+
+int
+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);
+}
+
+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,
+ xdr_bp_whoami_arg, &whoami_arg,
+ xdr_bp_whoami_res, &stat_whoami_res, 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,
+ xdr_bp_getfile_arg, &getfile_arg,
+ xdr_bp_getfile_res, &stat_getfile_res,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/btxld/Makefile b/usr.sbin/btxld/Makefile
new file mode 100644
index 0000000..dd00a04
--- /dev/null
+++ b/usr.sbin/btxld/Makefile
@@ -0,0 +1,7 @@
+# $Id:$
+
+PROG= btxld
+SRCS= btxld.c elfh.c
+MAN8= btxld.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/btxld/btx.h b/usr.sbin/btxld/btx.h
new file mode 100644
index 0000000..4a31012
--- /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.
+ *
+ * $Id:$
+ */
+
+#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..4267270
--- /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.
+.\"
+.\" $Id:$
+.\"
+.Dd September 12, 1998
+.Dt BTXLD 8
+.Os
+.Sh NAME
+.Nm btxld
+.Nd link editor for BTX clients
+.Sh SYNOPSIS
+.Nm btxld
+.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
+Exit status is 0 on success and >0 on error.
+.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..51620ac
--- /dev/null
+++ b/usr.sbin/btxld/btxld.c
@@ -0,0 +1,554 @@
+/*
+ * 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[] =
+ "$Id: btxld.c,v 1.1.1.1 1998/09/12 06:49:48 rnordier Exp $";
+#endif /* not lint */
+
+#include <sys/types.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 <a.out.h>
+#include <elf.h>
+
+#include "btx.h"
+#include "elfh.h"
+
+#define BTX_PATH "/sys/boot/i386/btx"
+
+#define I_LDR 0 /* BTX loader */
+#define I_BTX 1 /* BTX kernel */
+#define I_CLNT 2 /* Client program */
+
+#define F_BIN 0 /* Binary */
+#define F_AOUT 1 /* ZMAGIC a.out */
+#define F_ELF 2 /* 32-bit ELF */
+#define F_CNT 3 /* Number of formats */
+
+#define IMPURE 1 /* Writable text */
+#define MAXU32 0xffffffff /* Maximum unsigned 32-bit quantity */
+
+#define align(x, y) (((x) + (y) - 1) & ~((y) - 1))
+
+struct hdr {
+ unsigned fmt; /* Format */
+ unsigned flags; /* Bit flags */
+ unsigned size; /* Size of file */
+ unsigned text; /* Size of text segment */
+ unsigned data; /* Size of data segment */
+ unsigned bss; /* Size of bss segment */
+ unsigned org; /* Program origin */
+ unsigned 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 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 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;
+ struct hdr ihdr, ohdr;
+ unsigned ldr_size, cwr;
+ int fdi[3], fdo, i;
+
+ 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:
+ writex(fdo, &btx, sizeof(btx));
+ 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);
+}
+
+/*
+ * Get file size and read a.out or ELF header.
+ */
+static void
+gethdr(int fd, struct hdr *hdr)
+{
+ struct stat sb;
+ const struct exec *ex;
+ const Elf32_Ehdr *ee;
+ const Elf32_Phdr *ep;
+ void *p;
+ unsigned 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 exec) && !N_BADMAG(*ex)) {
+ hdr->fmt = fmt;
+ x = N_GETMAGIC(*ex);
+ if (x == OMAGIC || x == NMAGIC) {
+ if (x == NMAGIC)
+ Warn(fname, "Treating %s NMAGIC as OMAGIC",
+ fmtlist[fmt]);
+ hdr->flags |= IMPURE;
+ }
+ hdr->text = ex->a_text;
+ hdr->data = ex->a_data;
+ hdr->bss = ex->a_bss;
+ hdr->entry = ex->a_entry;
+ if (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 < ee->e_phnum; i++) {
+ ep = (void *)((uint8_t *)p + ee->e_phoff +
+ ee->e_phentsize * i);
+ if (ep->p_type == PT_LOAD)
+ switch (n++) {
+ case 0:
+ hdr->text = ep->p_filesz;
+ hdr->org = ep->p_paddr;
+ if (ep->p_flags & PF_W)
+ hdr->flags |= IMPURE;
+ break;
+ case 1:
+ hdr->data = ep->p_filesz;
+ hdr->bss = ep->p_memsz - ep->p_filesz;
+ break;
+ case 2:
+ Warn(fname,
+ "Ignoring extra %s PT_LOAD segments",
+ fmtlist[fmt]);
+ }
+ }
+ hdr->entry = ee->e_entry;
+ }
+ }
+ if (munmap(p, hdr->size))
+ err(2, "%s", fname);
+}
+
+/*
+ * Write a.out or ELF header.
+ */
+static void
+puthdr(int fd, struct hdr *hdr)
+{
+ struct exec ex;
+ struct elfh eh;
+
+ switch (hdr->fmt) {
+ case F_AOUT:
+ memset(&ex, 0, sizeof(ex));
+ N_SETMAGIC(ex, ZMAGIC, MID_ZERO, 0);
+ hdr->text = N_ALIGN(ex, hdr->text);
+ ex.a_text = hdr->text;
+ hdr->data = N_ALIGN(ex, hdr->data);
+ ex.a_data = hdr->data;
+ ex.a_entry = hdr->entry;
+ writex(fd, &ex, sizeof(ex));
+ hdr->size = N_ALIGN(ex, sizeof(ex));
+ seekx(fd, hdr->size);
+ break;
+ case F_ELF:
+ eh = elfhdr;
+ eh.e.e_entry = hdr->entry;
+ eh.p[0].p_vaddr = eh.p[0].p_paddr = hdr->org;
+ eh.p[0].p_filesz = eh.p[0].p_memsz = hdr->text;
+ eh.p[1].p_offset = eh.p[0].p_offset + eh.p[0].p_filesz;
+ eh.p[1].p_vaddr = eh.p[1].p_paddr = align(eh.p[0].p_paddr +
+ eh.p[0].p_memsz, 4);
+ eh.p[1].p_filesz = eh.p[1].p_memsz = 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
+optfmt(const char *arg)
+{
+ unsigned 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..c60f3b2
--- /dev/null
+++ b/usr.sbin/btxld/elfh.c
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ *
+ * $Id:$
+ */
+
+#include <stddef.h>
+#include "elfh.h"
+
+#define SET_ME 0xeeeeeeee /* filled in by btxld */
+
+/*
+ * ELF header template.
+ */
+const struct elfh elfhdr = {
+ {
+ {
+ ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, /* e_ident */
+ ELFCLASS32, ELFDATA2LSB, EV_CURRENT, 0,
+ 'F', 'r', 'e', 'e', 'B', 'S', 'D', 0
+ },
+ ET_EXEC, /* e_type */
+ EM_386, /* e_machine */
+ EV_CURRENT, /* e_version */
+ SET_ME, /* e_entry */
+ offsetof(struct elfh, p), /* e_phoff */
+ offsetof(struct elfh, sh), /* e_shoff */
+ 0, /* e_flags */
+ sizeof(elfhdr.e), /* e_ehsize */
+ sizeof(elfhdr.p[0]), /* e_phentsize */
+ sizeof(elfhdr.p) / sizeof(elfhdr.p[0]), /* e_phnum */
+ sizeof(elfhdr.sh[0]), /* e_shentsize */
+ sizeof(elfhdr.sh) / sizeof(elfhdr.sh[0]), /* e_shnum */
+ 1 /* e_shstrndx */
+ },
+ {
+ {
+ PT_LOAD, /* p_type */
+ sizeof(elfhdr), /* p_offset */
+ SET_ME, /* p_vaddr */
+ SET_ME, /* p_paddr */
+ SET_ME, /* p_filesz */
+ SET_ME, /* p_memsz */
+ PF_R | PF_X, /* p_flags */
+ 0x1000 /* p_align */
+ },
+ {
+ PT_LOAD, /* p_type */
+ SET_ME, /* p_offset */
+ SET_ME, /* p_vaddr */
+ SET_ME, /* p_paddr */
+ SET_ME, /* p_filesz */
+ SET_ME, /* p_memsz */
+ PF_R | PF_W, /* p_flags */
+ 0x1000 /* p_align */
+ }
+ },
+ {
+ {
+ 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0
+ },
+ {
+ 1, /* sh_name */
+ SHT_STRTAB, /* sh_type */
+ 0, /* sh_flags */
+ 0, /* sh_addr */
+ offsetof(struct elfh, shstrtab), /* sh_offset */
+ sizeof(elfhdr.shstrtab), /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 1, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ 0xb, /* sh_name */
+ SHT_PROGBITS, /* sh_type */
+ SHF_EXECINSTR | SHF_ALLOC, /* sh_flags */
+ SET_ME, /* sh_addr */
+ SET_ME, /* sh_offset */
+ SET_ME, /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 4, /* sh_addralign */
+ 0 /* sh_entsize */
+ },
+ {
+ 0x11, /* sh_name */
+ SHT_PROGBITS, /* sh_type */
+ SHF_ALLOC | SHF_WRITE, /* sh_flags */
+ SET_ME, /* sh_addr */
+ SET_ME, /* sh_offset */
+ SET_ME, /* sh_size */
+ SHN_UNDEF, /* sh_link */
+ 0, /* sh_info */
+ 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..f7a845a
--- /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.
+ *
+ * $Id:$
+ */
+
+#include <elf.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 */
+};
+
+const struct elfh elfhdr; /* ELF header template */
diff --git a/usr.sbin/cdcontrol/Makefile b/usr.sbin/cdcontrol/Makefile
new file mode 100644
index 0000000..86a42eb
--- /dev/null
+++ b/usr.sbin/cdcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG= cdcontrol
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cdcontrol/cdcontrol.1 b/usr.sbin/cdcontrol/cdcontrol.1
new file mode 100644
index 0000000..22e788f
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.1
@@ -0,0 +1,181 @@
+.\" $Id: cdcontrol.1,v 1.17 1999/01/31 03:35:17 billf Exp $
+.\"
+.Dd July 3, 1995
+.Dt CDCONTROL 1
+.Os FreeBSD
+.Sh NAME
+.Nm cdcontrol
+.Nd compact disc control utility
+.Sh SYNOPSIS
+.Nm cdcontrol
+.Op Fl sv
+.Op Fl f Ar device
+.Op Ar command ...
+.Sh DESCRIPTION
+.Nm Cdcontrol
+is a program to control audio features of a CD drive. The device is a name such
+as
+.Pa cd0
+or
+.Pa mcd0 .
+.Pp
+If the device not specified, the environment variables
+.Ev MUSIC_CD ,
+.Ev CD_DRIVE ,
+.Ev DISC
+and
+.Ev CDPLAY
+will be used (in this order) to find the cd device.
+.Pp
+If no 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/cd0c
+or
+.Pa mcd0 .
+Both absolute path and relative to
+.Pa /dev
+filename are possible.
+Suffix `c' is added to the device name if needed.
+.El
+.Pp
+The available commands are listed below. Only as many
+characters as are required to uniquely identify a command
+need be specified. Word
+.Em play
+can be omitted.
+.Bl -tag -width Cm
+
+.It Cm 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 Cm play Ar start_m:start_s.start_f Op Ar end_m:end_s.end_f
+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 Cm play Op Ar #start_block Op length
+Play starting from the logical block
+.Ar start_block
+using
+.Ar length
+logical blocks.
+
+.It Cm pause
+Stop playing. Do not stop the disc.
+
+.It Cm resume
+Resume playing. Used after the
+.Em pause
+command.
+
+.It Cm stop
+Stop the disc.
+
+.It Cm eject
+Eject the disc.
+
+.It Cm close
+Inject the disc.
+
+.It Cm volume Ar left_channel Ar 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 Cm volume Ar mute
+Turn the sound off.
+
+.It Cm volume Ar mono
+Set the mono mode.
+
+.It Cm volume Ar stereo
+Set the stereo mode.
+
+.It Cm volume Ar left
+Play the left subtrack on both left and right channels.
+
+.It Cm volume Ar right
+Play the right subtrack on both left and right channels.
+
+.It Cm info
+Print the table of contents.
+
+.It Cm status
+.Op Ar audio | media | volume
+
+Print the information about the disc:
+
+.Nm audio
+the current playing status and position,
+
+.Nm media
+the current media catalog status,
+
+.Nm volume
+the current values of the volume for left and right channels.
+
+.It Cm help
+Print the list of available commands.
+
+.It Cm debug Ar on
+Enable the debugging mode of the CD device driver.
+
+.It Cm debug Ar off
+Disable the driver debugging mode.
+
+.It Cm reset
+Perform the hardware reset of the device.
+
+.It Cm set Ar msf
+Set minute-second-frame ioctl mode (default).
+
+.It Cm set Ar lba
+Set LBA ioctl mode.
+
+.It Cm quit
+Quit the program.
+
+.Sh FILES
+.Bl -tag -width /dev/rmcd0c -compact
+.It Pa /dev/rcd0c
+.It Pa /dev/rmcd0c
+.It Pa /dev/rwcd0c
+.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..121600d
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.c
@@ -0,0 +1,1033 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: cdcontrol.c,v 1.17 1998/01/26 00:57:54 jmz Exp $";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/cdio.h>
+#include <sys/ioctl.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 */
+
+#ifndef DEFAULT_CD_DRIVE
+# define DEFAULT_CD_DRIVE "/dev/cd0c"
+#endif
+
+#ifndef DEFAULT_CD_PARTITION
+# define DEFAULT_CD_PARTITION "c"
+#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 STATUS_AUDIO 0x1
+#define STATUS_MEDIA 0x2
+#define STATUS_VOLUME 0x4
+
+struct cmdtab {
+ int command;
+ char *name;
+ unsigned min;
+ 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_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_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" },
+{ 0, }
+};
+
+struct cd_toc_entry toc_buffer[100];
+
+const char *cdname;
+int fd = -1;
+int verbose = 1;
+int msf = 1;
+
+int setvol __P((int, int));
+int read_toc_entrys __P((int));
+int play_msf __P((int, int, int, int, int, int));
+int play_track __P((int, int, int, int));
+int get_vol __P((int *, int *));
+int status __P((int *, int *, int *, int *));
+int open_cd __P((void));
+int play __P((char *arg));
+int info __P((char *arg));
+int pstatus __P((char *arg));
+char *input __P((int *));
+void prtrack __P((struct cd_toc_entry *e, int lastflag));
+void lba2msf __P((unsigned long lba,
+ u_char *m, u_char *s, u_char *f));
+unsigned int msf2lba __P((u_char m, u_char s, u_char f));
+int play_blocks __P((int blk, int len));
+int run __P((int cmd, char *arg));
+char *parse __P((char *buf, int *cmd));
+
+void help ()
+{
+ struct cmdtab *c;
+ char *s, 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);
+}
+
+int main (int argc, char **argv)
+{
+ int cmd;
+ char *arg;
+
+ cdname = getenv ("MUSIC_CD");
+ if (! cdname)
+ cdname = getenv ("CD_DRIVE");
+ if (! cdname)
+ cdname = getenv ("DISC");
+ if (! cdname)
+ cdname = getenv ("CDPLAY");
+
+ 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 = DEFAULT_CD_DRIVE;
+ warnx("no CD device name specified, defaulting to %s", cdname);
+ }
+
+ 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)
+{
+ 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_STATUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return pstatus (arg);
+
+ 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);
+
+ default:
+ case CMD_HELP:
+ help ();
+ return (0);
+
+ }
+}
+
+int play (char *arg)
+{
+ struct ioc_toc_header h;
+ int rc, n, 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;
+
+ 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);
+ }
+
+ tr1--;
+
+ 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);
+}
+
+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;
+
+ 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])
+ printf(", number \"%.15s\"",
+ ss.data->what.media_catalog.mc_number);
+ 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);
+}
+
+int info (char *arg)
+{
+ 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;
+ lba2msf (len, &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;
+}
+
+char *input (int *cmd)
+{
+ static char buf[80];
+ char *p;
+
+ do {
+ if (verbose)
+ fprintf (stderr, "cdcontrol> ");
+ 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;
+ int len;
+
+ for (p=buf; isspace (*p); p++)
+ continue;
+
+ if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
+ *cmd = CMD_PLAY;
+ return (p);
+ }
+
+ 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[80];
+
+ if (fd > -1)
+ return (1);
+
+ if (*cdname == '/')
+ strcpy (devbuf, cdname);
+ else if (*cdname == 'r')
+ sprintf (devbuf, "/dev/%s", cdname);
+ else
+ sprintf (devbuf, "/dev/r%s", cdname);
+
+ fd = open (devbuf, O_RDONLY);
+
+ if (fd < 0 && errno == ENOENT) {
+ strcat (devbuf, DEFAULT_CD_PARTITION);
+ fd = open (devbuf, 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", devbuf);
+ return (0);
+ }
+ err(1, "%s", devbuf);
+ }
+ return (1);
+}
diff --git a/usr.sbin/chkgrp/Makefile b/usr.sbin/chkgrp/Makefile
new file mode 100644
index 0000000..3c691ee
--- /dev/null
+++ b/usr.sbin/chkgrp/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= chkgrp
+SRCS= chkgrp.c
+MAN8= chkgrp.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chkgrp/chkgrp.8 b/usr.sbin/chkgrp/chkgrp.8
new file mode 100644
index 0000000..cced32d
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.8
@@ -0,0 +1,72 @@
+.\" 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.
+.\"
+.\" $Id: chkgrp.8,v 1.3 1998/05/31 17:24:25 des Exp $
+.\"
+.Dd May 5, 1998
+.Dt CHKGRP 8
+.Os
+.Sh NAME
+.Nm chkgrp
+.Nd check the syntax of the group file
+.Sh SYNOPSIS
+.Nm chkgrp
+.Op Ar groupfile
+.Sh DESCRIPTION
+.Nm Chkgrp
+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 /dev/null -compact
+.It Pa /etc/group
+.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
+.Nm Chkgrp
+returns EX_DATAERR if errors were found in the group file, and EX_OK
+otherwise.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 3.0
+.Sh AUTHORS
+.Nm Chkgrp
+and this manual page were written by
+.An Dag-Erling Coïdan Smørgav 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..1806301
--- /dev/null
+++ b/usr.sbin/chkgrp/chkgrp.c
@@ -0,0 +1,141 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: chkgrp [groupfile]\n");
+ exit(EX_USAGE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ unsigned int len;
+ int n = 0, i, k, e = 0;
+ char *gfn, *line, *f[4], *p;
+ 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, gfn); /* XXX - is IO_ERR the correct exit code? */
+
+ /* check line by line */
+ while (++n) {
+ if ((line = fgetln(gf, &len)) == NULL)
+ break;
+ 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..9a4974c
--- /dev/null
+++ b/usr.sbin/chown/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= chown
+CFLAGS+=-DSUPPORT_DOT
+MAN1= chgrp.1
+MAN8= chown.8
+LINKS= ${BINDIR}/chown /usr/bin/chgrp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chown/chgrp.1 b/usr.sbin/chown/chgrp.1
new file mode 100644
index 0000000..67122c4
--- /dev/null
+++ b/usr.sbin/chown/chgrp.1
@@ -0,0 +1,133 @@
+.\" 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
+.\"
+.Dd March 31, 1994
+.Dt CHGRP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm chgrp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar group
+.Ar files ...
+.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
+Options:
+.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.
+.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.
+.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.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+In previous versions of this system, symbolic links did not have groups.
+.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..38372c2
--- /dev/null
+++ b/usr.sbin/chown/chown.8
@@ -0,0 +1,152 @@
+.\" 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
+.\" $Id: chown.8,v 1.5 1997/03/31 13:03:47 peter Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHOWN 8
+.Os BSD 4
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm chown
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar owner Op Ar :group
+.Ar file ...
+.Nm chown
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar :group
+.Ar
+.Sh DESCRIPTION
+.Nm Chown
+sets the user ID and/or the group ID of the specified files.
+.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.
+.It Fl R
+Change the user ID and/or the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.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 rather than the file that the link points to.
+.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.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.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.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+command is expected to be
+.St -p1003.2
+compliant.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.sbin/chown/chown.c b/usr.sbin/chown/chown.c
new file mode 100644
index 0000000..71dafb9
--- /dev/null
+++ b/usr.sbin/chown/chown.c
@@ -0,0 +1,273 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94";
+#else
+static const char rcsid[] =
+ "$Id: chown.c,v 1.11 1998/05/02 12:57:57 helbig Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void a_gid __P((char *));
+void a_uid __P((char *));
+void chownerr __P((char *));
+u_long id __P((char *, char *));
+void usage __P((void));
+
+uid_t uid;
+gid_t gid;
+int Rflag, ischown, fflag, hflag;
+char *gname, *myname;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
+ char *cp;
+
+ myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
+ ischown = myname[2] == 'o';
+
+ Hflag = Lflag = Pflag = hflag = 0;
+ while ((ch = getopt(argc, argv, "HLPRfh")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ fts_options = FTS_PHYSICAL;
+ if (Rflag) {
+ if (hflag && (Lflag || Hflag))
+ errx(1, "the -R and -h options may not be specified together");
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ }
+
+ uid = gid = -1;
+ if (ischown) {
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strchr(*argv, '.')) != NULL) {
+ *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, continue. */
+ 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: /* Ignore. */
+ 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 (hflag) {
+ if (lchown(p->fts_accpath, uid, gid) && !fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ } else {
+ if (chown(p->fts_accpath, uid, gid) && !fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gname = s;
+ gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
+}
+
+void
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+ uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
+}
+
+u_long
+id(name, type)
+ char *name, *type;
+{
+ u_long val;
+ char *ep;
+
+ /*
+ * XXX
+ * We know that uid_t's and gid_t's are unsigned longs.
+ */
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno)
+ err(1, "%s", name);
+ if (*ep != '\0')
+ errx(1, "%s: illegal %s name", name, type);
+ return (val);
+}
+
+void
+chownerr(file)
+ char *file;
+{
+ static int euid = -1, ngroups = -1;
+ gid_t groups[NGROUPS];
+
+ /* Check for chown without being root. */
+ if (errno != EPERM ||
+ (uid != -1 && euid == -1 && (euid = geteuid()) != 0))
+ err(1, "%s", file);
+
+ /* Check group membership; kernel just returns EPERM. */
+ if (gid != -1 && ngroups == -1 &&
+ euid == -1 && (euid = geteuid()) != 0) {
+ ngroups = getgroups(NGROUPS, groups);
+ while (--ngroups >= 0 && gid != groups[ngroups]);
+ if (ngroups < 0)
+ errx(1, "you are not a member of group %s", gname);
+ }
+ warn("%s", file);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: chown [-R [-H | -L | -P]] [-f] [-h] owner[:group] file ...",
+ " chown [-R [-H | -L | -P]] [-f] [-h] :group file ...",
+ " chgrp [-R [-H | -L | -P]] [-f] [-h] group file ...");
+ exit(1);
+}
diff --git a/usr.sbin/chroot/Makefile b/usr.sbin/chroot/Makefile
new file mode 100644
index 0000000..69fe8b8
--- /dev/null
+++ b/usr.sbin/chroot/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= chroot
+MAN8= chroot.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chroot/chroot.8 b/usr.sbin/chroot/chroot.8
new file mode 100644
index 0000000..607cfe3
--- /dev/null
+++ b/usr.sbin/chroot/chroot.8
@@ -0,0 +1,80 @@
+.\" 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
+.\"
+.Dd June 9, 1993
+.Dt CHROOT 8
+.Os BSD 4.3
+.Sh NAME
+.Nm chroot
+.Nd change root directory
+.Sh SYNOPSIS
+.Nm chroot
+.Ar newroot
+.Op Ar command
+.Sh DESCRIPTION
+The
+.Nm
+command changes its root directory to the supplied directory
+.Ar newroot
+and exec's
+.Ar command ,
+if supplied, or an interactive copy of your shell.
+.Pp
+Note,
+.Ar command
+or the shell are run as your real-user-id.
+.Sh ENVIRONMENT
+The following environment variable is referenced by
+.Nm chroot :
+.Bl -tag -width 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 environ 7
+.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..4049c6c
--- /dev/null
+++ b/usr.sbin/chroot/chroot.c
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)chroot.c 8.1 (Berkeley) 6/9/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *shell;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (chdir(argv[0]) || chroot("."))
+ err(1, "%s", argv[0]);
+
+ if (argv[1]) {
+ execvp(argv[1], &argv[1]);
+ err(1, "%s", argv[1]);
+ }
+
+ if (!(shell = getenv("SHELL")))
+ shell = _PATH_BSHELL;
+ execlp(shell, shell, "-i", NULL);
+ err(1, "%s", shell);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: chroot newroot [command]\n");
+ exit(1);
+}
diff --git a/usr.sbin/ckdist/Makefile b/usr.sbin/ckdist/Makefile
new file mode 100644
index 0000000..884fe9e
--- /dev/null
+++ b/usr.sbin/ckdist/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.1 1997/01/14 14:50:52 rnordier Exp $
+
+PROG= ckdist
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+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..dd9b78e
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1997 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" $Id: ckdist.1,v 1.1.1.1 1997/01/21 12:58:53 jkh Exp $
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list 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 ckdist
+.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
+cksum(1), 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 BSD and 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..e71a581
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.c
@@ -0,0 +1,440 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern int crc(int fd, u_long * cval, u_long * 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, 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(path);
+ if (fp != stdin && fclose(fp))
+ err(2, 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, 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;
+ u_long sum, len, 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 %lu%c", ext, &sum,
+ &len, &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)len)
+ 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, 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..e994b0c
--- /dev/null
+++ b/usr.sbin/config/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= config
+CFLAGS+=-I. -I${.CURDIR}
+SRCS= config.y main.c lang.l mkioconf.c mkmakefile.c mkglue.c mkheaders.c \
+ mkoptions.c mkswapconf.c y.tab.h
+MAN8= config.8
+DPADD= ${LIBL}
+LDADD= -ll
+
+.include <bsd.prog.mk>
+
+$(OBJS): configvers.h
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/Makefile b/usr.sbin/config/SMM.doc/Makefile
new file mode 100644
index 0000000..3a2f188
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/02.config
+SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t a.t b.t c.t d.t e.t
+MACROS= -ms
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
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.8 b/usr.sbin/config/config.8
new file mode 100644
index 0000000..d2be2bb
--- /dev/null
+++ b/usr.sbin/config/config.8
@@ -0,0 +1,183 @@
+.\" 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
+.\"
+.Dd April 19, 1994
+.Dt CONFIG 8
+.Os BSD 4
+.Sh NAME
+.Nm config
+.Nd build system configuration files
+.Sh SYNOPSIS
+.Nm config
+.Op Fl gpr
+.Ar SYSTEM_NAME
+.Sh DESCRIPTION
+This is the old version of the
+.Nm
+program.
+It understands the old autoconfiguration scheme
+used on the HP300, i386, DECstation, and derivative platforms.
+The new version of config 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
+.Nm Config
+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:
+.Pp
+.Bl -tag -width SYSTEM_NAME
+.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
+will configure a system for high resolution profiling.
+.It Fl r
+Remove the old compile directory (see below).
+.It Ar SYSTEM_NAME
+Specifies the name of the system configuration file
+containing device specifications, configuration options
+and other system parameters for one system configuration.
+.El
+.Pp
+.Nm Config
+should be run from the
+.Pa conf
+subdirectory of the system source (usually
+.Pa /sys/ARCH/conf ) .
+.Nm Config
+will create the directory
+.Pa ../../compile/SYSTEM_NAME
+as necessary and place all output files there.
+If the directory already exists and the
+.Fl r
+flag was specified, it will be removed first.
+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 vector.h ,
+definitions of
+macros related to counting interrupts;
+.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;
+so-called swap configuration files,
+definitions for
+the disk areas to be used for the root file system
+and system dumps.
+.Pp
+After running
+.Nm config ,
+it is necessary to run
+.Dq Li make depend
+in the directory where the new makefile
+was created.
+.Nm Config
+prints a reminder of this when it completes.
+.Pp
+If any other error messages are produced by
+.Nm config ,
+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 option "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.
+.Pp
+Strings | grep ___ will print just the configure information.
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/Makefile.i386 -compact
+.It Pa /sys/conf/files
+list of common files system is built from
+.It Pa /sys/i386/conf/Makefile.i386
+generic makefile for the
+.Tn i386
+.It Pa /sys/i386/conf/files.i386
+list of
+.Tn i386
+specific files
+.It Pa /sys/i386/conf/devices.i386
+name to major device mapping file for the
+.Tn i386
+.It Pa /sys/i386/conf/files. Ns Em ERNIE
+list of files specific to
+.Em ERNIE
+system
+.El
+.Sh SEE ALSO
+The 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
+command 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..823d541
--- /dev/null
+++ b/usr.sbin/config/config.h
@@ -0,0 +1,239 @@
+/*
+ * 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
+ */
+
+/*
+ * Config.
+ */
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NODEV ((dev_t)-1)
+
+struct file_list {
+ struct file_list *f_next;
+ char *f_fn; /* the name */
+ int f_type; /* type or count */
+ u_char f_flags; /* see below */
+ char *f_special; /* special make rule if present */
+ char *f_depends; /* additional dependancies */
+ char *f_clean; /* File list to add to clean rule */
+ char *f_needs;
+ /*
+ * Random values:
+ * swap space parameters for swap areas
+ * root device, etc. for system specifications
+ */
+ union {
+ struct { /* when swap specification */
+ dev_t fuw_swapdev;
+ int fuw_swapsize;
+ int fuw_swapflag;
+ } fuw;
+ struct { /* when system specification */
+ dev_t fus_rootdev;
+ dev_t fus_dumpdev;
+ } fus;
+ struct { /* when component dev specification */
+ dev_t fup_compdev;
+ int fup_compinfo;
+ } fup;
+ } fun;
+#define f_swapdev fun.fuw.fuw_swapdev
+#define f_swapsize fun.fuw.fuw_swapsize
+#define f_swapflag fun.fuw.fuw_swapflag
+#define f_rootdev fun.fus.fus_rootdev
+#define f_dumpdev fun.fus.fus_dumpdev
+#define f_compdev fun.fup.fup_compdev
+#define f_compinfo fun.fup.fup_compinfo
+};
+
+/*
+ * Types.
+ */
+#define DRIVER 1
+#define NORMAL 2
+#define INVISIBLE 3
+#define PROFILING 4
+#define SYSTEMSPEC 5
+#define SWAPSPEC 6
+#define COMPDEVICE 7
+#define COMPSPEC 8
+#define NODEPEND 9
+#define LOCAL 10
+#define DEVDONE 0x80000000
+#define TYPEMASK 0x7fffffff
+
+/*
+ * Attributes (flags).
+ */
+#define CONFIGDEP 1
+#define NO_IMPLCT_RULE 2
+#define NO_OBJ 4
+#define BEFORE_DEPEND 8
+
+struct idlst {
+ char *id;
+ struct idlst *id_next;
+};
+
+struct device {
+ int d_type; /* CONTROLLER, DEVICE, bus adaptor */
+ struct device *d_conn; /* what it is connected to */
+ char *d_name; /* name of device (e.g. rk11) */
+ struct idlst *d_vec; /* interrupt vectors */
+ int d_pri; /* interrupt priority */
+ int d_addr; /* address of csr */
+ int d_unit; /* unit number */
+ int d_drive; /* drive number */
+ int d_target; /* target number */
+ int d_lun; /* unit number */
+ int d_slave; /* slave number */
+#define QUES -1 /* -1 means '?' */
+#define UNKNOWN -2 /* -2 means not set yet */
+ int d_dk; /* if init 1 set to number for iostat */
+ int d_flags; /* flags for device init */
+ int d_conflicts; /* I'm allowed to conflict */
+ int d_disabled; /* nonzero to skip probe/attach */
+ char *d_port; /* io port base manifest constant */
+ int d_portn; /* io port base (if number not manifest) */
+ char *d_mask; /* interrupt mask */
+ int d_maddr; /* io memory base */
+ int d_msize; /* io memory size */
+ int d_drq; /* DMA request */
+ int d_irq; /* interrupt request */
+ struct device *d_next; /* Next one in list */
+};
+#define TO_NEXUS (struct device *)-1
+#define TO_VBA (struct device *)-2
+
+struct config {
+ char *c_dev;
+ 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 ``vax'',
+ * it will build from ``Makefile.vax'' and use ``../vax/inline''
+ * in the makerules, etc.
+ */
+int machine;
+char *machinename;
+#define MACHINE_VAX 1
+#define MACHINE_TAHOE 2
+#define MACHINE_HP300 3
+#define MACHINE_I386 4
+#define MACHINE_MIPS 5
+#define MACHINE_PMAX 6
+#define MACHINE_LUNA68K 7
+#define MACHINE_NEWS3400 8
+#define MACHINE_PC98 9
+#define MACHINE_ALPHA 10
+
+/*
+ * 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;
+ struct cputype *cpu_next;
+} *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_line; /* line number for error-reporting */
+ int op_ownfile; /* true = own file, false = makefile */
+ struct opt *op_next;
+} *opt, *mkopt;
+
+struct opt_list {
+ char *o_name;
+ char *o_file;
+ struct opt_list *o_next;
+} *otab;
+
+char *ident;
+char *ns();
+char *tc();
+char *qu();
+char *get_word();
+char *get_quoted_word();
+char *path();
+char *raise();
+void moveifchanged();
+
+int do_trace;
+
+#if MACHINE_VAX
+int seen_mba, seen_uba;
+#endif
+#if MACHINE_TAHOE
+int seen_vba;
+#endif
+#if MACHINE_I386
+int seen_isa;
+int seen_scbus;
+#endif
+int seen_cd;
+
+struct device *connect();
+struct device *dtab;
+dev_t nametodev();
+char *devtoname();
+
+char errbuf[80];
+int yyline;
+
+struct file_list *ftab, *conf_list, **confp, *comp_list, **compp;
+
+int profiling;
+int debugging;
+
+int maxusers;
+u_int loadaddress;
+
+extern int old_config_present; /* Old config/build directory still there */
+
+extern char *PREFIX; /* Config file name - for error messages */
+
+#define eq(a,b) (!strcmp(a,b))
diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y
new file mode 100644
index 0000000..04757a9
--- /dev/null
+++ b/usr.sbin/config/config.y
@@ -0,0 +1,1159 @@
+%union {
+ char *str;
+ int val;
+ struct file_list *file;
+ struct idlst *lst;
+}
+
+%token AND
+%token ANY
+%token ARGS
+%token AT
+%token BIO
+%token BUS
+%token CAM
+%token HA
+%token COMMA
+%token CONFIG
+%token CONFLICTS
+%token CONTROLLER
+%token CPU
+%token CSR
+%token DEVICE
+%token DISABLE
+%token DISK
+%token DRIVE
+%token DRQ
+%token DUMPS
+%token EQUALS
+%token FLAGS
+%token IDENT
+%token INTERLEAVE
+%token IOMEM
+%token IOSIZ
+%token IRQ
+%token MACHINE
+%token MAJOR
+%token MASTER
+%token MAXUSERS
+%token MINOR
+%token MINUS
+%token NET
+%token NEXUS
+%token ON
+%token OPTIONS
+%token MAKEOPTIONS
+%token PORT
+%token PRIORITY
+%token PSEUDO_DEVICE
+%token ROOT
+%token SEMICOLON
+%token SEQUENTIAL
+%token SIZE
+%token SLAVE
+%token SWAP
+%token TARGET
+%token TTY
+%token TRACE
+%token UNIT
+%token VECTOR
+
+%token <str> ID
+%token <val> NUMBER
+%token <val> FPNUMBER
+
+%type <str> Save_id
+%type <str> Opt_value
+%type <str> Dev
+%type <lst> Id_list
+%type <val> optional_size
+%type <val> optional_sflag
+%type <str> device_name
+%type <val> major_minor
+%type <val> arg_device_spec
+%type <val> root_device_spec root_device_specs
+%type <val> dump_device_spec
+%type <file> swap_device_spec
+%type <file> comp_device_spec
+
+%{
+
+/*
+ * 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
+ */
+
+#include "config.h"
+
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#ifdef linux
+#include <sys/sysmacros.h>
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+struct device cur;
+struct device *curp = 0;
+
+#define ns(s) strdup(s)
+
+int alreadychecked __P((dev_t, dev_t[], dev_t *));
+void deverror __P((char *, char *));
+int finddev __P((dev_t));
+void init_dev __P((struct device *));
+void verifycomp __P((struct file_list *));
+
+%}
+%%
+Configuration:
+ Many_specs
+ = { verifysystemspecs(); }
+ ;
+
+Many_specs:
+ Many_specs Spec
+ |
+ /* lambda */
+ ;
+
+Spec:
+ Device_spec SEMICOLON
+ = { newdev(&cur); } |
+ Config_spec SEMICOLON
+ |
+ TRACE SEMICOLON
+ = { do_trace = !do_trace; } |
+ SEMICOLON
+ |
+ error SEMICOLON
+ ;
+
+Config_spec:
+ MACHINE Save_id
+ = {
+ if (!strcmp($2, "vax")) {
+ machine = MACHINE_VAX;
+ machinename = "vax";
+ } else if (!strcmp($2, "tahoe")) {
+ machine = MACHINE_TAHOE;
+ machinename = "tahoe";
+ } else if (!strcmp($2, "hp300")) {
+ machine = MACHINE_HP300;
+ machinename = "hp300";
+ } else if (!strcmp($2, "i386")) {
+ machine = MACHINE_I386;
+ machinename = "i386";
+ } else if (!strcmp($2, "pc98")) {
+ machine = MACHINE_PC98;
+ machinename = "pc98";
+ } else if (!strcmp($2, "mips")) {
+ machine = MACHINE_MIPS;
+ machinename = "mips";
+ } else if (!strcmp($2, "pmax")) {
+ machine = MACHINE_PMAX;
+ machinename = "pmax";
+ } else if (!strcmp($2, "luna68k")) {
+ machine = MACHINE_LUNA68K;
+ machinename = "luna68k";
+ } else if (!strcmp($2, "news3400")) {
+ machine = MACHINE_NEWS3400;
+ machinename = "news3400";
+ } else if (!strcmp($2, "alpha")) {
+ machine = MACHINE_ALPHA;
+ machinename = "alpha";
+ } else
+ yyerror("Unknown machine type");
+ } |
+ CPU Save_id
+ = {
+ struct cputype *cp =
+ (struct cputype *)malloc(sizeof (struct cputype));
+ memset(cp, 0, sizeof(*cp));
+ cp->cpu_name = $2;
+ cp->cpu_next = cputype;
+ cputype = cp;
+ } |
+ OPTIONS Opt_list
+ |
+ MAKEOPTIONS Mkopt_list
+ |
+ IDENT ID
+ = { ident = $2; } |
+ System_spec
+ |
+ MAXUSERS NUMBER
+ = { maxusers = $2; };
+
+System_spec:
+ System_id System_parameter_list
+ = { checksystemspec(*confp); }
+ ;
+
+System_id:
+ CONFIG Save_id
+ = { mkconf($2); }
+ ;
+
+System_parameter_list:
+ System_parameter_list System_parameter
+ | System_parameter
+ ;
+
+System_parameter:
+ addr_spec
+ | swap_spec
+ | root_spec
+ | dump_spec
+ | arg_spec
+ ;
+
+addr_spec:
+ AT NUMBER
+ = { loadaddress = $2; }
+ ;
+
+swap_spec:
+ SWAP optional_on swap_device_list
+ ;
+
+swap_device_list:
+ swap_device_list AND swap_device
+ | swap_device
+ ;
+
+swap_device:
+ swap_device_spec optional_size optional_sflag
+ = { mkswap(*confp, $1, $2, $3); }
+ ;
+
+swap_device_spec:
+ device_name
+ = {
+ struct file_list *fl = newflist(SWAPSPEC);
+
+ if (eq($1, "generic"))
+ fl->f_fn = $1;
+ else {
+ fl->f_swapdev = nametodev($1, 0,
+ COMPATIBILITY_SLICE, 'b');
+ fl->f_fn = devtoname(fl->f_swapdev);
+ }
+ $$ = fl;
+ }
+ | major_minor
+ = {
+ struct file_list *fl = newflist(SWAPSPEC);
+
+ fl->f_swapdev = $1;
+ fl->f_fn = devtoname($1);
+ $$ = fl;
+ }
+ ;
+
+root_spec:
+ ROOT optional_on root_device_specs
+ = {
+ struct file_list *fl = *confp;
+
+ if (fl && fl->f_rootdev != NODEV)
+ yyerror("extraneous root device specification");
+ else
+ fl->f_rootdev = $3;
+ }
+ ;
+
+root_device_specs:
+ root_device_spec AND root_device_specs
+ = {
+ warnx("extraneous root devices ignored");
+ $$ = $1;
+ }
+ | root_device_spec
+ ;
+
+root_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'a'); }
+ | major_minor
+ ;
+
+dump_spec:
+ DUMPS optional_on dump_device_spec
+ = {
+ struct file_list *fl = *confp;
+
+ if (fl && fl->f_dumpdev != NODEV)
+ yyerror("extraneous dump device specification");
+ else
+ fl->f_dumpdev = $3;
+ }
+
+ ;
+
+dump_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'b'); }
+ | major_minor
+ ;
+
+arg_spec:
+ ARGS optional_on arg_device_spec
+ = { yyerror("arg device specification obsolete, ignored"); }
+ ;
+
+arg_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'b'); }
+ | major_minor
+ ;
+
+major_minor:
+ MAJOR NUMBER MINOR NUMBER
+ = { $$ = makedev($2, $4); }
+ ;
+
+optional_on:
+ ON
+ | /* empty */
+ ;
+
+optional_size:
+ SIZE NUMBER
+ = { $$ = $2; }
+ | /* empty */
+ = { $$ = 0; }
+ ;
+
+optional_sflag:
+ SEQUENTIAL
+ = { $$ = 2; }
+ | /* empty */
+ = { $$ = 0; }
+ ;
+
+device_name:
+ Save_id
+ = { $$ = $1; }
+ | Save_id NUMBER
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d", $1, $2);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s", $1, $2, $3);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID NUMBER
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s%d", $1, $2, $3, $4);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID NUMBER ID
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s%d%s",
+ $1, $2, $3, $4, $5);
+ $$ = ns(buf); free($1);
+ }
+ ;
+
+Opt_list:
+ Opt_list COMMA Option
+ |
+ Option
+ ;
+
+Option:
+ Save_id
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ char *s;
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_next = opt;
+ op->op_value = 0;
+ /*
+ * op->op_line is 1-based; yyline is 0-based but is now 1
+ * larger than when `Save_id' was lexed.
+ */
+ op->op_line = yyline;
+ opt = op;
+ if ((s = strchr(op->op_name, '='))) {
+ /* AARGH!!!! Old-style bogon */
+ *s = '\0';
+ op->op_value = ns(s + 1);
+ }
+ } |
+ Save_id EQUALS Opt_value
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_next = opt;
+ op->op_value = $3;
+ op->op_line = yyline + 1;
+ opt = op;
+ } ;
+
+Opt_value:
+ ID
+ = { $$ = $1; } |
+ NUMBER
+ = {
+ char nb[16];
+ (void) sprintf(nb, "%d", $1);
+ $$ = ns(nb);
+ } ;
+
+
+Save_id:
+ ID
+ = { $$ = $1; }
+ ;
+
+Mkopt_list:
+ Mkopt_list COMMA Mkoption
+ |
+ Mkoption
+ ;
+
+Mkoption:
+ Save_id EQUALS Opt_value
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_ownfile = 0; /* for now */
+ op->op_next = mkopt;
+ op->op_value = $3;
+ op->op_line = yyline + 1;
+ mkopt = op;
+ } ;
+
+Dev:
+ ID
+ = { $$ = $1; }
+ ;
+
+Device_spec:
+ DEVICE Dev_name Dev_info Int_spec
+ = { cur.d_type = DEVICE; } |
+ MASTER Dev_name Dev_info Int_spec
+ = { cur.d_type = MASTER; } |
+ DISK Dev_name Dev_info Int_spec
+ = { cur.d_dk = 1; cur.d_type = DEVICE; } |
+ CONTROLLER Dev_name Dev_info Int_spec
+ = { cur.d_type = CONTROLLER; } |
+ PSEUDO_DEVICE Init_dev Dev
+ = {
+ cur.d_name = $3;
+ cur.d_type = PSEUDO_DEVICE;
+ } |
+ PSEUDO_DEVICE Init_dev Dev NUMBER
+ = {
+ cur.d_name = $3;
+ cur.d_type = PSEUDO_DEVICE;
+ cur.d_slave = $4;
+ } |
+ PSEUDO_DEVICE Dev_name Cdev_init Cdev_info
+ = {
+ if (!eq(cur.d_name, "cd"))
+ yyerror("improper spec for pseudo-device");
+ seen_cd = 1;
+ cur.d_type = DEVICE;
+ verifycomp(*compp);
+ };
+
+Cdev_init:
+ /* lambda */
+ = { mkcomp(&cur); };
+
+Cdev_info:
+ optional_on comp_device_list comp_option_list
+ ;
+
+comp_device_list:
+ comp_device_list AND comp_device
+ | comp_device
+ ;
+
+comp_device:
+ comp_device_spec
+ = { addcomp(*compp, $1); }
+ ;
+
+comp_device_spec:
+ device_name
+ = {
+ struct file_list *fl = newflist(COMPSPEC);
+
+ fl->f_compdev = nametodev($1, 0, COMPATIBILITY_SLICE,
+ 'c');
+ fl->f_fn = devtoname(fl->f_compdev);
+ $$ = fl;
+ }
+ | major_minor
+ = {
+ struct file_list *fl = newflist(COMPSPEC);
+
+ fl->f_compdev = $1;
+ fl->f_fn = devtoname($1);
+ $$ = fl;
+ }
+ ;
+
+comp_option_list:
+ comp_option_list comp_option
+ |
+ /* lambda */
+ ;
+
+comp_option:
+ INTERLEAVE NUMBER
+ = { cur.d_pri = $2; } |
+ FLAGS NUMBER
+ = { cur.d_flags = $2; };
+
+Dev_name:
+ Init_dev Dev NUMBER
+ = {
+ cur.d_name = $2;
+ if (eq($2, "mba"))
+ seen_mba = 1;
+ else if (eq($2, "uba"))
+ seen_uba = 1;
+ else if (eq($2, "vba"))
+ seen_vba = 1;
+ else if (eq($2, "isa"))
+ seen_isa = 1;
+ else if (eq($2, "scbus"))
+ seen_scbus = 1;
+ cur.d_unit = $3;
+ };
+
+Init_dev:
+ /* lambda */
+ = { init_dev(&cur); };
+
+Dev_info:
+ Con_info Info_list
+ |
+ /* lambda */
+ ;
+
+Con_info:
+ AT Dev NUMBER
+ = {
+ if (eq(cur.d_name, "mba") || eq(cur.d_name, "uba")) {
+ (void) sprintf(errbuf,
+ "%s must be connected to a nexus", cur.d_name);
+ yyerror(errbuf);
+ }
+ cur.d_conn = connect($2, $3);
+ } |
+ AT NEXUS NUMBER
+ = { check_nexus(&cur, $3); cur.d_conn = TO_NEXUS; };
+
+Info_list:
+ Info_list Info
+ |
+ /* lambda */
+ ;
+
+Info:
+ CSR NUMBER
+ = { cur.d_addr = $2; } |
+ BUS NUMBER
+ = {
+ if (cur.d_conn != 0 && cur.d_conn->d_type == CONTROLLER)
+ cur.d_slave = $2;
+ else
+ yyerror("can't specify a bus to something "
+ "other than a controller");
+ } |
+ TARGET NUMBER
+ = { cur.d_target = $2; } |
+ UNIT NUMBER
+ = { cur.d_lun = $2; } |
+ DRIVE NUMBER
+ = { cur.d_drive = $2; } |
+ SLAVE NUMBER
+ = {
+ if (cur.d_conn != 0 && cur.d_conn != TO_NEXUS &&
+ cur.d_conn->d_type == MASTER)
+ cur.d_slave = $2;
+ else
+ yyerror("can't specify slave--not to master");
+ } |
+ IRQ NUMBER
+ = { cur.d_irq = $2; } |
+ DRQ NUMBER
+ = { cur.d_drq = $2; } |
+ IOMEM NUMBER
+ = { cur.d_maddr = $2; } |
+ IOSIZ NUMBER
+ = { cur.d_msize = $2; } |
+ PORT device_name
+ = { cur.d_port = $2; } |
+ PORT NUMBER
+ = { cur.d_portn = $2; } |
+ TTY
+ = { cur.d_mask = "tty"; } |
+ BIO
+ = { cur.d_mask = "bio"; } |
+ CAM
+ = { cur.d_mask = "cam"; } |
+ HA
+ = { cur.d_mask = "ha"; } |
+ NET
+ = { cur.d_mask = "net"; } |
+ FLAGS NUMBER
+ = { cur.d_flags = $2; } |
+ DISABLE
+ = { cur.d_disabled = 1; } |
+ CONFLICTS
+ = { cur.d_conflicts = 1; };
+
+Int_spec:
+ VECTOR Id_list
+ = { cur.d_vec = $2; } |
+ PRIORITY NUMBER
+ = { cur.d_pri = $2; } |
+ /* lambda */
+ ;
+
+Id_list:
+ Save_id
+ = {
+ struct idlst *a = (struct idlst *)malloc(sizeof(struct idlst));
+ memset(a, 0, sizeof(*a));
+ a->id = $1; a->id_next = 0; $$ = a;
+ } |
+ Save_id Id_list =
+ {
+ struct idlst *a = (struct idlst *)malloc(sizeof(struct idlst));
+ memset(a, 0, sizeof(*a));
+ a->id = $1; a->id_next = $2; $$ = a;
+ };
+
+%%
+
+void
+yyerror(s)
+ char *s;
+{
+
+ warnx("line %d: %s", yyline + 1, s);
+}
+
+/*
+ * add a device to the list of devices
+ */
+void
+newdev(dp)
+ register struct device *dp;
+{
+ register struct device *np;
+
+ np = (struct device *) malloc(sizeof *np);
+ memset(np, 0, sizeof(*np));
+ *np = *dp;
+ np->d_next = 0;
+ if (curp == 0)
+ dtab = np;
+ else
+ curp->d_next = np;
+ curp = np;
+}
+
+/*
+ * note that a configuration should be made
+ */
+void
+mkconf(sysname)
+ char *sysname;
+{
+ register struct file_list *fl, **flp;
+
+ fl = (struct file_list *) malloc(sizeof *fl);
+ memset(fl, 0, sizeof(*fl));
+ fl->f_type = SYSTEMSPEC;
+ fl->f_needs = sysname;
+ fl->f_rootdev = NODEV;
+ fl->f_dumpdev = NODEV;
+ fl->f_fn = 0;
+ fl->f_next = 0;
+ for (flp = confp; *flp; flp = &(*flp)->f_next)
+ ;
+ *flp = fl;
+ confp = flp;
+}
+
+struct file_list *
+newflist(ftype)
+ u_char ftype;
+{
+ struct file_list *fl = (struct file_list *)malloc(sizeof (*fl));
+ memset(fl, 0, sizeof(*fl));
+
+ fl->f_type = ftype;
+ fl->f_next = 0;
+ fl->f_swapdev = NODEV;
+ fl->f_swapsize = 0;
+ fl->f_needs = 0;
+ fl->f_fn = 0;
+ return (fl);
+}
+
+/*
+ * Add a swap device to the system's configuration
+ */
+void
+mkswap(system, fl, size, flag)
+ struct file_list *system, *fl;
+ int size, flag;
+{
+ register struct file_list **flp;
+
+ if (system == 0 || system->f_type != SYSTEMSPEC) {
+ yyerror("\"swap\" spec precedes \"config\" specification");
+ return;
+ }
+ if (size < 0) {
+ yyerror("illegal swap partition size");
+ return;
+ }
+ /*
+ * Append swap description to the end of the list.
+ */
+ flp = &system->f_next;
+ for (; *flp && (*flp)->f_type == SWAPSPEC; flp = &(*flp)->f_next)
+ ;
+ fl->f_next = *flp;
+ *flp = fl;
+ fl->f_swapsize = size;
+ fl->f_swapflag = flag;
+ /*
+ * If first swap device for this system,
+ * set up f_fn field to insure swap
+ * files are created with unique names.
+ */
+ if (system->f_fn)
+ return;
+ if (eq(fl->f_fn, "generic"))
+ system->f_fn = ns(fl->f_fn);
+ else
+ system->f_fn = ns(system->f_needs);
+}
+
+void
+mkcomp(dp)
+ register struct device *dp;
+{
+ register struct file_list *fl, **flp;
+ char buf[80];
+
+ fl = (struct file_list *) malloc(sizeof *fl);
+ memset(fl, 0, sizeof(*fl));
+ fl->f_type = COMPDEVICE;
+ fl->f_compinfo = dp->d_unit;
+ fl->f_fn = ns(dp->d_name);
+ (void) sprintf(buf, "%s%d", dp->d_name, dp->d_unit);
+ fl->f_needs = ns(buf);
+ fl->f_next = 0;
+ for (flp = compp; *flp; flp = &(*flp)->f_next)
+ ;
+ *flp = fl;
+ compp = flp;
+}
+
+void
+addcomp(compdev, fl)
+ struct file_list *compdev, *fl;
+{
+ register struct file_list **flp;
+
+ if (compdev == 0 || compdev->f_type != COMPDEVICE) {
+ yyerror("component spec precedes device specification");
+ return;
+ }
+ /*
+ * Append description to the end of the list.
+ */
+ flp = &compdev->f_next;
+ for (; *flp && (*flp)->f_type == COMPSPEC; flp = &(*flp)->f_next)
+ ;
+ fl->f_next = *flp;
+ *flp = fl;
+}
+
+/*
+ * find the pointer to connect to the given device and number.
+ * returns 0 if no such device and prints an error message
+ */
+struct device *
+connect(dev, num)
+ register char *dev;
+ register int num;
+{
+ register struct device *dp;
+ struct device *huhcon();
+
+ if (num == QUES)
+ return (huhcon(dev));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if ((num != dp->d_unit) || !eq(dev, dp->d_name))
+ continue;
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER) {
+ (void) sprintf(errbuf,
+ "%s connected to non-controller", dev);
+ yyerror(errbuf);
+ return (0);
+ }
+ return (dp);
+ }
+ (void) sprintf(errbuf, "%s %d not defined", dev, num);
+ yyerror(errbuf);
+ return (0);
+}
+
+/*
+ * connect to an unspecific thing
+ */
+struct device *
+huhcon(dev)
+ register char *dev;
+{
+ register struct device *dp, *dcp;
+ struct device rdev;
+ int oldtype;
+
+ /*
+ * First make certain that there are some of these to wildcard on
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if (eq(dp->d_name, dev))
+ break;
+ if (dp == 0) {
+ (void) sprintf(errbuf, "no %s's to wildcard", dev);
+ yyerror(errbuf);
+ return (0);
+ }
+ oldtype = dp->d_type;
+ dcp = dp->d_conn;
+ /*
+ * Now see if there is already a wildcard entry for this device
+ * (e.g. Search for a "uba ?")
+ */
+ for (; dp != 0; dp = dp->d_next)
+ if (eq(dev, dp->d_name) && dp->d_unit == -1)
+ break;
+ /*
+ * If there isn't, make one because everything needs to be connected
+ * to something.
+ */
+ if (dp == 0) {
+ dp = &rdev;
+ init_dev(dp);
+ dp->d_unit = QUES;
+ dp->d_name = ns(dev);
+ dp->d_type = oldtype;
+ newdev(dp);
+ dp = curp;
+ /*
+ * Connect it to the same thing that other similar things are
+ * connected to, but make sure it is a wildcard unit
+ * (e.g. up connected to sc ?, here we make connect sc? to a
+ * uba?). If other things like this are on the NEXUS or
+ * if they aren't connected to anything, then make the same
+ * connection, else call ourself to connect to another
+ * unspecific device.
+ */
+ if (dcp == TO_NEXUS || dcp == 0)
+ dp->d_conn = dcp;
+ else
+ dp->d_conn = connect(dcp->d_name, QUES);
+ }
+ return (dp);
+}
+
+void
+init_dev(dp)
+ register struct device *dp;
+{
+
+ dp->d_name = "OHNO!!!";
+ dp->d_type = DEVICE;
+ dp->d_conn = 0;
+ dp->d_conflicts = 0;
+ dp->d_disabled = 0;
+ dp->d_vec = 0;
+ dp->d_addr = dp->d_flags = dp->d_dk = 0;
+ dp->d_pri = -1;
+ dp->d_slave = dp->d_lun = dp->d_target = dp->d_drive = dp->d_unit = UNKNOWN;
+ dp->d_port = (char *)0;
+ dp->d_portn = -1;
+ dp->d_irq = -1;
+ dp->d_drq = -1;
+ dp->d_maddr = 0;
+ dp->d_msize = 0;
+ dp->d_mask = "null";
+}
+
+/*
+ * make certain that this is a reasonable type of thing to connect to a nexus
+ */
+void
+check_nexus(dev, num)
+ register struct device *dev;
+ int num;
+{
+
+ switch (machine) {
+
+ case MACHINE_VAX:
+ if (!eq(dev->d_name, "uba") && !eq(dev->d_name, "mba") &&
+ !eq(dev->d_name, "bi"))
+ yyerror("only uba's, mba's, and bi's should be connected to the nexus");
+ if (num != QUES)
+ yyerror("can't give specific nexus numbers");
+ break;
+
+ case MACHINE_TAHOE:
+ if (!eq(dev->d_name, "vba"))
+ yyerror("only vba's should be connected to the nexus");
+ break;
+
+ case MACHINE_HP300:
+ case MACHINE_LUNA68K:
+ if (num != QUES)
+ dev->d_addr = num;
+ break;
+
+ case MACHINE_I386:
+ case MACHINE_PC98:
+ if (!eq(dev->d_name, "isa"))
+ yyerror("only isa's should be connected to the nexus");
+ break;
+
+ case MACHINE_NEWS3400:
+ if (!eq(dev->d_name, "iop") && !eq(dev->d_name, "hb") &&
+ !eq(dev->d_name, "vme"))
+ yyerror("only iop's, hb's and vme's should be connected to the nexus");
+ break;
+ }
+}
+
+/*
+ * Check system specification and apply defaulting
+ * rules on root, argument, dump, and swap devices.
+ */
+void
+checksystemspec(fl)
+ register struct file_list *fl;
+{
+ char buf[BUFSIZ];
+ register struct file_list *swap;
+ int generic;
+
+ if (fl == 0 || fl->f_type != SYSTEMSPEC) {
+ yyerror("internal error, bad system specification");
+ exit(1);
+ }
+ swap = fl->f_next;
+ generic = swap && swap->f_type == SWAPSPEC && eq(swap->f_fn, "generic");
+ if (fl->f_rootdev == NODEV && !generic) {
+ yyerror("no root device specified");
+ exit(1);
+ }
+ /*
+ * Default swap area to be in 'b' partition of root's
+ * device. If root specified to be other than on 'a'
+ * partition, give warning, something probably amiss.
+ */
+ if (swap == 0 || swap->f_type != SWAPSPEC) {
+ dev_t dev;
+
+ swap = newflist(SWAPSPEC);
+ dev = fl->f_rootdev;
+ if (dkpart(dev) != 0) {
+ (void) sprintf(buf,
+"Warning, swap defaulted to 'b' partition with root on '%c' partition",
+ dkpart(dev) + 'a');
+ yyerror(buf);
+ }
+ swap->f_swapdev = dkmodpart(dev, SWAP_PART);
+ swap->f_fn = devtoname(swap->f_swapdev);
+ mkswap(fl, swap, 0);
+ }
+ /*
+ * Make sure a generic swap isn't specified, along with
+ * other stuff (user must really be confused).
+ */
+ if (generic) {
+ if (fl->f_rootdev != NODEV)
+ yyerror("root device specified with generic swap");
+ if (fl->f_dumpdev != NODEV)
+ yyerror("dump device specified with generic swap");
+ return;
+ }
+ /*
+ * Warn if dump device is not a swap area.
+ */
+ if (fl->f_dumpdev != NODEV && fl->f_dumpdev != swap->f_swapdev) {
+ struct file_list *p = swap->f_next;
+
+ for (; p && p->f_type == SWAPSPEC; p = p->f_next)
+ if (fl->f_dumpdev == p->f_swapdev)
+ return;
+ (void) sprintf(buf,
+ "Warning: dump device is not a swap partition");
+ yyerror(buf);
+ }
+}
+
+/*
+ * Verify all devices specified in the system specification
+ * are present in the device specifications.
+ */
+void
+verifysystemspecs()
+{
+ register struct file_list *fl;
+ dev_t checked[50], *verifyswap();
+ register dev_t *pchecked = checked;
+
+ for (fl = conf_list; fl; fl = fl->f_next) {
+ if (fl->f_type != SYSTEMSPEC)
+ continue;
+ if (!finddev(fl->f_rootdev))
+ deverror(fl->f_needs, "root");
+ *pchecked++ = fl->f_rootdev;
+ pchecked = verifyswap(fl->f_next, checked, pchecked);
+ if (!alreadychecked(fl->f_dumpdev, checked, pchecked)) {
+ if (!finddev(fl->f_dumpdev))
+ deverror(fl->f_needs, "dump");
+ *pchecked++ = fl->f_dumpdev;
+ }
+ }
+}
+
+/*
+ * Do as above, but for swap devices.
+ */
+dev_t *
+verifyswap(fl, checked, pchecked)
+ register struct file_list *fl;
+ dev_t checked[];
+ register dev_t *pchecked;
+{
+
+ for (;fl && fl->f_type == SWAPSPEC; fl = fl->f_next) {
+ if (eq(fl->f_fn, "generic"))
+ continue;
+ if (alreadychecked(fl->f_swapdev, checked, pchecked))
+ continue;
+ if (!finddev(fl->f_swapdev))
+ warnx("swap device %s not configured", fl->f_fn);
+ *pchecked++ = fl->f_swapdev;
+ }
+ return (pchecked);
+}
+
+/*
+ * Verify that components of a compound device have themselves been config'ed
+ */
+void
+verifycomp(fl)
+ register struct file_list *fl;
+{
+ char *dname = fl->f_needs;
+
+ for (fl = fl->f_next; fl; fl = fl->f_next) {
+ if (fl->f_type != COMPSPEC || finddev(fl->f_compdev))
+ continue;
+ warnx("%s: component device %s not configured", dname, fl->f_needs);
+ }
+}
+
+/*
+ * Has a device already been checked
+ * for its existence in the configuration?
+ */
+int
+alreadychecked(dev, list, last)
+ dev_t dev, list[];
+ register dev_t *last;
+{
+ register dev_t *p;
+
+ for (p = list; p < last; p++)
+ if (dkmodpart(*p, 0) != dkmodpart(dev, 0))
+ return (1);
+ return (0);
+}
+
+void
+deverror(systemname, devtype)
+ char *systemname, *devtype;
+{
+
+ warnx("%s: %s device not configured", systemname, devtype);
+}
+
+/*
+ * Look for the device in the list of
+ * configured hardware devices. Must
+ * take into account stuff wildcarded.
+ */
+/*ARGSUSED*/
+int
+finddev(dev)
+ dev_t dev;
+{
+
+ /* punt on this right now */
+ return (1);
+}
diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h
new file mode 100644
index 0000000..c43c000
--- /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 = 30 etc). The least
+ * significant digits are incremented for each incompatable change.
+ *
+ * The numbering scheme is inspired by the sys/conf/newvers.sh RELDATE
+ * and <osreldate.h> system.
+ *
+ * $Id: configvers.h,v 1.7 1998/10/23 17:05:06 bde Exp $
+ */
+#define CONFIGVERS 300009
diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l
new file mode 100644
index 0000000..b3c0f8f
--- /dev/null
+++ b/usr.sbin/config/lang.l
@@ -0,0 +1,239 @@
+%{
+/*-
+ * 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
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include "y.tab.h"
+#include "config.h"
+
+#define tprintf if (do_trace) printf
+
+/*
+ * Key word table
+ */
+
+struct kt {
+ char *kt_name;
+ int kt_val;
+} key_words[] = {
+ { "and", AND },
+ { "args", ARGS },
+ { "at", AT },
+#if MACHINE_I386
+ { "bio", BIO },
+ { "bus", BUS },
+ { "cam", CAM },
+ { "conflicts", CONFLICTS },
+#endif
+ { "config", CONFIG },
+ { "controller", CONTROLLER },
+ { "cpu", CPU },
+ { "csr", CSR },
+ { "device", DEVICE },
+#if MACHINE_I386
+ { "disable", DISABLE },
+#endif
+ { "disk", DISK },
+ { "drive", DRIVE },
+#if MACHINE_I386
+ { "drq", DRQ },
+#endif
+ { "dumps", DUMPS },
+ { "flags", FLAGS },
+#if MACHINE_I386
+ { "ha", HA },
+#endif
+ { "ident", IDENT },
+ { "interleave", INTERLEAVE },
+#if MACHINE_I386
+ { "iomem", IOMEM },
+ { "iosiz", IOSIZ },
+ { "irq", IRQ },
+#endif
+ { "machine", MACHINE },
+ { "major", MAJOR },
+ { "makeoptions", MAKEOPTIONS },
+ { "master", MASTER },
+ { "maxusers", MAXUSERS },
+ { "minor", MINOR },
+#if MACHINE_I386
+ { "net", NET },
+#endif
+ { "nexus", NEXUS },
+ { "on", ON },
+ { "options", OPTIONS },
+#if MACHINE_I386
+ { "port", PORT },
+#endif
+ { "priority", PRIORITY },
+ { "pseudo-device",PSEUDO_DEVICE },
+ { "root", ROOT },
+#if MACHINE_HP300 || MACHINE_LUNA68K
+ { "scode", NEXUS },
+#endif
+ { "sequential", SEQUENTIAL },
+ { "size", SIZE },
+ { "slave", SLAVE },
+ { "swap", SWAP },
+ { "tape", DEVICE },
+ { "target", TARGET },
+#if MACHINE_I386
+ { "tty", TTY },
+#endif
+ { "trace", TRACE },
+ { "unit", UNIT },
+ { "vector", VECTOR },
+ { 0, 0 },
+};
+
+
+int kw_lookup __P((char *));
+int octal __P((char *));
+int hex __P((char *));
+
+%}
+WORD [A-Za-z_][-A-Za-z_]*
+%%
+{WORD} {
+ int i;
+
+ if ((i = kw_lookup(yytext)) == -1)
+ {
+ yylval.str = strdup(yytext);
+ tprintf("id(%s) ", yytext);
+ return ID;
+ }
+ tprintf("(%s) ", yytext);
+ return i;
+ }
+\\\"[^"]+\\\" {
+ yytext[strlen(yytext)-2] = '"';
+ yytext[strlen(yytext)-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+\"[^"]+\" {
+ yytext[strlen(yytext)-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+0[0-7]* {
+ yylval.val = octal(yytext);
+ tprintf("#O:%o ", yylval.val);
+ return NUMBER;
+ }
+0x[0-9a-fA-F]+ {
+ yylval.val = hex(yytext);
+ tprintf("#X:%x ", yylval.val);
+ return NUMBER;
+ }
+[1-9][0-9]* {
+ yylval.val = atoi(yytext);
+ tprintf("#D:%d ", yylval.val);
+ return NUMBER;
+ }
+[0-9]"."[0-9]* {
+ double atof();
+ yylval.val = (int) (60 * atof(yytext) + 0.5);
+ return FPNUMBER;
+ }
+"-" {
+ return MINUS;
+ }
+"?" {
+ yylval.val = -1;
+ tprintf("? ");
+ return NUMBER;
+ }
+\n/[ \t] {
+ yyline++;
+ tprintf("\n... ");
+ }
+\n {
+ yyline++;
+ tprintf("\n");
+ return SEMICOLON;
+ }
+#.* { /* Ignored (comment) */; }
+[ \t\f]* { /* Ignored (white space) */; }
+";" { return SEMICOLON; }
+"," { return COMMA; }
+"=" { return EQUALS; }
+"@" { return AT; }
+. { 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(word)
+register char *word;
+{
+ register 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
+ */
+
+int
+octal(str)
+char *str;
+{
+ int num;
+
+ (void) sscanf(str, "%o", &num);
+ return num;
+}
+
+int
+hex(str)
+char *str;
+{
+ int num;
+
+ (void) sscanf(str+2, "%x", &num);
+ return num;
+}
diff --git a/usr.sbin/config/main.c b/usr.sbin/config/main.c
new file mode 100644
index 0000000..0c05358
--- /dev/null
+++ b/usr.sbin/config/main.c
@@ -0,0 +1,445 @@
+/*
+ * 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[] =
+ "$Id: main.c,v 1.25 1998/06/09 14:02:03 dfr Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include "y.tab.h"
+#include "config.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+char *PREFIX;
+static int no_config_clobber = TRUE;
+int old_config_present;
+
+static void usage __P((void));
+void configfile __P((void));
+
+/*
+ * Config builds a set of files for building a UNIX
+ * system given a description of the desired system.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ struct stat buf;
+ int ch;
+ char *p;
+
+ while ((ch = getopt(argc, argv, "gprn")) != -1)
+ switch (ch) {
+ case 'g':
+ debugging++;
+ break;
+ case 'p':
+ profiling++;
+ break;
+ case 'n':
+ /* no_config_clobber is now true by default, no-op */
+ fprintf(stderr,
+ "*** Using obsolete config option '-n' ***\n");
+ break;
+ case 'r':
+ no_config_clobber = FALSE;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (freopen(PREFIX = *argv, "r", stdin) == NULL)
+ err(2, "%s", PREFIX);
+
+ 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);
+ }
+ else if (!no_config_clobber) {
+ char tmp[strlen(p) + 8];
+
+ fprintf(stderr, "Removing old directory %s: ", p);
+ fflush(stderr);
+ sprintf(tmp, "rm -rf %s", p);
+ if (system(tmp)) {
+ fprintf(stderr, "Failed!\n");
+ err(2, "%s", tmp);
+ }
+ fprintf(stderr, "Done.\n");
+ if (mkdir(p, 0777))
+ err(2, "%s", p);
+ }
+ else
+ old_config_present = 1;
+
+ loadaddress = -1;
+ dtab = NULL;
+ confp = &conf_list;
+ compp = &comp_list;
+ if (yyparse())
+ exit(3);
+ switch (machine) {
+
+ case MACHINE_VAX:
+ vax_ioconf(); /* Print ioconf.c */
+ ubglue(); /* Create ubglue.s */
+ break;
+
+ case MACHINE_TAHOE:
+ tahoe_ioconf();
+ vbglue();
+ break;
+
+ case MACHINE_HP300:
+ case MACHINE_LUNA68K:
+ hp300_ioconf();
+ hpglue();
+ break;
+
+ case MACHINE_I386:
+ case MACHINE_PC98:
+ i386_ioconf(); /* Print ioconf.c */
+ vector(); /* Create vector.s */
+ break;
+
+ case MACHINE_MIPS:
+ case MACHINE_PMAX:
+ pmax_ioconf();
+ break;
+
+ case MACHINE_NEWS3400:
+ news_ioconf();
+ break;
+
+ case MACHINE_ALPHA:
+ alpha_ioconf();
+ break;
+
+ default:
+ printf("Specify machine type, e.g. ``machine vax''\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".
+ */
+ {
+ char xxx[80];
+
+ (void) snprintf(xxx, sizeof(xxx), "../../%s/include", machinename);
+ (void) symlink(xxx, path("machine"));
+ }
+ options(); /* make options .h files */
+ makefile(); /* build Makefile */
+ headers(); /* make a lot of .h files */
+ swapconf(); /* swap config files */
+ configfile(); /* put config file into kernel*/
+ printf("Kernel build directory is %s\n", p);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: config [-gpr] 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(fp)
+ register FILE *fp;
+{
+ static char line[80];
+ register int ch;
+ register 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(fp)
+ register FILE *fp;
+{
+ static char line[256];
+ register int ch;
+ register 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 == '\'') {
+ register 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(file)
+ char *file;
+{
+ register char *cp;
+
+#define CDIR "../../compile/"
+ cp = malloc((unsigned int)(sizeof(CDIR) + strlen(PREFIX) +
+ (file ? strlen(file) : 0) + 2));
+ (void) strcpy(cp, CDIR);
+ (void) strcat(cp, PREFIX);
+ if (file) {
+ (void) strcat(cp, "/");
+ (void) strcat(cp, file);
+ }
+ return (cp);
+}
+
+void
+configfile()
+{
+ 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);
+ }
+
+#ifdef DIAG
+ if (changed)
+ printf("CHANGED! rename (%s, %s)\n", from_name, to_name);
+ else
+ printf("SAME! unlink (%s)\n", from_name);
+#endif
+
+ return;
+}
diff --git a/usr.sbin/config/mkglue.c b/usr.sbin/config/mkglue.c
new file mode 100644
index 0000000..9bfae60
--- /dev/null
+++ b/usr.sbin/config/mkglue.c
@@ -0,0 +1,412 @@
+/*
+ * 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[] = "@(#)mkglue.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkglue.c,v 1.13 1997/09/21 22:12:49 gibbs Exp $";
+#endif /* not lint */
+
+/*
+ * Make the bus adaptor interrupt glue files.
+ */
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include "config.h"
+#include "y.tab.h"
+
+void vector_devtab __P((FILE *, char *, int *));
+void vector __P((void));
+void dump_ctrs __P((FILE *));
+void dump_intname __P((FILE *, char *, int));
+void dump_std __P((FILE *, FILE *));
+void dump_vbavec __P((FILE *, char *, int));
+void dump_ubavec __P((FILE *, char *, int));
+
+/*
+ * Create the UNIBUS interrupt vector glue file.
+ */
+void
+ubglue()
+{
+ register FILE *fp, *gp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("ubglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ubglue.s"));
+ gp = fopen(path("ubvec.s"), "w");
+ if (gp == 0)
+ err(1, "%s", path("ubvec.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_ubavec(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_std(fp, gp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+ (void) fclose(gp);
+}
+
+static int cntcnt = 0; /* number of interrupt counters allocated */
+
+/*
+ * Print a UNIBUS interrupt vector.
+ */
+void
+dump_ubavec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "\t.globl\t_X%s\n\t.align\t2\n_X%s:\n\tpushr\t$0x3f\n",
+ v, v);
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ if (strncmp(vector, "dzx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdzdma\n\n", number);
+ else if (strncmp(vector, "dpx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdpxdma\n\n", number);
+ else if (strncmp(vector, "dpr", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdprdma\n\n", number);
+ else {
+ if (strncmp(vector, "uur", 3) == 0) {
+ fprintf(fp, "#ifdef UUDMA\n");
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjsb\tuudma\n", number);
+ fprintf(fp, "#endif\n");
+ }
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcalls\t$1,_%s\n\tpopr\t$0x3f\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n\trei\n\n");
+ }
+}
+
+/*
+ * Create the VERSAbus interrupt vector glue file.
+ */
+void
+vbglue()
+{
+ register FILE *fp, *gp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("vbglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("vbglue.s"));
+ gp = fopen(path("vbvec.s"), "w");
+ if (gp == 0)
+ err(1, "%s", path("vbvec.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ struct idlst *id, *id2;
+
+ mp = dp->d_conn;
+ if (mp == 0 || mp == (struct device *)-1 ||
+ eq(mp->d_name, "mba"))
+ continue;
+ for (id = dp->d_vec; id; id = id->id_next)
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id == id2) {
+ dump_vbavec(fp, id->id, dp->d_unit);
+ break;
+ }
+ if (eq(id->id, id2->id))
+ break;
+ }
+ }
+ dump_std(fp, gp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (eq(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+ (void) fclose(gp);
+}
+
+/*
+ * Print a VERSAbus interrupt vector
+ */
+void
+dump_vbavec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "SCBVEC(%s):\n", v);
+ fprintf(fp, "\tCHECK_SFE(4)\n");
+ fprintf(fp, "\tSAVE_FPSTAT(4)\n");
+ fprintf(fp, "\tPUSHR\n");
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcallf\t$8,_%s\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n");
+ fprintf(fp, "\tPOPR\n");
+ fprintf(fp, "\tREST_FPSTAT\n");
+ fprintf(fp, "\trei\n\n");
+}
+
+/*
+ * HP9000/300 interrupts are auto-vectored.
+ * Code is hardwired in locore.s
+ */
+void
+hpglue() {}
+
+static char *vaxinames[] = {
+ "clock", "cnr", "cnx", "tur", "tux",
+ "mba0", "mba1", "mba2", "mba3",
+ "uba0", "uba1", "uba2", "uba3"
+};
+static char *tahoeinames[] = {
+ "clock", "cnr", "cnx", "rmtr", "rmtx", "buserr",
+};
+static struct stdintrs {
+ char **si_names; /* list of standard interrupt names */
+ int si_n; /* number of such names */
+} stdintrs[] = {
+ { vaxinames, sizeof (vaxinames) / sizeof (vaxinames[0]) },
+ { tahoeinames, (sizeof (tahoeinames) / sizeof (tahoeinames[0])) }
+};
+/*
+ * Start the interrupt name table with the names
+ * of the standard vectors not directly associated
+ * with a bus. Also, dump the defines needed to
+ * reference the associated counters into a separate
+ * file which is prepended to locore.s.
+ */
+void
+dump_std(fp, gp)
+ register FILE *fp, *gp;
+{
+ register struct stdintrs *si = &stdintrs[machine-1];
+ register char **cpp;
+ register int i;
+
+ fprintf(fp, "\n\t.globl\t_intrnames\n");
+ fprintf(fp, "\n\t.globl\t_eintrnames\n");
+ fprintf(fp, "\t.data\n");
+ fprintf(fp, "_intrnames:\n");
+ cpp = si->si_names;
+ for (i = 0; i < si->si_n; i++) {
+ register char *cp, *tp;
+ char buf[80];
+
+ cp = *cpp;
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ }
+ for (tp = buf; *cp; cp++)
+ if (islower(*cp))
+ *tp++ = toupper(*cp);
+ else
+ *tp++ = *cp;
+ *tp = '\0';
+ fprintf(gp, "#define\tI_%s\t%d\n", buf, i*sizeof (long));
+ fprintf(fp, "\t.asciz\t\"%s\"\n", *cpp);
+ cpp++;
+ }
+}
+
+void
+dump_intname(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ register char *cp = vector;
+
+ fprintf(fp, "\t.asciz\t\"");
+ /*
+ * Skip any "int" or "intr" in the name.
+ */
+ while (*cp)
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ } else {
+ putc(*cp, fp);
+ cp++;
+ }
+ fprintf(fp, "%d\"\n", number);
+}
+
+/*
+ * Reserve space for the interrupt counters.
+ */
+void
+dump_ctrs(fp)
+ register FILE *fp;
+{
+ struct stdintrs *si = &stdintrs[machine-1];
+
+ fprintf(fp, "_eintrnames:\n");
+ fprintf(fp, "\n\t.globl\t_intrcnt\n");
+ fprintf(fp, "\n\t.globl\t_eintrcnt\n");
+ fprintf(fp, "\t.align 2\n");
+ fprintf(fp, "_intrcnt:\n");
+ fprintf(fp, "\t.space\t4 * %d\n", si->si_n);
+ fprintf(fp, "_fltintrcnt:\n");
+ fprintf(fp, "\t.space\t4 * %d\n", cntcnt);
+ fprintf(fp, "_eintrcnt:\n\n");
+ fprintf(fp, "\t.text\n");
+}
+
+/*
+ * Create the ISA interrupt vector glue file.
+ *
+ * The interrupt handlers are hardwired into vector.s and are attached
+ * at runtime depending on the data in ioconf.c and on the results of
+ * probing. Here we only need to generate the names of the interrupt
+ * handlers in an ancient form suitable for vmstat (the _eintrcnt label
+ * can't be expressed in C). We give the names of all devices to
+ * simplify the correspondence between devices and interrupt handlers.
+ * The order must match that in mkioconf.c.
+ */
+void
+vector()
+{
+ int dev_id;
+ FILE *fp;
+
+ fp = fopen(path("vector.h.new"), "w");
+ if (fp == NULL)
+ err(1, "%s", path("vector.h.new"));
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * vector.h\n");
+ fprintf(fp, " * Macros for interrupt vector routines\n");
+ fprintf(fp, " * Generated by config program\n");
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "#define\tDEVICE_NAMES \"\\\n");
+ fprintf(fp, "clk0 irqnn\\0\\\n");
+ fprintf(fp, "rtc0 irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ dev_id = 10;
+ vector_devtab(fp, "bio", &dev_id);
+ vector_devtab(fp, "tty", &dev_id);
+ vector_devtab(fp, "net", &dev_id);
+ vector_devtab(fp, "cam", &dev_id);
+ vector_devtab(fp, "ha", &dev_id);
+ vector_devtab(fp, "null", &dev_id);
+ fprintf(fp, "\"\n\n");
+ fprintf(fp, "#define\tNR_DEVICES\t%d\n", dev_id);
+ (void) fclose(fp);
+ moveifchanged(path("vector.h.new"), path("vector.h"));
+}
+
+void
+vector_devtab(fp, table, dev_idp)
+ FILE *fp;
+ char *table;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+
+ for (dp = dtab; dp != NULL; dp = dp->d_next) {
+ if (dp->d_unit == QUES || !eq(dp->d_mask, table))
+ continue;
+ mp = dp->d_conn;
+ if (mp == NULL || mp == TO_NEXUS || !eq(mp->d_name, "isa"))
+ continue;
+ fprintf(fp, "%s%d irqnn\\0\\\n", dp->d_name, dp->d_unit);
+ (*dev_idp)++;
+ }
+}
diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c
new file mode 100644
index 0000000..74e92ee
--- /dev/null
+++ b/usr.sbin/config/mkheaders.c
@@ -0,0 +1,229 @@
+/*
+ * 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[] =
+ "$Id: mkheaders.c,v 1.7 1997/10/28 07:21:02 joerg Exp $";
+#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 "config.h"
+#include "y.tab.h"
+
+#define ns(s) strdup(s)
+
+void do_header __P((char *, char *, int));
+void do_count __P((char *, char *, int));
+
+void
+headers()
+{
+ register struct file_list *fl;
+ struct device *dp;
+
+ for (fl = ftab; fl != 0; fl = fl->f_next)
+ if (fl->f_needs != 0)
+ do_count(fl->f_needs, fl->f_needs, 1);
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if ((dp->d_type & TYPEMASK) == PSEUDO_DEVICE) {
+ if (!(dp->d_type & DEVDONE))
+ printf("Warning: pseudo-device \"%s\" is unknown\n",
+ dp->d_name);
+ else
+ dp->d_type &= TYPEMASK;
+ }
+}
+
+/*
+ * count all the devices of a certain type and recurse to count
+ * whatever the device is connected to
+ */
+void
+do_count(dev, hname, search)
+ register char *dev, *hname;
+ int search;
+{
+ register struct device *dp, *mp;
+ register int count, hicount;
+
+ /*
+ * 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.
+ */
+ for (hicount = count = 0, dp = dtab; dp != 0; dp = dp->d_next)
+ if (dp->d_unit != -1 && eq(dp->d_name, dev)) {
+ if ((dp->d_type & TYPEMASK) == PSEUDO_DEVICE) {
+ count =
+ dp->d_slave != UNKNOWN ? dp->d_slave : 1;
+ dp->d_type |= DEVDONE;
+ break;
+ }
+ count++;
+ /*
+ * Allow holes in unit numbering,
+ * assumption is unit numbering starts
+ * at zero.
+ */
+ if (dp->d_unit + 1 > hicount)
+ hicount = dp->d_unit + 1;
+ if (search) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != TO_NEXUS &&
+ mp->d_conn != 0 && mp->d_conn != TO_NEXUS) {
+ do_count(mp->d_name, hname, 0);
+ search = 0;
+ }
+ }
+ }
+ do_header(dev, hname, count > hicount ? count : hicount);
+}
+
+void
+do_header(dev, hname, count)
+ char *dev, *hname;
+ int count;
+{
+ char *file, *name, *inw, *toheader(), *tomacro();
+ struct file_list *fl, *fl_head, *tflp;
+ FILE *inf, *outf;
+ int inc, oldcount;
+
+ file = toheader(hname);
+ name = tomacro(dev);
+ 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;
+ }
+ fl_head = NULL;
+ 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;
+ fl->f_next = fl_head;
+ fl_head = fl;
+ }
+ (void) fclose(inf);
+ if (count == oldcount) {
+ for (fl = fl_head; fl != NULL; fl = tflp) {
+ tflp = fl->f_next;
+ free(fl->f_fn);
+ free(fl);
+ }
+ return;
+ }
+ if (oldcount == -1) {
+ fl = (struct file_list *) malloc(sizeof *fl);
+ bzero(fl, sizeof(*fl));
+ fl->f_fn = ns(name);
+ fl->f_type = count;
+ fl->f_next = fl_head;
+ fl_head = fl;
+ }
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ for (fl = fl_head; fl != NULL; fl = tflp) {
+ fprintf(outf,
+ "#define %s %u\n", fl->f_fn, count ? fl->f_type : 0);
+ tflp = fl->f_next;
+ free(fl->f_fn);
+ free(fl);
+ }
+ (void) fclose(outf);
+}
+
+/*
+ * convert a dev name to a .h file name
+ */
+char *
+toheader(dev)
+ char *dev;
+{
+ static char hbuf[80];
+
+ (void) strcpy(hbuf, path(dev));
+ (void) strcat(hbuf, ".h");
+ return (hbuf);
+}
+
+/*
+ * convert a dev name to a macro name
+ */
+char *tomacro(dev)
+ register char *dev;
+{
+ static char mbuf[20];
+ register char *cp;
+
+ cp = mbuf;
+ *cp++ = 'N';
+ while (*dev)
+ *cp++ = islower(*dev) ? toupper(*dev++) : *dev++;
+ *cp++ = 0;
+ return (mbuf);
+}
diff --git a/usr.sbin/config/mkioconf.c b/usr.sbin/config/mkioconf.c
new file mode 100644
index 0000000..824f4aa
--- /dev/null
+++ b/usr.sbin/config/mkioconf.c
@@ -0,0 +1,1369 @@
+/*
+ * 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[] = "@(#)mkioconf.c 8.2 (Berkeley) 1/21/94";
+#endif
+static const char rcsid[] =
+ "$Id: mkioconf.c,v 1.47 1999/02/05 16:49:18 bde Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include "y.tab.h"
+#include "config.h"
+
+/*
+ * build the ioconf.c file
+ */
+char *qu();
+char *intv();
+char *wnum();
+void pseudo_ioconf();
+void comp_config __P((FILE *));
+void scbus_devtab __P((FILE *, int *));
+void isa_devtab __P((FILE *, char *, int *));
+void isa_biotab __P((FILE *, char *));
+void i386_ioconf __P((void));
+void hp300_ioconf __P((void));
+int hpbadslave __P((struct device *, struct device *));
+void tahoe_ioconf __P((void));
+void vax_ioconf __P((void));
+
+#if MACHINE_VAX
+void
+vax_ioconf()
+{
+ register struct device *dp, *mp, *np;
+ register int uba_n, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <vax/include/pte.h>\n");
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <vax/mba/mbavar.h>\n");
+ fprintf(fp, "#include <vax/uba/ubavar.h>\n\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ /*
+ * First print the mba initialization structures
+ */
+ if (seen_mba) {
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "mba"))
+ continue;
+ fprintf(fp, "extern struct mba_driver %sdriver;\n",
+ dp->d_name);
+ }
+ fprintf(fp, "\nstruct mba_device mbdinit[] = {\n");
+ fprintf(fp, "\t/* Device, Unit, Mba, Drive, Dk */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0 ||
+ mp == TO_NEXUS || !eq(mp->d_name, "mba"))
+ continue;
+ if (dp->d_addr) {
+ printf("can't specify csr address on mba for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("can't specify vector for %s%d on mba\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive == UNKNOWN) {
+ printf("drive not specified for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_slave != UNKNOWN) {
+ printf("can't specify slave number for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ fprintf(fp, "\t{ &%sdriver, %d, %s,",
+ dp->d_name, dp->d_unit, qu(mp->d_unit));
+ fprintf(fp, " %s, %d },\n",
+ qu(dp->d_drive), dp->d_dk);
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ /*
+ * Print the mbsinit structure
+ * Driver Controller Unit Slave
+ */
+ fprintf(fp, "struct mba_slave mbsinit [] = {\n");
+ fprintf(fp, "\t/* Driver, Ctlr, Unit, Slave */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ /*
+ * All slaves are connected to something which
+ * is connected to the massbus.
+ */
+ if ((mp = dp->d_conn) == 0 || mp == TO_NEXUS)
+ continue;
+ np = mp->d_conn;
+ if (np == 0 || np == TO_NEXUS ||
+ !eq(np->d_name, "mba"))
+ continue;
+ fprintf(fp, "\t{ &%sdriver, %s",
+ mp->d_name, qu(mp->d_unit));
+ fprintf(fp, ", %2d, %s },\n",
+ dp->d_unit, qu(dp->d_slave));
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ }
+ /*
+ * Now generate interrupt vectors for the unibus
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_vec != 0) {
+ struct idlst *ip;
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ (!eq(mp->d_name, "uba") && !eq(mp->d_name, "bi")))
+ continue;
+ fprintf(fp,
+ "extern struct uba_driver %sdriver;\n",
+ dp->d_name);
+ fprintf(fp, "extern ");
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ";\n");
+ fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
+ dp->d_unit);
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ", 0 } ;\n");
+ }
+ }
+ fprintf(fp, "\nstruct uba_ctlr ubminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\tubanum,\talive,\tintr,\taddr */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "uba"))
+ continue;
+ if (dp->d_vec == 0) {
+ printf("must specify vector for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr address for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; dont ");
+ printf("specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf("controllers (e.g. %s%d) ",
+ dp->d_name, dp->d_unit);
+ printf("don't have flags, only devices do\n");
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0%o },\n",
+ dp->d_name, dp->d_unit, qu(mp->d_unit),
+ dp->d_name, dp->d_unit, dp->d_addr);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* unibus devices */
+ fprintf(fp, "\nstruct uba_device ubdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, ubanum, slave, intr, addr, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || mp->d_type == MASTER ||
+ eq(mp->d_name, "mba"))
+ continue;
+ np = mp->d_conn;
+ if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
+ continue;
+ np = 0;
+ if (eq(mp->d_name, "uba")) {
+ if (dp->d_vec == 0) {
+ printf("must specify vector for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified ");
+ printf("only for controllers, ");
+ printf("not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ uba_n = mp->d_unit;
+ slave = QUES;
+ } else {
+ if ((np = mp->d_conn) == 0) {
+ printf("%s%d isn't connected to anything ",
+ mp->d_name, mp->d_unit);
+ printf(", so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ uba_n = np->d_unit;
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' ");
+ printf("for %s%d\n", dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("interrupt vectors should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only ");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp, "\t{ &%sdriver, %2d, %s,",
+ eq(mp->d_name, "uba") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "uba") ? " -1" : qu(mp->d_unit));
+ fprintf(fp, " %s, %2d, %s, C 0%-6o, %d, 0x%x },\n",
+ qu(uba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_TAHOE
+void
+tahoe_ioconf()
+{
+ register struct device *dp, *mp, *np;
+ register int vba_n, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <tahoe/include/pte.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <tahoe/vba/vbavar.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ /*
+ * Now generate interrupt vectors for the versabus
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS || !eq(mp->d_name, "vba"))
+ continue;
+ if (dp->d_vec != 0) {
+ struct idlst *ip;
+ fprintf(fp,
+ "extern struct vba_driver %sdriver;\n",
+ dp->d_name);
+ fprintf(fp, "extern ");
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ";\n");
+ fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
+ dp->d_unit);
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ", 0 } ;\n");
+ } else if (dp->d_type == DRIVER) /* devices w/o interrupts */
+ fprintf(fp,
+ "extern struct vba_driver %sdriver;\n",
+ dp->d_name);
+ }
+ fprintf(fp, "\nstruct vba_ctlr vbminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\tvbanum,\talive,\tintr,\taddr */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "vba"))
+ continue;
+ if (dp->d_vec == 0) {
+ printf("must specify vector for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr address for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; dont ");
+ printf("specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf("controllers (e.g. %s%d) ",
+ dp->d_name, dp->d_unit);
+ printf("don't have flags, only devices do\n");
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0x%x },\n",
+ dp->d_name, dp->d_unit, qu(mp->d_unit),
+ dp->d_name, dp->d_unit, dp->d_addr);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* versabus devices */
+ fprintf(fp, "\nstruct vba_device vbdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, vbanum, slave, intr, addr, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || mp->d_type == MASTER ||
+ eq(mp->d_name, "mba"))
+ continue;
+ np = mp->d_conn;
+ if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
+ continue;
+ np = 0;
+ if (eq(mp->d_name, "vba")) {
+ if (dp->d_vec == 0)
+ printf(
+ "Warning, no interrupt vector specified for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ if (dp->d_addr == 0) {
+ printf("must specify csr for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified ");
+ printf("only for controllers, ");
+ printf("not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ vba_n = mp->d_unit;
+ slave = QUES;
+ } else {
+ if ((np = mp->d_conn) == 0) {
+ printf("%s%d isn't connected to anything ",
+ mp->d_name, mp->d_unit);
+ printf(", so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ vba_n = np->d_unit;
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' ");
+ printf("for %s%d\n", dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("interrupt vectors should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only ");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp, "\t{ &%sdriver, %2d, %s,",
+ eq(mp->d_name, "vba") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "vba") ? " -1" : qu(mp->d_unit));
+ fprintf(fp, " %s, %2d, %s, C 0x%-6x, %d, 0x%x },\n",
+ qu(vba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_HP300 || MACHINE_LUNA68K
+void
+hp300_ioconf()
+{
+ register struct device *dp, *mp;
+ register int hpib, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ if (machine == MACHINE_HP300)
+ fprintf(fp, "#include <hp/dev/device.h>\n\n");
+ else
+ fprintf(fp, "#include <luna68k/dev/device.h>\n\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n");
+ fprintf(fp, "#define D (struct driver *)\n\n");
+ /*
+ * First print the hpib controller initialization structures
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0)
+ continue;
+ fprintf(fp, "extern struct driver %sdriver;\n", dp->d_name);
+ }
+ fprintf(fp, "\nstruct hp_ctlr hp_cinit[] = {\n");
+ fprintf(fp, "/*\tdriver,\t\tunit,\talive,\taddr,\tflags */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES ||
+ (dp->d_type != MASTER && dp->d_type != CONTROLLER))
+ continue;
+ if (mp != TO_NEXUS) {
+ printf("%s%s must be attached to an sc (nexus)\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t0,\tC 0x%x,\t0x%x },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* devices */
+ fprintf(fp, "\nstruct hp_device hp_dinit[] = {\n");
+ fprintf(fp,
+ "/*driver,\tcdriver,\tunit,\tctlr,\tslave,\taddr,\tdk,\tflags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || dp->d_type != DEVICE || hpbadslave(mp, dp))
+ continue;
+ if (mp == TO_NEXUS) {
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = QUES;
+ hpib = QUES;
+ } else {
+ if (dp->d_addr != 0) {
+ printf("can't specify sc for device %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (mp->d_type == CONTROLLER) {
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify drive for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = dp->d_drive;
+ } else {
+ if (dp->d_slave == UNKNOWN) {
+ printf("must specify slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = dp->d_slave;
+ }
+ hpib = mp->d_unit;
+ }
+ fprintf(fp, "{ &%sdriver,\t", dp->d_name);
+ if (mp == TO_NEXUS)
+ fprintf(fp, "D 0x0,\t");
+ else
+ fprintf(fp, "&%sdriver,", mp->d_name);
+ fprintf(fp, "\t%d,\t%d,\t%d,\tC 0x%x,\t%d,\t0x%x },\n",
+ dp->d_unit, hpib, slave,
+ dp->d_addr, dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+
+#define ishpibdev(n) (eq(n,"rd") || eq(n,"ct") || eq(n,"mt") || eq(n,"ppi"))
+#define isscsidev(n) (eq(n,"sd") || eq(n,"st") || eq(n,"ac"))
+
+int
+hpbadslave(mp, dp)
+ register struct device *dp, *mp;
+{
+
+ if ((mp == TO_NEXUS && ishpibdev(dp->d_name)) ||
+ (mp != TO_NEXUS && eq(mp->d_name, "hpib") &&
+ !ishpibdev(dp->d_name))) {
+ printf("%s%s must be attached to an hpib\n",
+ dp->d_name, wnum(dp->d_unit));
+ return (1);
+ }
+ if ((mp == TO_NEXUS && isscsidev(dp->d_name)) ||
+ (mp != TO_NEXUS && eq(mp->d_name, "scsi") &&
+ !isscsidev(dp->d_name))) {
+ printf("%s%s must be attached to a scsi\n",
+ dp->d_name, wnum(dp->d_unit));
+ return (1);
+ }
+ return (0);
+}
+#endif
+
+#if MACHINE_I386
+char *sirq();
+
+void
+i386_ioconf()
+{
+ register struct device *dp, *mp;
+ int dev_id;
+ FILE *fp;
+ static char *old_d_name;
+ int count;
+
+ fp = fopen(path("ioconf.c.new"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c.new"));
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * I/O configuration.\n");
+ fprintf(fp, " * DO NOT EDIT-- this file is automatically generated.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n");
+ /*
+ * First print the isa initialization structures
+ */
+ if (seen_isa) {
+ int seen_wdc = 0, seen_fdc = 0;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * ISA devices.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <i386/isa/icu.h>\n");
+ if (machine == MACHINE_I386)
+ fprintf(fp, "#include <i386/isa/isa.h>\n");
+ else
+ fprintf(fp, "#include <pc98/pc98/pc98.h>\n");
+ fprintf(fp, "#include <i386/isa/isa_device.h>\n");
+ fprintf(fp, "\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "isa"))
+ continue;
+ if (old_d_name == NULL || !eq(dp->d_name, old_d_name)) {
+ old_d_name = dp->d_name;
+ fprintf(fp,
+ "extern struct isa_driver %3sdriver;\n",
+ old_d_name);
+ }
+ if (eq(dp->d_name, "wdc"))
+ seen_wdc++;
+ if (eq(dp->d_name, "fdc"))
+ seen_fdc++;
+ if ((dp->d_irq == 2) && (machine == MACHINE_I386)) {
+ fprintf(stderr,
+ "remapped irq 2 to irq 9, please update your config file\n");
+ dp->d_irq = 9;
+ }
+ }
+ dev_id = 10; /* XXX must match mkglue.c */
+ isa_devtab(fp, "bio", &dev_id);
+ if (seen_wdc)
+ isa_biotab(fp, "wdc");
+ if (seen_fdc)
+ isa_biotab(fp, "fdc");
+ isa_devtab(fp, "tty", &dev_id);
+ isa_devtab(fp, "net", &dev_id);
+ isa_devtab(fp, "cam", &dev_id);
+ isa_devtab(fp, "ha", &dev_id);
+ isa_devtab(fp, "null", &dev_id);
+ }
+ if (seen_scbus)
+ scbus_devtab(fp, &dev_id);
+
+ fprintf(fp, "\n");
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * New bus architecture devices.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <sys/queue.h>\n");
+ fprintf(fp, "#include <sys/bus_private.h>\n");
+ fprintf(fp, "\n");
+ count = 0;
+ fprintf(fp, "struct config_device devtab[] = {\n");
+ fprintf(fp, "/* name, unit, resource count, resources */\n");
+ fprintf(fp, "{ 0, 0, 0, 0 }\n");
+ fprintf(fp, "};\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "int devtab_count = %d;\n", count);
+
+ (void) fclose(fp);
+ moveifchanged(path("ioconf.c.new"), path("ioconf.c"));
+}
+
+void
+isa_biotab(fp, table)
+ FILE *fp;
+ char *table;
+{
+ register struct device *dp, *mp;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct isa_device isa_biotab_%s[] = {\n", table);
+ fprintf(fp, "\
+/* id driver iobase irq drq maddr msiz intr unit flags drive alive ri_flags reconfig enabled conflicts next */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0 ||
+ mp == TO_NEXUS || !eq(mp->d_name, table))
+ continue;
+ fprintf(fp, "{ -1, &%3sdriver, ",
+ mp->d_name);
+ if (mp->d_port)
+ fprintf(fp, " %8s,", mp->d_port);
+ else if (mp->d_portn == -1)
+ fprintf(fp, " %d,", mp->d_portn);
+ else
+ fprintf(fp, " 0x%04x,", mp->d_portn);
+ fprintf(fp, "%6s, %2d, C 0x%05X, %5d, {0}, %3d, 0x%04X, %5d, 0, 0, 0, %6d, %8d, 0 },\n",
+ sirq(mp->d_irq), mp->d_drq, mp->d_maddr,
+ mp->d_msize, dp->d_unit,
+ dp->d_flags, dp->d_drive, !dp->d_disabled,
+ dp->d_conflicts);
+ }
+ fprintf(fp, "{ 0 }\n};\n");
+}
+
+/*
+ * Generized routine for isa bus device table, instead of repeating
+ * all this 4 times, call this with the table argument.
+ *
+ * 4/26/93 rgrimes
+ */
+void
+isa_devtab(fp, table, dev_idp)
+ FILE *fp;
+ char *table;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct isa_device isa_devtab_%s[] = {\n", table);
+ fprintf(fp, "\
+/* id driver iobase irq drq maddr msiz intr unit flags scsiid alive ri_flags reconfig enabled conflicts next */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_unit == QUES || !eq(dp->d_mask, table))
+ continue;
+ mp = dp->d_conn;
+ if (mp == NULL || mp == TO_NEXUS || !eq(mp->d_name, "isa"))
+ continue;
+ fprintf(fp, "{ %2d, &%3sdriver,", (*dev_idp)++, dp->d_name);
+ if (dp->d_port)
+ fprintf(fp, " %8s,", dp->d_port);
+ else if (dp->d_portn == -1)
+ fprintf(fp, " %d,", dp->d_portn);
+ else
+ fprintf(fp, " 0x%04x,", dp->d_portn);
+ fprintf(fp, "%6s, %2d, C 0x%05X, %5d, {0}, %3d, 0x%04X, 0, 0, 0, 0, %6d, %8d, 0 },\n",
+ sirq(dp->d_irq), dp->d_drq, dp->d_maddr,
+ dp->d_msize, dp->d_unit,
+ dp->d_flags, !dp->d_disabled, dp->d_conflicts);
+ }
+ fprintf(fp, "{ 0 }\n};\n");
+}
+
+static char *
+id(int unit)
+{
+ char *s;
+ switch(unit)
+ {
+ case UNKNOWN:
+ s ="CAMCONF_UNSPEC";
+ break;;
+
+ case QUES:
+ s ="CAMCONF_ANY";
+ break;
+
+ default:
+ s = qu(unit);
+ }
+
+ return s;
+}
+
+static void id_put(fp, unit, s)
+ FILE *fp;
+ int unit;
+ char *s;
+{
+ fprintf(fp, "%s%s", id(unit), s);
+}
+
+/* XXX: dufault@hda.com: wiped out mkioconf.c locally:
+ * All that nice "conflicting SCSI ID checking" is now
+ * lost and should be put back in.
+ */
+void
+scbus_devtab(fp, dev_idp)
+ FILE *fp;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * CAM devices.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <cam/cam_conf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "struct cam_sim_config cam_sinit[] = {\n");
+ fprintf(fp, "/* pathid, sim name, sim unit, sim bus */\n");
+
+ /* XXX: Why do we always get an entry such as:
+ * { '?', "ncr", '?', '?' },
+ */
+
+ for (dp = dtab; dp; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(dp->d_name, "scbus")) {
+ continue;
+ }
+ fprintf(fp, "{ %s, ", id(dp->d_unit));
+ fprintf(fp, "\"%s\", ", mp->d_name);
+ fprintf(fp, "%s, ", id(mp->d_unit));
+ fprintf(fp, "%s },\n", id(dp->d_slave));
+ }
+ fprintf(fp, "{ 0, 0, 0, 0 }\n");
+ fprintf(fp, "};\n");
+
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct cam_periph_config cam_pinit[] = {\n");
+ fprintf(fp,
+"/* periph name, periph unit, pathid, target, LUN, flags */\n");
+ for (dp = dtab; dp; dp = dp->d_next) {
+ if (dp->d_type == CONTROLLER || dp->d_type == MASTER ||
+ dp->d_type == PSEUDO_DEVICE)
+ continue;
+
+ mp = dp->d_conn;
+ if (mp == 0 || !eq(mp->d_name, "scbus")) {
+ continue;
+ }
+
+ if (mp->d_conn == 0 &&
+ (dp->d_target != UNKNOWN && dp->d_target != QUES)) {
+ fprintf(stderr,
+ "Warning: %s%s is configured at ",
+ dp->d_name, wnum(dp->d_unit));
+
+ fprintf(stderr,
+ "%s%s which is not fixed at a single adapter.\n",
+ mp->d_name, wnum(mp->d_unit));
+ }
+
+ fprintf(fp, "{ ");
+ fprintf(fp, "\"%s\", ", dp->d_name);
+ id_put(fp, dp->d_unit, ", ");
+ id_put(fp, mp->d_unit, ", ");
+ id_put(fp, dp->d_target, ", ");
+ id_put(fp, dp->d_lun, ", ");
+ fprintf(fp, " 0x%x },\n", dp->d_flags);
+ }
+ fprintf(fp, "{ 0, 0, 0, 0, 0, 0 }\n");
+ fprintf(fp, "};\n");
+}
+
+/*
+ * XXX - there should be a general function to print devtabs instead of these
+ * little pieces of it.
+ */
+
+char *
+sirq(num)
+{
+
+ if (num == -1)
+ return ("0");
+ sprintf(errbuf, "IRQ%d", num);
+ return (errbuf);
+}
+#endif
+
+#if MACHINE_PMAX
+void
+pmax_ioconf()
+{
+ register struct device *dp, *mp;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/types.h>\n");
+ fprintf(fp, "#include <sys/time.h>\n");
+ fprintf(fp, "#include <pmax/dev/device.h>\n\n");
+ fprintf(fp, "#define C (char *)\n\n");
+
+ /* print controller initialization structures */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type == PSEUDO_DEVICE)
+ continue;
+ fprintf(fp, "extern struct driver %sdriver;\n", dp->d_name);
+ }
+ fprintf(fp, "\nstruct pmax_ctlr pmax_cinit[] = {\n");
+ fprintf(fp, "/*\tdriver,\t\tunit,\taddr,\t\tpri,\tflags */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER)
+ continue;
+ if (dp->d_conn != TO_NEXUS) {
+ printf("%s%s must be attached to a nexus (internal bus)\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_unit == UNKNOWN || dp->d_unit == QUES)
+ dp->d_unit = 0;
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\tC 0x%x,\t%d,\t0x%x },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_pri,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+
+ /* print devices connected to other controllers */
+ fprintf(fp, "\nstruct scsi_device scsi_dinit[] = {\n");
+ fprintf(fp,
+ "/*driver,\tcdriver,\tunit,\tctlr,\tdrive,\tslave,\tdk,\tflags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type == CONTROLLER || dp->d_type == MASTER ||
+ dp->d_type == PSEUDO_DEVICE)
+ continue;
+ mp = dp->d_conn;
+ if (mp == 0 ||
+ (!eq(mp->d_name, "asc") && !eq(mp->d_name, "sii"))) {
+ printf("%s%s: devices must be attached to a SCSI (asc or sii) controller\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if ((unsigned)dp->d_drive > 6) {
+ printf("%s%s: SCSI drive must be in the range 0..6\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ /* may want to allow QUES later */
+ if ((unsigned)dp->d_slave > 7) {
+ printf("%s%s: SCSI slave (LUN) must be in the range 0..7\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ fprintf(fp, "{ &%sdriver,\t&%sdriver,", dp->d_name, mp->d_name);
+ fprintf(fp, "\t%d,\t%d,\t%d,\t%d,\t%d,\t0x%x },\n",
+ dp->d_unit, mp->d_unit, dp->d_drive, dp->d_slave,
+ dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_NEWS3400
+int have_iop = 0;
+int have_hb = 0;
+int have_vme = 0;
+
+void
+news_ioconf()
+{
+ register struct device *dp, *mp;
+ register int slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "#include <vm/vm.h>\n");
+ fprintf(fp, "#include \"iop.h\"\n");
+ fprintf(fp, "#include \"hb.h\"\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#if NIOP > 0\n");
+ fprintf(fp, "#include <news3400/iop/iopvar.h>\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "#if NHB > 0\n");
+ fprintf(fp, "#include <news3400/hbdev/hbvar.h>\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ fprintf(fp, "\n");
+
+/* BEGIN HB */
+ fprintf(fp, "#if NHB > 0\n");
+ /*
+ * Now generate interrupt vectors for the HYPER-BUS
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_pri >= 0) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "hb"))
+ continue;
+ fprintf(fp, "extern struct hb_driver %sdriver;\n",
+ dp->d_name);
+ have_hb++;
+ }
+ }
+ /*
+ * Now spew forth the hb_cinfo structure
+ */
+ fprintf(fp, "\nstruct hb_ctlr hminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\talive,\taddr,\tintpri */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if ((dp->d_type != MASTER && dp->d_type != CONTROLLER)
+ || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "hb"))
+ continue;
+ if (dp->d_pri < 0) {
+ printf("must specify priority for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; ");
+ printf("dont specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf(
+ "controllers (e.g. %s%d) don't have flags, only devices do\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ fprintf(fp, "\t{ &%sdriver,\t%d,\t0,\tC 0x%x,\t%d },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_pri);
+ }
+ fprintf(fp, "\t0\n};\n");
+ /*
+ * Now we go for the hb_device stuff
+ */
+ fprintf(fp, "\nstruct hb_device hdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, slave, addr, pri, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || /* mp->d_type == MASTER || */
+ eq(mp->d_name, "iop") || eq(mp->d_name, "vme"))
+ continue;
+ if (eq(mp->d_name, "hb")) {
+ if (dp->d_pri < 0) {
+ printf("must specify vector for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified only ");
+ printf("for controllers, not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = QUES;
+ } else {
+ if (mp->d_conn == 0) {
+ printf("%s%d isn't connected to anything, ",
+ mp->d_name, mp->d_unit);
+ printf("so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE IOP ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_pri >= 0) {
+ printf("interrupt priority should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp,
+"\t{ &%sdriver, %2d, %s, %2d, C 0x%x, %d, %d, 0x%x },\n",
+ eq(mp->d_name, "hb") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "hb") ? " -1" : qu(mp->d_unit),
+ slave, dp->d_addr, dp->d_pri, dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ fprintf(fp, "#endif\n\n");
+/* END HB */
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_ALPHA
+static char *
+devstr(struct device *dp)
+{
+ static char buf[100];
+
+ if (dp->d_unit >= 0) {
+ sprintf(buf, "%s%d", dp->d_name, dp->d_unit);
+ return buf;
+ } else
+ return dp->d_name;
+}
+
+static void
+write_device_resources(FILE *fp, struct device *dp)
+{
+ int count = 0;
+
+ fprintf(fp, "struct config_resource %s_resources[] = {\n", devstr(dp));
+ if (dp->d_conn) {
+ fprintf(fp, "\t\"at\",\tRES_STRING,\t(long)\"%s\",\n",
+ devstr(dp->d_conn));
+ count++;
+ }
+ if (dp->d_drive != -2) {
+ fprintf(fp, "\t\"drive\",\tRES_INT,\t%d,\n", dp->d_drive);
+ count++;
+ }
+ if (dp->d_target != -2) {
+ fprintf(fp, "\t\"target\",\tRES_INT,\t%d,\n", dp->d_target);
+ count++;
+ }
+ if (dp->d_lun != -2) {
+ fprintf(fp, "\t\"lun\",\tRES_INT,\t%d,\n", dp->d_lun);
+ count++;
+ }
+ if (dp->d_flags) {
+ fprintf(fp, "\t\"flags\",\tRES_INT,\t0x%x,\n", dp->d_flags);
+ count++;
+ }
+ if (dp->d_conflicts) {
+ fprintf(fp, "\t\"conflicts\",\tRES_INT,\t%d,\n", dp->d_conflicts);
+ count++;
+ }
+ if (dp->d_disabled) {
+ fprintf(fp, "\t\"disabled\",\tRES_INT,\t%d,\n", dp->d_disabled);
+ count++;
+ }
+ if (dp->d_port) {
+ fprintf(fp, "\t\"port\",\tRES_INT,\t%s,\n", dp->d_port);
+ count++;
+ }
+ if (dp->d_portn > 0) {
+ fprintf(fp, "\t\"port\",\tRES_INT,\t0x%x,\n", dp->d_portn);
+ count++;
+ }
+ if (dp->d_maddr > 0) {
+ fprintf(fp, "\t\"maddr\",\tRES_INT,\t0x%x,\n", dp->d_maddr);
+ count++;
+ }
+ if (dp->d_msize > 0) {
+ fprintf(fp, "\t\"msize\",\tRES_INT,\t0x%x,\n", dp->d_msize);
+ count++;
+ }
+ if (dp->d_drq > 0) {
+ fprintf(fp, "\t\"drq\",\tRES_INT,\t%d,\n", dp->d_drq);
+ count++;
+ }
+ if (dp->d_irq > 0) {
+ fprintf(fp, "\t\"irq\",\tRES_INT,\t%d,\n", dp->d_irq);
+ count++;
+ }
+ fprintf(fp, "};\n");
+ fprintf(fp, "#define %s_count %d\n", devstr(dp), count);
+}
+
+void
+alpha_ioconf()
+{
+ register struct device *dp, *mp;
+ FILE *fp;
+ int dev_id = 10;
+ int count;
+
+ fp = fopen(path("ioconf.c.new"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/types.h>\n");
+ fprintf(fp, "#include <sys/time.h>\n");
+ fprintf(fp, "#include <sys/queue.h>\n\n");
+ fprintf(fp, "#include <sys/bus_private.h>\n");
+ fprintf(fp, "#include <isa/isareg.h>\n\n");
+ fprintf(fp, "#define C (char *)\n\n");
+
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER
+ && dp->d_type != DEVICE)
+ continue;
+ write_device_resources(fp, dp);
+ }
+ count = 0;
+ fprintf(fp, "struct config_device devtab[] = {\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ char* n = devstr(dp);
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER
+ && dp->d_type != DEVICE)
+ continue;
+ fprintf(fp, "\t\"%s\",\t%d,\t%s_count,\t%s_resources,\n",
+ dp->d_name, dp->d_unit, n, n);
+ count++;
+ }
+ fprintf(fp, "};\n");
+ fprintf(fp, "int devtab_count = %d;\n", count);
+
+ if (seen_scbus)
+ scbus_devtab(fp, &dev_id);
+
+ (void) fclose(fp);
+ moveifchanged(path("ioconf.c.new"), path("ioconf.c"));
+}
+
+#endif
+
+char *
+intv(dev)
+ register struct device *dev;
+{
+ static char buf[20];
+
+ if (dev->d_vec == 0)
+ return (" 0");
+ (void) sprintf(buf, "%sint%d", dev->d_name, dev->d_unit);
+ return (buf);
+}
+
+char *
+qu(num)
+{
+
+ if (num == QUES)
+ return ("'?'");
+ if (num == UNKNOWN)
+ return (" -1");
+ (void) sprintf(errbuf, "%3d", num);
+ return (errbuf);
+}
+
+char *
+wnum(num)
+{
+
+ if (num == QUES || num == UNKNOWN)
+ return ("?");
+ (void) sprintf(errbuf, "%d", num);
+ return (errbuf);
+}
+
+void
+pseudo_ioconf(fp)
+ register FILE *fp;
+{
+ register struct device *dp;
+
+ (void)fprintf(fp, "\n#include <sys/device.h>\n\n");
+ for (dp = dtab; dp != NULL; dp = dp->d_next)
+ if (dp->d_type == PSEUDO_DEVICE)
+ (void)fprintf(fp, "extern void %sattach __P((int));\n",
+ dp->d_name);
+ /*
+ * XXX concatonated disks are pseudo-devices but appear as DEVICEs
+ * since they don't adhere to normal pseudo-device conventions
+ * (i.e. one entry with total count in d_slave).
+ */
+ if (seen_cd)
+ (void)fprintf(fp, "extern void cdattach __P((int));\n");
+ /* XXX temporary for HP300, others */
+ (void)fprintf(fp, "\n#include <sys/systm.h> /* XXX */\n");
+ (void)fprintf(fp, "#define etherattach (void (*)__P((int)))nullop\n");
+ (void)fprintf(fp, "#define iteattach (void (*) __P((int)))nullop\n");
+ (void)fprintf(fp, "\nstruct pdevinit pdevinit[] = {\n");
+ for (dp = dtab; dp != NULL; dp = dp->d_next)
+ if (dp->d_type == PSEUDO_DEVICE)
+ (void)fprintf(fp, "\t{ %sattach, %d },\n", dp->d_name,
+ dp->d_slave > 0 ? dp->d_slave : 1);
+ /*
+ * XXX count up cds and put out an entry
+ */
+ if (seen_cd) {
+ struct file_list *fl;
+ int cdmax = -1;
+
+ for (fl = comp_list; fl != NULL; fl = fl->f_next)
+ if (fl->f_type == COMPDEVICE && fl->f_compinfo > cdmax)
+ cdmax = fl->f_compinfo;
+ (void)fprintf(fp, "\t{ cdattach, %d },\n", cdmax+1);
+ }
+ (void)fprintf(fp, "\t{ 0, 0 }\n};\n");
+ if (seen_cd)
+ comp_config(fp);
+}
+
+void
+comp_config(fp)
+ FILE *fp;
+{
+ register struct file_list *fl;
+ register struct device *dp;
+
+ fprintf(fp, "\n#include <dev/cdvar.h>\n");
+ fprintf(fp, "\nstruct cddevice cddevice[] = {\n");
+ fprintf(fp, "/*\tunit\tileave\tflags\tdk\tdevs\t\t\t\t*/\n");
+
+ fl = comp_list;
+ while (fl) {
+ if (fl->f_type != COMPDEVICE) {
+ fl = fl->f_next;
+ continue;
+ }
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if (dp->d_type == DEVICE &&
+ eq(dp->d_name, fl->f_fn) &&
+ dp->d_unit == fl->f_compinfo)
+ break;
+ if (dp == 0)
+ continue;
+ fprintf(fp, "\t%d,\t%d,\t%d,\t%d,\t{",
+ dp->d_unit, dp->d_pri < 0 ? 0 : dp->d_pri,
+ dp->d_flags, 1);
+ for (fl = fl->f_next; fl->f_type == COMPSPEC; fl = fl->f_next)
+ fprintf(fp, " 0x%x,", fl->f_compdev);
+ fprintf(fp, " NODEV },\n");
+ }
+ fprintf(fp, "\t-1,\t0,\t0,\t0,\t{ 0 },\n};\n");
+}
diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c
new file mode 100644
index 0000000..ea4c405
--- /dev/null
+++ b/usr.sbin/config/mkmakefile.c
@@ -0,0 +1,867 @@
+/*
+ * 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[] =
+ "$Id: mkmakefile.c,v 1.33 1998/09/03 21:03:43 nsouch Exp $";
+#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 "y.tab.h"
+#include "config.h"
+#include "configvers.h"
+
+#define next_word(fp, wd) \
+ { register char *word = get_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+#define next_quoted_word(fp, wd) \
+ { register char *word = get_quoted_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+
+#define ns(s) strdup(s)
+
+static struct file_list *fcur;
+char *tail();
+extern int old_config_present;
+
+void do_swapspec __P((FILE *, char *));
+void do_clean __P((FILE *));
+void do_load __P((FILE *));
+void do_rules __P((FILE *));
+void do_sfiles __P((FILE *));
+void do_mfiles __P((FILE *));
+void do_cfiles __P((FILE *));
+void do_objs __P((FILE *));
+void do_before_depend __P((FILE *));
+int opteq __P((char *, char *));
+void read_files __P((void));
+void makefile __P((void));
+
+/*
+ * Lookup a file, by name.
+ */
+struct file_list *
+fl_lookup(file)
+ register char *file;
+{
+ register struct file_list *fp;
+
+ for (fp = ftab ; fp != 0; fp = fp->f_next) {
+ if (eq(fp->f_fn, file))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Lookup a file, by final component name.
+ */
+struct file_list *
+fltail_lookup(file)
+ register char *file;
+{
+ register struct file_list *fp;
+
+ for (fp = ftab ; fp != 0; fp = fp->f_next) {
+ if (eq(tail(fp->f_fn), tail(file)))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Make a new file list entry
+ */
+struct file_list *
+new_fent()
+{
+ register struct file_list *fp;
+
+ fp = (struct file_list *) malloc(sizeof *fp);
+ bzero(fp, sizeof *fp);
+ if (fcur == 0)
+ fcur = ftab = fp;
+ else
+ fcur->f_next = fp;
+ fcur = fp;
+ return (fp);
+}
+
+/*
+ * Build the makefile from the skeleton
+ */
+void
+makefile()
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ struct opt *op;
+ int warn_make_clean = 0;
+ int versreq;
+
+ read_files();
+ strcpy(line, "Makefile.");
+ (void) strcat(line, machinename);
+ ifp = fopen(line, "r");
+ if (ifp == 0)
+ err(1, "%s", line);
+ ofp = fopen(path("Makefile.new"), "w");
+ if (ofp == 0)
+ err(1, "%s", path("Makefile.new"));
+ fprintf(ofp, "KERN_IDENT=%s\n", raise(ident));
+ fprintf(ofp, "IDENT=");
+ if (profiling)
+ fprintf(ofp, " -DGPROF");
+
+ if (cputype == 0) {
+ printf("cpu type must be specified\n");
+ exit(1);
+ }
+ for (op = opt; op; op = op->op_next) {
+ if (!op->op_ownfile) {
+ warn_make_clean++;
+ if (op->op_value)
+ fprintf(ofp, " -D%s=%s", op->op_name, op->op_value);
+ else
+ fprintf(ofp, " -D%s", op->op_name);
+ printf("%s:%d: unknown option \"%s\"\n",
+ PREFIX, op->op_line, op->op_name);
+ }
+ }
+ fprintf(ofp, "\n");
+ if (loadaddress != -1) {
+ fprintf(ofp, "LOAD_ADDRESS=%X\n", loadaddress);
+ }
+ for (op = mkopt; op; op = op->op_next)
+ fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
+ if (debugging)
+ fprintf(ofp, "DEBUG=-g\n");
+ if (profiling) {
+ fprintf(ofp, "PROF=-pg\n");
+ fprintf(ofp, "PROFLEVEL=%d\n", profiling);
+ }
+ 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 (eq(line, "%MFILES\n"))
+ do_mfiles(ofp);
+ else if (eq(line, "%CFILES\n"))
+ do_cfiles(ofp);
+ else if (eq(line, "%SFILES\n"))
+ do_sfiles(ofp);
+ else if (eq(line, "%RULES\n"))
+ do_rules(ofp);
+ else if (eq(line, "%LOAD\n"))
+ do_load(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, "WARNING: 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");
+ }
+ } else
+ fprintf(stderr,
+ "Unknown %% construct in generic makefile: %s",
+ line);
+ }
+ (void) fclose(ifp);
+ (void) fclose(ofp);
+ moveifchanged(path("Makefile.new"), path("Makefile"));
+
+ if (warn_make_clean != 0 && old_config_present) {
+ printf(
+ "Unknown option%s used - it is VERY important that you do\n",
+ (warn_make_clean > 1 ? "s" : ""));
+ printf(" make clean && make depend\n");
+ printf("before recompiling\n");
+ } else
+ printf("Don't forget to do a ``make depend''\n");
+}
+
+/*
+ * Read in the information about files used in making the system.
+ * Store it in the ftab linked list.
+ */
+void
+read_files()
+{
+ FILE *fp;
+ register struct file_list *tp, *pf;
+ register struct device *dp;
+ struct device *save_dp;
+ register struct opt *op;
+ char *wd, *this, *needs, *special, *depends, *clean;
+ char fname[80];
+ int nreqs, first = 1, configdep, isdup, std, filetype,
+ imp_rule, no_obj, before_depend, mandatory;
+
+ ftab = 0;
+ (void) snprintf(fname, sizeof fname, "../../conf/files");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0)
+ err(1, "%s", fname);
+ if(ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+next:
+ /*
+ * filename [ standard | mandatory | optional ] [ config-dependent ]
+ * [ dev* | profiling-routine ] [ device-driver] [ no-obj ]
+ * [ compile-with "compile rule" [no-implicit-rule] ]
+ * [ dependency "dependency-list"] [ before-depend ]
+ * [ clean "file-list"]
+ */
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ (void) snprintf(fname, sizeof fname, "files.%s", machinename);
+ first++;
+ goto openit;
+ }
+ if (first == 2) {
+ (void) snprintf(fname, sizeof fname, "files.%s", raise(ident));
+ first++;
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ /*************************************************\
+ * If it's a comment ignore to the end of the line *
+ \*************************************************/
+ 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 = 1;
+ else
+ isdup = 0;
+ tp = 0;
+ if (first == 3 && (tp = fltail_lookup(this)) != 0)
+ printf("%s: Local file %s overrides %s.\n",
+ fname, this, tp->f_fn);
+ nreqs = 0;
+ special = 0;
+ depends = 0;
+ clean = 0;
+ configdep = 0;
+ needs = 0;
+ std = mandatory = 0;
+ imp_rule = 0;
+ no_obj = 0;
+ before_depend = 0;
+ filetype = NORMAL;
+ if (eq(wd, "standard"))
+ std = 1;
+ /*
+ * If an entry is marked "mandatory", config will abort if it's
+ * not called by a configuration line in the config file. Apart
+ * from this, the device is handled like one marked "optional".
+ */
+ else if (eq(wd, "mandatory"))
+ mandatory = 1;
+ else if (!eq(wd, "optional")) {
+ printf("%s: %s must be optional, mandatory or standard\n",
+ fname, this);
+ exit(1);
+ }
+nextparam:
+ next_word(fp, wd);
+ if (wd == 0)
+ goto doneparam;
+ if (eq(wd, "config-dependent")) {
+ configdep++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-obj")) {
+ no_obj++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-implicit-rule")) {
+ if (special == 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);
+ }
+ special = 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, "device-driver")) {
+ filetype = DRIVER;
+ goto nextparam;
+ }
+ if (eq(wd, "profiling-routine")) {
+ filetype = PROFILING;
+ goto nextparam;
+ }
+ if (needs == 0 && nreqs == 1)
+ needs = ns(wd);
+ if (isdup)
+ goto invis;
+ for (dp = dtab; dp != 0; save_dp = dp, dp = dp->d_next)
+ if (eq(dp->d_name, wd)) {
+ if (std && dp->d_type == PSEUDO_DEVICE &&
+ dp->d_slave <= 0)
+ dp->d_slave = 1;
+ goto nextparam;
+ }
+ if (mandatory) {
+ printf("%s: mandatory device \"%s\" not found\n",
+ fname, wd);
+ exit(1);
+ }
+ if (std) {
+ dp = (struct device *) malloc(sizeof *dp);
+ bzero(dp, sizeof *dp);
+ init_dev(dp);
+ dp->d_name = ns(wd);
+ dp->d_type = PSEUDO_DEVICE;
+ dp->d_slave = 1;
+ save_dp->d_next = dp;
+ goto nextparam;
+ }
+ for (op = opt; op != 0; op = op->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;
+ tp->f_special = special;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ goto next;
+
+doneparam:
+ if (std == 0 && nreqs == 0) {
+ printf("%s: what is %s optional on?\n",
+ fname, this);
+ exit(1);
+ }
+
+save:
+ 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 = 0;
+ if (configdep)
+ tp->f_flags |= CONFIGDEP;
+ 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 (imp_rule)
+ tp->f_flags |= NO_IMPLCT_RULE;
+ if (no_obj)
+ tp->f_flags |= NO_OBJ;
+ tp->f_needs = needs;
+ tp->f_special = special;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ if (pf && pf->f_type == INVISIBLE)
+ pf->f_flags = 1; /* mark as duplicate */
+ goto next;
+}
+
+int
+opteq(cp, dp)
+ char *cp, *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);
+ }
+}
+
+void
+do_before_depend(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("BEFORE_DEPEND=", fp);
+ lpos = 15;
+ for (tp = ftab; tp; tp = tp->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);
+}
+
+void
+do_objs(fp)
+ FILE *fp;
+{
+ register struct file_list *tp, *fl;
+ register int lpos, len;
+ register char *cp, och, *sp;
+ char swapname[32];
+
+ fprintf(fp, "OBJS=");
+ lpos = 6;
+ for (tp = ftab; tp != 0; tp = tp->f_next) {
+ if (tp->f_type == INVISIBLE || tp->f_flags & NO_OBJ)
+ continue;
+ sp = tail(tp->f_fn);
+ for (fl = conf_list; fl; fl = fl->f_next) {
+ if (fl->f_type != SWAPSPEC)
+ continue;
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ if (eq(sp, swapname))
+ goto cont;
+ }
+ 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;
+cont:
+ ;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_cfiles(fp)
+ FILE *fp;
+{
+ register struct file_list *tp, *fl;
+ register int lpos, len;
+ char swapname[32];
+
+ fputs("CFILES=", fp);
+ lpos = 8;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_type != INVISIBLE && tp->f_type != NODEPEND) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - 1] != 'c')
+ 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;
+ }
+ for (fl = conf_list; fl; fl = fl->f_next)
+ if (fl->f_type == SYSTEMSPEC) {
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ if ((len = 3 + strlen(swapname)) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (eq(fl->f_fn, "generic"))
+ fprintf(fp, "$S/%s/%s/%s ",
+ machinename, machinename, swapname);
+ else
+ fprintf(fp, "%s ", swapname);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_mfiles(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("MFILES=", fp);
+ lpos = 8;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_type != INVISIBLE) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - 1] != 'm' || tp->f_fn[len - 2] != '.')
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_sfiles(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("SFILES=", fp);
+ lpos = 8;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_type != INVISIBLE) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - 1] != 'S' && tp->f_fn[len - 1] != 's')
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+
+char *
+tail(fn)
+ char *fn;
+{
+ register 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.
+ * Devices are processed with the special c2 option -i
+ * which avoids any problem areas with i/o addressing
+ * (e.g. for the VAX); assembler files are processed by as.
+ */
+void
+do_rules(f)
+ FILE *f;
+{
+ register char *cp, *np, och, *tp;
+ register struct file_list *ftp;
+ char *special;
+
+ for (ftp = ftab; ftp != 0; ftp = ftp->f_next) {
+ if (ftp->f_type == INVISIBLE)
+ continue;
+ 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, "%so: $S/%s%c %s\n", tail(np),
+ np, och, ftp->f_depends);
+ else
+ fprintf(f, "%so: $S/%s%c\n", tail(np),
+ np, och);
+ }
+ tp = tail(np);
+ special = ftp->f_special;
+ if (special == 0) {
+ char *ftype;
+ static char cmd[128];
+
+ switch (ftp->f_type) {
+
+ case NORMAL:
+ ftype = "NORMAL";
+ break;
+
+ case DRIVER:
+ ftype = "DRIVER";
+ break;
+
+ case PROFILING:
+ if (!profiling)
+ continue;
+ ftype = "PROFILE";
+ break;
+
+ default:
+ printf("config: don't know rules for %s\n", np);
+ break;
+ }
+ (void)sprintf(cmd, "${%s_%c%s}", ftype, toupper(och),
+ ftp->f_flags & CONFIGDEP? "_C" : "");
+ special = cmd;
+ }
+ *cp = och;
+ fprintf(f, "\t%s\n\n", special);
+ }
+}
+
+/*
+ * Create the load strings
+ */
+void
+do_load(f)
+ register FILE *f;
+{
+ register struct file_list *fl;
+ register int first;
+ struct file_list *do_systemspec();
+
+ for (first = 1, fl = conf_list; fl; first = 0)
+ fl = fl->f_type == SYSTEMSPEC ?
+ do_systemspec(f, fl, first) : fl->f_next;
+ fputs("all:", f);
+ for (fl = conf_list; fl; fl = fl->f_next)
+ if (fl->f_type == SYSTEMSPEC)
+ fprintf(f, " %s", fl->f_needs);
+ putc('\n', f);
+}
+
+void
+do_clean(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("CLEAN=", fp);
+ lpos = 7;
+ for (tp = ftab; tp; tp = tp->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);
+}
+
+struct file_list *
+do_systemspec(f, fl, first)
+ FILE *f;
+ register struct file_list *fl;
+ int first;
+{
+
+ fprintf(f, "%s: ${SYSTEM_DEP} swap%s.o", fl->f_needs, fl->f_fn);
+ if (first)
+ fprintf(f, " vers.o");
+ fprintf(f, "\n\t${SYSTEM_LD_HEAD}\n");
+ fprintf(f, "\t${SYSTEM_LD} swap%s.o\n", fl->f_fn);
+ fprintf(f, "\t${SYSTEM_LD_TAIL}\n\n");
+ do_swapspec(f, fl->f_fn);
+ for (fl = fl->f_next; fl; fl = fl->f_next)
+ if (fl->f_type != SWAPSPEC)
+ break;
+ return (fl);
+}
+
+void
+do_swapspec(f, name)
+ FILE *f;
+ register char *name;
+{
+
+ if (!eq(name, "generic"))
+ fprintf(f, "swap%s.o: swap%s.c\n", name, name);
+ else
+ fprintf(f, "swapgeneric.o: $S/%s/%s/swapgeneric.c\n",
+ machinename, machinename);
+ fprintf(f, "\t${NORMAL_C}\n\n");
+}
+
+char *
+raise(str)
+ register char *str;
+{
+ register 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..e638f20
--- /dev/null
+++ b/usr.sbin/config/mkoptions.c
@@ -0,0 +1,371 @@
+/*
+ * 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[] =
+ "$Id: mkoptions.c,v 1.7 1998/07/12 02:31:08 bde Exp $";
+#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 "config.h"
+#include "y.tab.h"
+
+#define ns(s) strdup(s)
+
+static struct users {
+ int u_default;
+ int u_min;
+ int u_max;
+} users[] = {
+ { 8, 2, 512 }, /* MACHINE_VAX */
+ { 8, 2, 512 }, /* MACHINE_TAHOE */
+ { 8, 2, 512 }, /* MACHINE_HP300 */
+ { 8, 2, 512 }, /* MACHINE_I386 */
+ { 8, 2, 512 }, /* MACHINE_MIPS */
+ { 8, 2, 512 }, /* MACHINE_PMAX */
+ { 8, 2, 512 }, /* MACHINE_LUNA68K */
+ { 8, 2, 512 }, /* MACHINE_NEWS3400 */
+ { 8, 2, 512 }, /* MACHINE_PC98 */
+ { 8, 2, 512 }, /* MACHINE_ALPHA */
+};
+#define NUSERS (sizeof (users) / sizeof (users[0]))
+
+static char *lower __P((char *));
+void read_options __P((void));
+void do_option __P((char *));
+
+void
+options()
+{
+ char buf[40];
+ struct cputype *cp;
+ struct opt_list *ol;
+ struct opt *op;
+ struct users *up;
+
+ /* Fake the cpu types as options. */
+ for (cp = cputype; cp != NULL; cp = cp->cpu_next) {
+ op = (struct opt *)malloc(sizeof(*op));
+ memset(op, 0, sizeof(*op));
+ op->op_name = ns(cp->cpu_name);
+ op->op_next = opt;
+ opt = op;
+ }
+
+ /* Initialize `maxusers'. */
+ if ((unsigned)machine > NUSERS) {
+ printf("maxusers config info isn't present, using vax\n");
+ up = &users[MACHINE_VAX - 1];
+ } else
+ up = &users[machine - 1];
+ if (maxusers == 0) {
+ printf("maxusers not specified; %d assumed\n", up->u_default);
+ maxusers = up->u_default;
+ } else if (maxusers < up->u_min) {
+ printf("minimum of %d maxusers assumed\n", up->u_min);
+ maxusers = up->u_min;
+ } else if (maxusers > up->u_max)
+ printf("warning: maxusers > %d (%d)\n", up->u_max, maxusers);
+
+ /* Fake MAXUSERS as an option. */
+ op = (struct opt *)malloc(sizeof(*op));
+ memset(op, 0, sizeof(*op));
+ op->op_name = "MAXUSERS";
+ sprintf(buf, "%d", maxusers);
+ op->op_value = ns(buf);
+ op->op_next = opt;
+ opt = op;
+
+ read_options();
+ for (ol = otab; ol != 0; ol = ol->o_next)
+ do_option(ol->o_name);
+}
+
+/*
+ * Generate an <options>.h file
+ */
+
+void
+do_option(name)
+ char *name;
+{
+ char *file, *inw, *tooption();
+ struct opt *op, *op_head, *topp;
+ FILE *inf, *outf;
+ char *value;
+ char *oldvalue;
+ int seen;
+
+ file = tooption(name);
+
+ /*
+ * Check to see if the option was specified..
+ */
+ value = NULL;
+ for (op = opt; op; op = op->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:%d: option \"%s\" redefined from %s to %s\n",
+ PREFIX, op->op_line, op->op_name, oldvalue,
+ value);
+ op->op_ownfile++;
+ }
+ }
+
+ 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;
+ }
+ oldvalue = NULL;
+ op_head = NULL;
+ seen = 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);
+ cp = get_word(inf);
+ if (cp == 0 || cp == (char *)EOF)
+ break;
+ /* option value */
+ invalue = ns(cp); /* malloced */
+ if (eq(inw, name)) {
+ oldvalue = invalue;
+ invalue = value;
+ seen++;
+ }
+ op = (struct opt *) malloc(sizeof *op);
+ bzero(op, sizeof(*op));
+ op->op_name = inw;
+ op->op_value = invalue;
+ op->op_next = op_head;
+ op_head = op;
+
+ /* EOL? */
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ }
+ (void) fclose(inf);
+ if ((value == NULL && oldvalue == NULL) ||
+ (value && oldvalue && eq(value,oldvalue))) {
+ for (op = op_head; op != NULL; op = topp) {
+ topp = op->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;
+ op->op_next = op_head;
+ op_head = op;
+ }
+
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ for (op = op_head; op != NULL; op = topp) {
+ /* was the option in the config file? */
+ if (op->op_value) {
+ fprintf(outf, "#define %s %s\n",
+ op->op_name, op->op_value);
+ }
+ topp = op->op_next;
+ free(op->op_name);
+ free(op->op_value);
+ free(op);
+ }
+ (void) fclose(outf);
+}
+
+/*
+ * Find the filename to store the option spec into.
+ */
+char *
+tooption(name)
+ char *name;
+{
+ static char hbuf[80];
+ char nbuf[80];
+ struct opt_list *po;
+
+ /* "cannot happen"? the otab list should be complete.. */
+ (void) strcpy(nbuf, "options.h");
+
+ for (po = otab ; po != 0; po = po->o_next) {
+ if (eq(po->o_name, name)) {
+ strcpy(nbuf, po->o_file);
+ break;
+ }
+ }
+
+ (void) strcpy(hbuf, path(nbuf));
+ return (hbuf);
+}
+
+/*
+ * read the options and options.<machine> files
+ */
+void
+read_options()
+{
+ FILE *fp;
+ char fname[80];
+ char *wd, *this, *val;
+ struct opt_list *po;
+ int first = 1;
+ char genopt[80];
+
+ otab = 0;
+ (void) snprintf(fname, sizeof fname, "../../conf/options");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0) {
+ return;
+ }
+ if(ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+next:
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ (void) snprintf(fname, sizeof fname, "options.%s", machinename);
+ first++;
+ goto openit;
+ }
+ if (first == 2) {
+ (void) snprintf(fname, sizeof fname, "options.%s", raise(ident));
+ first++;
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ /*************************************************\
+ * If it's a comment ignore to the end of the line *
+ \*************************************************/
+ 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);
+
+ for (po = otab ; po != 0; po = po->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;
+ po->o_next = otab;
+ otab = po;
+
+ goto next;
+}
+
+static char *
+lower(str)
+ register char *str;
+{
+ register char *cp = str;
+
+ while (*str) {
+ if (isupper(*str))
+ *str = tolower(*str);
+ str++;
+ }
+ return (cp);
+}
+
diff --git a/usr.sbin/config/mkswapconf.c b/usr.sbin/config/mkswapconf.c
new file mode 100644
index 0000000..d276099
--- /dev/null
+++ b/usr.sbin/config/mkswapconf.c
@@ -0,0 +1,272 @@
+/*
+ * 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[] = "@(#)mkswapconf.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkswapconf.c,v 1.14 1997/09/17 06:20:45 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Build a swap configuration file.
+ */
+#include <err.h>
+#include <unistd.h>
+#include "config.h"
+
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+#ifdef linux
+#include <sys/sysmacros.h>
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ns(s) strdup(s)
+
+void initdevtable __P((void));
+
+void
+swapconf()
+{
+ register struct file_list *fl;
+ struct file_list *do_swap();
+
+ fl = conf_list;
+ while (fl) {
+ if (fl->f_type != SYSTEMSPEC) {
+ fl = fl->f_next;
+ continue;
+ }
+ fl = do_swap(fl);
+ }
+}
+
+struct file_list *
+do_swap(fl)
+ register struct file_list *fl;
+{
+ FILE *fp;
+ char newswapname[80];
+ char swapname[80];
+ register struct file_list *swap;
+
+ if (eq(fl->f_fn, "generic")) {
+ fl = fl->f_next;
+ return (fl->f_next);
+ }
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ (void) sprintf(newswapname, "swap%s.c.new", fl->f_fn);
+ fp = fopen(path(newswapname), "w");
+ if (fp == 0)
+ err(1, "%s", path(newswapname));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/conf.h>\n");
+ fprintf(fp, "\n");
+ /*
+ * If there aren't any swap devices
+ * specified, just return, the error
+ * has already been noted.
+ */
+ swap = fl->f_next;
+ if (swap == 0 || swap->f_type != SWAPSPEC) {
+ (void) unlink(path(newswapname));
+ fclose(fp);
+ return (swap);
+ }
+ fprintf(fp, "dev_t\trootdev = makedev(%d, 0x%08x);\t\t/* %s */\n",
+ major(fl->f_rootdev), minor(fl->f_rootdev),
+ devtoname(fl->f_rootdev));
+ if (fl->f_dumpdev != NODEV) {
+ fprintf(fp, "dev_t\tdumpdev = makedev(%d, 0x%08x);\t\t/* %s */\n",
+ major(fl->f_dumpdev), minor(fl->f_dumpdev),
+ devtoname(fl->f_dumpdev));
+ } else {
+ fprintf(fp, "dev_t\tdumpdev = NODEV;\t\t\t/* unconfigured */\n");
+ }
+ fprintf(fp, "\n");
+ fprintf(fp, "void\nsetconf()\n{\n}\n");
+ fclose(fp);
+ moveifchanged(path(newswapname), path(swapname));
+ return (swap);
+}
+
+static int devtablenotread = 1;
+static struct devdescription {
+ char *dev_name;
+ int dev_major;
+ struct devdescription *dev_next;
+} *devtable;
+
+/*
+ * Given a device name specification figure out:
+ * major device number
+ * partition
+ * device name
+ * unit number
+ * This is a hack, but the system still thinks in
+ * terms of major/minor instead of string names.
+ */
+dev_t
+nametodev(name, defunit, defslice, defpartition)
+ char *name;
+ int defunit;
+ int defslice;
+ char defpartition;
+{
+ char *cp, partition;
+ int unit, slice;
+ register struct devdescription *dp;
+
+ cp = name;
+ if (cp == 0)
+ errx(1, "internal error, nametodev");
+ while (*cp && !isdigit(*cp))
+ cp++;
+ unit = *cp ? atoi(cp) : defunit;
+ if (unit < 0 || unit > 31) {
+ warnx(
+ "%s: invalid device specification, unit out of range", name);
+ unit = defunit; /* carry on more checking */
+ }
+ if (*cp) {
+ *cp++ = '\0';
+ while (*cp && isdigit(*cp))
+ cp++;
+ }
+ slice = defslice;
+ if (*cp == 's') {
+ ++cp;
+ if (*cp) {
+ slice = atoi(cp);
+ if (slice < 0 || slice >= MAX_SLICES - 1) {
+ warnx(
+ "%s: invalid device specification, slice out of range",
+ cp);
+ slice = defslice;
+ }
+ if (slice != COMPATIBILITY_SLICE)
+ slice++;
+ *cp++ = '\0';
+ while (*cp && isdigit(*cp))
+ cp++;
+ }
+ }
+ partition = *cp ? *cp : defpartition;
+ if (partition < 'a' || partition > 'h') {
+ warnx("%c: invalid device specification, bad partition", *cp);
+ partition = defpartition; /* carry on */
+ }
+ if (devtablenotread)
+ initdevtable();
+ for (dp = devtable; dp; dp = dp->dev_next)
+ if (eq(name, dp->dev_name))
+ break;
+ if (dp == 0) {
+ warnx("%s: unknown device", name);
+ return (NODEV);
+ }
+ return (makedev(dp->dev_major,
+ dkmakeminor(unit, slice, partition - 'a')));
+}
+
+char *
+devtoname(dev)
+ dev_t dev;
+{
+ char buf[80];
+ register struct devdescription *dp;
+ int part;
+ char partname[2];
+ int slice;
+ char slicename[32];
+
+ if (devtablenotread)
+ initdevtable();
+ for (dp = devtable; dp; dp = dp->dev_next)
+ if (major(dev) == dp->dev_major)
+ break;
+ if (dp == 0)
+ dp = devtable;
+ part = dkpart(dev);
+ slice = dkslice(dev);
+ slicename[0] = partname[0] = '\0';
+ if (slice != WHOLE_DISK_SLICE || part != RAW_PART) {
+ partname[0] = 'a' + part;
+ partname[1] = '\0';
+ if (slice != COMPATIBILITY_SLICE)
+ sprintf(slicename, "s%d", slice - 1);
+ }
+ (void) sprintf(buf, "%s%d%s%s", dp->dev_name,
+ dkunit(dev), slicename, partname);
+ return (ns(buf));
+}
+
+void
+initdevtable()
+{
+ char linebuf[256];
+ char buf[BUFSIZ];
+ int maj;
+ register struct devdescription **dp = &devtable;
+ FILE *fp;
+
+ (void) sprintf(buf, "../conf/devices.%s", machinename);
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ errx(1, "can't open %s", buf);
+ while(fgets(linebuf,256,fp)) {
+ /*******************************\
+ * Allow a comment *
+ \*******************************/
+ if(linebuf[0] == '#') continue;
+
+ if (sscanf(linebuf, "%s\t%d\n", buf, &maj) == 2) {
+ *dp = (struct devdescription *)malloc(sizeof (**dp));
+ memset(*dp, 0, sizeof(**dp));
+ (*dp)->dev_name = ns(buf);
+ (*dp)->dev_major = maj;
+ dp = &(*dp)->dev_next;
+ } else {
+ fprintf(stderr,"illegal line in devices file\n");
+ break;
+ }
+ }
+ *dp = 0;
+ fclose(fp);
+ devtablenotread = 0;
+}
diff --git a/usr.sbin/config/mkubglue.c b/usr.sbin/config/mkubglue.c
new file mode 100644
index 0000000..c72766b
--- /dev/null
+++ b/usr.sbin/config/mkubglue.c
@@ -0,0 +1,198 @@
+/*-
+ * 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[] = "@(#)mkubglue.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Make the uba interrupt file ubglue.s
+ */
+#include <stdio.h>
+#include "config.h"
+#include "y.tab.h"
+
+ubglue()
+{
+ register FILE *fp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("ubglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ubglue.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_vec(fp, id->id, dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_std(fp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+}
+
+static int cntcnt = 0; /* number of interrupt counters allocated */
+
+/*
+ * print an interrupt vector
+ */
+dump_vec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "\t.globl\t_X%s\n\t.align\t2\n_X%s:\n\tpushr\t$0x3f\n",
+ v, v);
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ if (strncmp(vector, "dzx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdzdma\n\n", number);
+ else if (strncmp(vector, "dpx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdpxdma\n\n", number);
+ else if (strncmp(vector, "dpr", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdprdma\n\n", number);
+ else {
+ if (strncmp(vector, "uur", 3) == 0) {
+ fprintf(fp, "#ifdef UUDMA\n");
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjsb\tuudma\n", number);
+ fprintf(fp, "#endif\n");
+ }
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcalls\t$1,_%s\n\tpopr\t$0x3f\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n\trei\n\n");
+ }
+}
+
+/*
+ * Start the interrupt name table with the names
+ * of the standard vectors not on the unibus.
+ * The number and order of these names should correspond
+ * with the definitions in scb.s.
+ */
+dump_std(fp)
+ register FILE *fp;
+{
+ fprintf(fp, "\n\t.globl\t_intrnames\n");
+ fprintf(fp, "\n\t.globl\t_eintrnames\n");
+ fprintf(fp, "\t.data\n");
+ fprintf(fp, "_intrnames:\n");
+ fprintf(fp, "\t.asciz\t\"clock\"\n");
+ fprintf(fp, "\t.asciz\t\"cnr\"\n");
+ fprintf(fp, "\t.asciz\t\"cnx\"\n");
+ fprintf(fp, "\t.asciz\t\"tur\"\n");
+ fprintf(fp, "\t.asciz\t\"tux\"\n");
+ fprintf(fp, "\t.asciz\t\"mba0\"\n");
+ fprintf(fp, "\t.asciz\t\"mba1\"\n");
+ fprintf(fp, "\t.asciz\t\"mba2\"\n");
+ fprintf(fp, "\t.asciz\t\"mba3\"\n");
+ fprintf(fp, "\t.asciz\t\"uba0\"\n");
+ fprintf(fp, "\t.asciz\t\"uba1\"\n");
+ fprintf(fp, "\t.asciz\t\"uba2\"\n");
+ fprintf(fp, "\t.asciz\t\"uba3\"\n");
+#define I_FIXED 13 /* number of names above */
+}
+
+dump_intname(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ register char *cp = vector;
+
+ fprintf(fp, "\t.asciz\t\"");
+ /*
+ * Skip any "int" or "intr" in the name.
+ */
+ while (*cp)
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ } else {
+ putc(*cp, fp);
+ cp++;
+ }
+ fprintf(fp, "%d\"\n", number);
+}
+
+dump_ctrs(fp)
+ register FILE *fp;
+{
+ fprintf(fp, "_eintrnames:\n");
+ fprintf(fp, "\n\t.globl\t_intrcnt\n");
+ fprintf(fp, "\n\t.globl\t_eintrcnt\n");
+ fprintf(fp, "_intrcnt:\n", I_FIXED);
+ fprintf(fp, "\t.space\t4 * %d\n", I_FIXED);
+ fprintf(fp, "_fltintrcnt:\n", cntcnt);
+ fprintf(fp, "\t.space\t4 * %d\n", cntcnt);
+ fprintf(fp, "_eintrcnt:\n\n");
+ fprintf(fp, "\t.text\n");
+}
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
new file mode 100644
index 0000000..1131cff
--- /dev/null
+++ b/usr.sbin/cron/Makefile
@@ -0,0 +1,3 @@
+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..b6021e3
--- /dev/null
+++ b/usr.sbin/cron/Makefile.inc
@@ -0,0 +1,9 @@
+# $Id$
+
+.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..e52d31d
--- /dev/null
+++ b/usr.sbin/cron/cron/Makefile
@@ -0,0 +1,10 @@
+# $Id$
+
+PROG= cron
+SRCS= cron.c database.c do_command.c job.c user.c popen.c
+CFLAGS+=-DLOGIN_CAP
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -lutil
+MAN8= cron.8
+
+.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..e286e0f
--- /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
+ */
+
+/*
+ * $Id$
+ */
+
+#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..d704888
--- /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
+ *
+ * $Id$
+ */
+
+#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..256de2b
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.8
@@ -0,0 +1,75 @@
+.\"/* 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
+.\" */
+.\"
+.\" $Id: cron.8,v 1.5 1997/09/15 06:39:03 charnier Exp $
+.\"
+.Dd December 20, 1993
+.Dt CRON 8
+.Os
+.Sh NAME
+.Nm cron
+.Nd daemon to execute scheduled commands (Vixie Cron)
+.Sh SYNOPSIS
+.Nm cron
+.Op Fl x Ar debugflag Ns Op ,...
+.Sh DESCRIPTION
+.Nm Cron
+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
+.Nm Cron
+searches
+.Pa /var/cron/tabs
+for crontab files which are named after accounts in
+.Pa /etc/passwd ;
+crontabs found are loaded into memory.
+.Nm Cron
+also searches for
+.Pa /etc/crontab
+which is in a different format (see
+.Xr crontab 5 ).
+.Nm Cron
+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 modtime (or the modtime
+on
+.Pa /etc/crontab )
+has changed, and if it has,
+.Nm
+will then examine the modtime 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 modtime of the spool directory whenever it changes a
+crontab.
+.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..ff90831
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.c
@@ -0,0 +1,322 @@
+/* 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[] =
+ "$Id: cron.c,v 1.7 1998/07/06 20:28:04 bde Exp $";
+#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((void)),
+#ifdef USE_SIGCHLD
+ sigchld_handler __P((int)),
+#endif
+ sighup_handler __P((int)),
+ parse_args __P((int c, char *v[]));
+
+
+static void
+usage() {
+ char **dflags;
+
+ fprintf(stderr, "usage: cron [-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 {
+ switch (fork()) {
+ case -1:
+ log_it("CRON",getpid(),"DEATH","can't fork");
+ exit(0);
+ break;
+ case 0:
+ /* child process */
+ log_it("CRON",getpid(),"STARTUP","fork ok");
+ (void) setsid();
+ break;
+ default:
+ /* parent process should just die */
+ _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();
+
+ 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;
+{
+ 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))
+
+ /* 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 (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))
+ )
+ ) {
+ job_add(e, u);
+ }
+ }
+ }
+}
+
+
+/* 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() {
+ 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_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);
+ }
+ }
+}
+
+
+#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;
+
+ while ((argch = getopt(argc, argv, "x:")) != -1) {
+ switch (argch) {
+ 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..9513271
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.h
@@ -0,0 +1,289 @@
+/* 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
+ *
+ * $Id: cron.h,v 1.7 1997/09/15 06:39:04 charnier Exp $
+ *
+ * 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 64
+#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
+} 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 *));
+
+
+ /* 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;
+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 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..7d8bf6f
--- /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[] =
+ "$Id: database.c,v 1.6 1997/02/22 16:04:42 peter Exp $";
+#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..03236f4
--- /dev/null
+++ b/usr.sbin/cron/cron/do_command.c
@@ -0,0 +1,541 @@
+/* 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[] =
+ "$Id: do_command.c,v 1.13 1997/09/15 06:39:06 charnier Exp $";
+#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.
+ */
+ /*local*/{
+ register char *pch;
+
+ for (pch = ProgramName; *pch; pch++)
+ *pch = MkUpper(*pch);
+ }
+
+ /* 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_IGN);
+#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()))
+
+ /* 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)
+ }
+#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;
+
+ 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 = 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"))) {
+ warn("%s", MAILCMD);
+ (void) _exit(ERROR_EXIT);
+ }
+ fprintf(mail, "From: root (Cron Daemon)\n");
+ 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..01ed0d6
--- /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[] =
+ "$Id: job.c,v 1.4 1997/02/22 16:04:44 peter Exp $";
+#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..5c58967
--- /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
+ */
+
+/*
+ * $Id$
+ */
+
+#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..7613f54
--- /dev/null
+++ b/usr.sbin/cron/cron/popen.c
@@ -0,0 +1,179 @@
+/*
+ * 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[] =
+ "$Id: popen.c,v 1.5 1997/09/15 06:39:07 charnier Exp $";
+#endif /* not lint */
+
+#include "cron.h"
+#include <sys/signal.h>
+#include <fcntl.h>
+
+
+#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)
+ char *program, *type;
+{
+ register char *cp;
+ FILE *iop;
+ int argc, pdes[2];
+ PID_T pid;
+ char *argv[MAX_ARGS + 1];
+#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;
+
+#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 (*type == 'r') {
+ /* Do not share our parent's stdin */
+ (void)close(0);
+ (void)open("/dev/null", 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("/dev/null", O_RDWR);
+ (void)close(2);
+ (void)open("/dev/null", O_RDWR);
+ (void)close(pdes[1]);
+ }
+#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..d583361
--- /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[] =
+ "$Id: user.c,v 1.6 1997/09/15 06:39:07 charnier Exp $";
+#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..99b6712
--- /dev/null
+++ b/usr.sbin/cron/crontab/Makefile
@@ -0,0 +1,15 @@
+# $Id$
+
+PROG= crontab
+CFLAGS+=-I${.CURDIR}/../cron
+DPADD= ${LIBCRON} ${LIBUTIL}
+LDADD= ${LIBCRON} -lutil
+MAN1= crontab.1
+MAN5= crontab.5
+
+BINDIR= /usr/bin
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.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..0a12ace
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.1
@@ -0,0 +1,115 @@
+.\"/* 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
+.\" */
+.\"
+.\" $Id: crontab.1,v 1.4 1997/09/15 06:39:13 charnier Exp $
+.\"
+.Dd December 29, 1993
+.Dt CRONTAB 1
+.Os
+.Sh NAME
+.Nm crontab
+.Nd maintain crontab files for individual users (V3)
+.Sh SYNOPSIS
+.Nm crontab
+.Op Fl u Ar user
+.Ar file
+.Nm crontab
+.Op Fl u Ar user
+{
+.Fl l |
+.Fl r |
+.Fl e
+}
+.Sh DESCRIPTION
+.Nm Crontab
+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.
+.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 ``-'' 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 "your" crontab, i.e., the crontab of the person executing the
+command. Note that
+.Xr su 8
+can confuse
+.Nm
+and that if you are running inside of
+.Xr su 8
+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. 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 .
+This 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..bd8625e
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -0,0 +1,225 @@
+.\"/* 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
+.\" */
+.\"
+.\" $Id: crontab.5,v 1.7 1997/11/02 17:22:10 ache Exp $
+.\"
+.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,
+.Pp
+ name = value
+.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.
+.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 BSD 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. 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)
+match 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,
+.br
+``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.
+.Sh EXAMPLE CRON FILE
+.Bd -literal
+
+# use /bin/sh to run commands, no matter what /etc/passwd says
+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.
+BSD and 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 ATT or BSD 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 BSD or ATT, the
+environment handed to child processes is basically the one from
+.Pa /etc/rc .
+.Pp
+Command output is mailed to the crontab owner (BSD 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).
+.Sh AUTHORS
+.An Paul Vixie Aq paul@vix.com
diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c
new file mode 100644
index 0000000..89a2d75
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.c
@@ -0,0 +1,593 @@
+/* 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[] =
+ "$Id: crontab.c,v 1.10 1997/03/31 05:09:58 imp Exp $";
+#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 <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(0);
+ /*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;
+
+ 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)
+ while (EOF != (ch = get_char(f)))
+ putchar(ch);
+ fclose(f);
+}
+
+
+static void
+delete_cmd() {
+ char n[MAX_FNAME];
+
+ 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;
+ 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("/dev/null", "r")))
+ err(ERROR_EXIT, "/dev/null");
+ }
+
+ 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, "w"))) {
+ 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 (fclose(NewCrontab))
+ err(ERROR_EXIT, "%s", Filename);
+ again:
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ fatal: unlink(Filename);
+ exit(ERROR_EXIT);
+ }
+ 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, 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 (mtime == statbuf.st_mtime) {
+ warnx("no changes made to crontab");
+ goto remove;
+ }
+ warnx("installing new crontab");
+ if (!(NewCrontab = fopen(Filename, "r"))) {
+ warn("%s", Filename);
+ goto fatal;
+ }
+ 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
+ */
+ Set_LineNum(1)
+ while (EOF != (ch = get_char(NewCrontab)))
+ putc(ch, tmp);
+ fclose(NewCrontab);
+ 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..c18be0e
--- /dev/null
+++ b/usr.sbin/cron/doc/CONVERSION
@@ -0,0 +1,85 @@
+$Id$
+
+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 seperate 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..3c19cef
--- /dev/null
+++ b/usr.sbin/cron/doc/FEATURES
@@ -0,0 +1,84 @@
+$Id$
+
+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..0537c6d
--- /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
+ */
+
+$Id$
+
+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..551575e
--- /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 ]
+
+$Id$
+
+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..f659223
--- /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
+#
+# $Id$
+#
+# 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..d06e905
--- /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.
+
+$Id$
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..5270131
--- /dev/null
+++ b/usr.sbin/cron/lib/Makefile
@@ -0,0 +1,12 @@
+LIB= cron
+
+SRCS= entry.c env.c misc.c
+CFLAGS+=-I${.CURDIR}/../cron
+CFLAGS+=-DLOGIN_CAP
+NOPIC= yes
+NOPROFILE= yes
+
+install:
+ @echo -n
+
+.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..2143bc4
--- /dev/null
+++ b/usr.sbin/cron/lib/compat.c
@@ -0,0 +1,236 @@
+/* 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[] = "$Id$";
+#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>
+
+
+/* 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("/dev/tty", 2)) >= 0)
+ {
+ (void) ioctl(fd, TIOCNOTTY, (char*)0);
+ (void) close(fd);
+ }
+# else /*BSD*/
+ newpgrp = setpgrp();
+
+ (void) close(STDIN); (void) open("/dev/null", 0);
+ (void) close(STDOUT); (void) open("/dev/null", 1);
+ (void) close(STDERR); (void) open("/dev/null", 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..b7dba59
--- /dev/null
+++ b/usr.sbin/cron/lib/entry.c
@@ -0,0 +1,552 @@
+/* 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[] =
+ "$Id: entry.c,v 1.7 1997/09/15 06:39:21 charnier Exp $";
+#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
+#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",
+#ifdef LOGIN_CAP
+ "bad class name",
+#endif
+ };
+
+
+void
+free_entry(e)
+ entry *e;
+{
+#ifdef LOGIN_CAP
+ if (e->class != NULL)
+ free(e->class);
+#endif
+ free(e->cmd);
+ 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];
+
+ 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 (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
+ */
+ ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
+ if (!strcmp("reboot", cmd)) {
+ e->flags |= WHEN_REBOOT;
+ } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
+ 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));
+ } else if (!strcmp("monthly", cmd)) {
+ 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));
+ } else if (!strcmp("weekly", cmd)) {
+ 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_set(e->dow, 0);
+ } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
+ 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)) {
+ bit_set(e->minute, 0);
+ bit_set(e->hour, (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;
+ }
+ } 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, *group;
+ struct group *grp;
+
+ 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);
+ } else
+ e->class = strdup(RESOURCE_RC);
+ if (login_getclass(e->class) == NULL) {
+ ecode = e_class;
+ goto eof;
+ }
+#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 (!env_get("SHELL", e->envp)) {
+ sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
+ e->envp = env_set(e->envp, envstr);
+ }
+ sprintf(envstr, "HOME=%s", pw->pw_dir);
+ e->envp = env_set(e->envp, envstr);
+ if (!env_get("PATH", e->envp)) {
+ sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
+ e->envp = env_set(e->envp, envstr);
+ }
+ sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+#if defined(BSD)
+ sprintf(envstr, "%s=%s", "USER", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+#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);
+
+ Debug(DPARS, ("load_entry()...returning successfully\n"))
+
+ /* success, fini, return pointer to the entry we just created...
+ */
+ return e;
+
+ eof:
+ free(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..d66e1a3
--- /dev/null
+++ b/usr.sbin/cron/lib/env.c
@@ -0,0 +1,203 @@
+/* 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[] =
+ "$Id: env.c,v 1.5 1997/02/22 16:05:07 peter Exp $";
+#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;
+
+ for (p = envp; *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;
+
+ /*
+ * 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.
+ */
+ free(envp[found]);
+ if ((envp[found] = strdup(envstr)) == NULL) {
+ envp[found] = "";
+ errno = ENOMEM;
+ return NULL;
+ }
+ 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) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = p[count-1];
+ if ((p[count-1] = strdup(envstr)) == NULL) {
+ 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];
+ int fields;
+
+ 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))
+
+ name[0] = val[0] = '\0';
+ fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val);
+ if (fields != 2) {
+ Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields))
+ fseek(f, filepos, 0);
+ Set_LineNum(fileline);
+ return (FALSE);
+ }
+
+ /* 2 fields from scanf; looks like an env setting
+ */
+
+ /*
+ * process value string
+ */
+ /*local*/{
+ int len = strdtb(val);
+
+ if (len >= 2) {
+ if (val[0] == '\'' || val[0] == '"') {
+ if (val[len-1] == val[0]) {
+ val[len-1] = '\0';
+ (void) strcpy(val, val+1);
+ }
+ }
+ }
+ }
+
+ 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..3e03182
--- /dev/null
+++ b/usr.sbin/cron/lib/misc.c
@@ -0,0 +1,648 @@
+/* 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[] =
+ "$Id: misc.c,v 1.6 1997/09/15 06:39:25 charnier Exp $";
+#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')
+ 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;
+{
+ static int init = FALSE;
+ static FILE *allow, *deny;
+
+ if (!init) {
+ init = TRUE;
+#if defined(ALLOW_FILE) && defined(DENY_FILE)
+ allow = fopen(ALLOW_FILE, "r");
+ deny = fopen(DENY_FILE, "r");
+ Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
+#else
+ allow = NULL;
+ deny = NULL;
+#endif
+ }
+
+ if (allow)
+ return (in_file(username, allow));
+ if (deny)
+ return (!in_file(username, deny));
+
+#if defined(ALLOW_ONLY_ROOT)
+ return (strcmp(username, ROOT_USER) == 0);
+#else
+ return TRUE;
+#endif
+}
+
+
+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 (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);
+
+ 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..a38e0b9
--- /dev/null
+++ b/usr.sbin/crunch/Makefile
@@ -0,0 +1,4 @@
+
+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..da42105
--- /dev/null
+++ b/usr.sbin/crunch/Makefile.inc
@@ -0,0 +1,2 @@
+# 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..abdc21ea
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/Makefile
@@ -0,0 +1,10 @@
+
+PROG=crunchgen
+SRCS=crunchgen.c crunched_skel.c
+CFLAGS+=-Wall
+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..5ebf04f
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunched_main.c
@@ -0,0 +1,117 @@
+/*
+ * 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
+ */
+/*
+ * 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 *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);
+ 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)
+{
+ struct stub *ep;
+ int columns, len;
+
+ if(argc <= 1)
+ crunched_usage();
+
+ return main(--argc, ++argv);
+}
+
+
+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..367cd2c
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.1
@@ -0,0 +1,278 @@
+.\"
+.\" 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
+.\"
+.Dd September 29, 1997
+.Dt CRUNCHGEN 1
+.Os BSD 4
+.Sh NAME
+.Nm \&crunchgen
+.Nd generates build environment for a crunched binary
+.Sh SYNOPSIS
+.Nm \&crunchgen
+.Op Fl fql
+.Op Fl m Ar makefile-name
+.Op Fl c Ar c-file-name
+.Op Fl e Ar exec-file-name
+.Op Ar conf-file
+.Sh DESCRIPTION
+
+A crunched binary is a program made up of many other programs linked
+together into a single executable. The crunched binary main()
+function determines which component program to run by the contents of
+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
+.Nm Crunchgen
+reads in the specifications in
+.Ar conf-file
+for a crunched binary, and generates a Makefile and accompanying
+top-level C source file that when built create the crunched executable
+file from the component programs. For each component program,
+.Nm crunchgen
+can optionally attempt to determine the object (.o) files that make up
+the program from its source directory Makefile. This information is
+cached between runs.
+.Nm Crunchgen
+uses the companion program
+.Nm crunchide
+to eliminate link-time conflicts between the component programs by
+hiding all unnecessary symbols.
+
+.Pp
+After
+.Nm
+is run, the crunched binary can be built by running ``make -f
+<conf-name>.mk''. The component programs' object files must already
+be built. A ``objs'' target, included in the output makefile, will
+run make 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 ``<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 ``<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 m Ar makefile-name
+Set output Makefile name to
+.Ar makefile-name .
+The default name is ``<conf-name>.mk''.
+.It Fl q
+Quiet operation. Status messages are suppressed.
+.El
+.Sh CRUNCHGEN CONFIGURATION FILE COMMANDS
+
+.Nm Crunchgen
+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.
+.Nm Crunchgen
+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 Nm 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
+BSD ``<source-dir>/<progname>/'' convention. Multiple
+.Nm srcdirs
+lines can be specified. The directories are searched in the order
+they are given.
+.It Nm progs Ar progname ...
+A list of programs that make up the crunched binary. Multiple
+.Nm progs
+lines can be specified.
+.It Nm libs Ar libspec ...
+A list of library specifications to be included in the crunched binary link.
+Multiple
+.Nm libs
+lines can be specified.
+.It Nm ln Ar progname linkname
+Causes the crunched binary to invoke
+.Ar progname
+whenever
+.Ar linkname
+appears in argv[0]. This allows programs that change their behavior when
+run under different names to operate correctly.
+.El
+
+To handle specialized situations, such as when the source is not
+available or not built via a conventional Makefile, the following
+.Nm special
+commands can be used to set
+.Nm
+parameters for a component program.
+.Bl -tag -width indent
+.It Nm special Ar progname Nm srcdir Ar pathname
+Set the source directory for
+.Ar progname .
+This is normally calculated by searching the specified
+.Nm srcdirs
+for a directory named
+.Ar progname .
+.It Nm special Ar progname Nm objdir Ar pathname
+Set the obj directory for
+.Ar progname .
+This is normally calculated by looking for a directory named
+.Dq Pa obj
+under the
+.Ar srcdir ,
+and if that is not found, the
+.Ar srcdir
+itself becomes the
+.Ar objdir .
+.It Nm special Ar progname Nm 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 Nm srcdir / Pa Makefile
+and outputs the value of $(OBJS).
+.It Nm special Ar progname Nm 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
+.Nm objdir
+pathname to each file in the
+.Nm objs
+list.
+.It Nm special Ar progname Nm keep Ar symbol-name ...
+Add specified list of symbols to the keep list for program
+.Ar progname .
+An underscore 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.
+.El
+
+.Pp
+Only the
+.Nm objpaths
+parameter is actually needed by
+.Nm crunchgen ,
+but it is calculated from
+.Nm objdir
+and
+.Nm objs ,
+which are in turn calculated from
+.Nm 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
+.Ar objs
+target that will build the object files for each component program by
+running make inside that program's source directory. For this to work the
+.Nm srcdir
+and
+.Nm objs
+parameters must also be valid. If they are not valid for a particular program, that
+program is skipped in the
+.Ar objs
+target.
+.Sh EXAMPLE
+Here is an example
+.Nm
+input conf file, named
+.Dq Pa kcopy.conf :
+.Pp
+.nf
+ srcdirs /usr/src/bin /usr/src/sbin
+
+ progs test cp echo sh fsck halt init mount umount myinstall
+ 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
+
+ libs -lutil -lcrypt
+.fi
+.Pp
+This conf file specifies a small crunched binary consisting of some
+basic system utilities plus a homegrown install program ``myinstall'',
+for which no source directory is specified, but its object file is
+specified directly with the
+.Nm special
+line.
+.Pp
+The crunched binary ``kcopy'' can be built as follows:
+.Pp
+.nf
+ % 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!
+.fi
+.Pp
+At this point the binary ``kcopy'' can be copied onto an install floppy
+and hard-linked to the names of the component programs.
+.Sh SEE ALSO
+.Xr crunchide 1
+.Sh CAVEATS
+While
+.Nm crunch
+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 BSD build environment do not by default build the
+intermediate object file for single-source file programs. The ``make
+objs'' target must then be used to get those object files built, or
+some other arrangements made.
+.Sh AUTHORS
+.Nm Crunch
+was written by
+.An James da Silva Aq jds@cs.umd.edu .
+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..8998ea4
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.c
@@ -0,0 +1,883 @@
+/*
+ * 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
+ */
+/*
+ * ========================================================================
+ * crunchgen.c
+ *
+ * Generates a Makefile and main C file for a crunched executable,
+ * from specs given in a .conf file.
+ */
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.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;
+ char *name, *ident;
+ char *srcdir, *objdir;
+ strlst_t *objs, *objpaths;
+ strlst_t *keeplist;
+ strlst_t *links;
+ int goterror;
+} prog_t;
+
+
+/* global state */
+
+strlst_t *srcdirs = NULL;
+strlst_t *libs = NULL;
+prog_t *progs = NULL;
+
+char line[MAXLINELEN];
+
+char confname[MAXPATHLEN], infilename[MAXPATHLEN];
+char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
+char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
+int linenum = -1;
+int goterror = 0;
+
+int verbose, readcache; /* options */
+int reading_cache;
+
+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';
+
+ while((optc = getopt(argc, argv, "lm:c:e:fq")) != -1) {
+ switch(optc) {
+ case 'f': readcache = 0; break;
+ case 'q': verbose = 0; break;
+
+ case 'm': strcpy(outmkname, optarg); break;
+ case 'c': strcpy(outcfname, optarg); break;
+ case 'e': strcpy(execfname, optarg); break;
+ case 'l': list_mode++; verbose = 0; break;
+
+ case '?':
+ default: usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1) usage();
+
+ /*
+ * generate filenames
+ */
+
+ strcpy(infilename, argv[0]);
+
+ /* confname = `basename infilename .conf` */
+
+ if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1);
+ else strcpy(confname, infilename);
+ if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
+
+ if(!*outmkname) sprintf(outmkname, "%s.mk", confname);
+ if(!*outcfname) sprintf(outcfname, "%s.c", confname);
+ if(!*execfname) sprintf(execfname, "%s", confname);
+
+ sprintf(cachename, "%s.cache", confname);
+ sprintf(tempfname, ".tmp_%sXXXXXX", confname);
+ if(mktemp(tempfname) == NULL) {
+ perror(tempfname);
+ exit(1);
+ }
+
+ parse_conf_file();
+ if (list_mode)
+ exit(goterror);
+
+ gen_outputs();
+
+ exit(goterror);
+}
+
+
+void usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: crunchgen [-fq] [-m <makefile>] [-c <c file>]",
+ " [-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_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;
+
+ sprintf(line, "reading %s", filename);
+ status(line);
+ strcpy(curfilename, filename);
+
+ 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], "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 command needs at least 1 argument, skipping",
+ curfilename, linenum, fieldv[0]);
+ 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 = p2->srcdir = p2->objdir = NULL;
+ p2->links = p2->objs = p2->keeplist = 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_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 {
+ 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 objs exe\" to build crunched binary.\n",
+ outmkname);
+}
+
+
+void fillin_program(prog_t *p)
+{
+ char path[MAXPATHLEN];
+ char *srcparent;
+ strlst_t *s;
+
+ sprintf(line, "filling in parms for %s", p->name);
+ status(line);
+
+ if(!p->ident)
+ p->ident = genident(p->name);
+ if(!p->srcdir) {
+ srcparent = dir_search(p->name);
+ if(srcparent)
+ sprintf(path, "%s/%s", srcparent, p->name);
+ if(is_dir(path))
+ p->srcdir = strdup(path);
+ }
+ if(!p->objdir && p->srcdir) {
+ FILE *f;
+
+ sprintf(path, "cd %s && echo -n /usr/obj`/bin/pwd`", p->srcdir);
+ p->objdir = p->srcdir;
+ f = popen(path,"r");
+ if (f) {
+ fgets(path,sizeof path, f);
+ if (!pclose(f)) {
+ if(is_dir(path))
+ p->objdir = strdup(path);
+ }
+ }
+ }
+
+ if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir);
+ if(!p->objs && p->srcdir && is_nonempty_file(path))
+ fillin_program_objs(p, path);
+
+ if(!p->objpaths && p->objdir && p->objs)
+ for(s = p->objs; s != NULL; s = s->next) {
+ sprintf(line, "%s/%s", p->objdir, s->str);
+ add_string(&p->objpaths, line);
+ }
+
+ if(!p->srcdir && verbose)
+ warnx("%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->objpaths) {
+ warnx("%s: %s: error: no objpaths specified or calculated",
+ infilename, p->name);
+ p->goterror = goterror = 1;
+ }
+}
+
+void fillin_program_objs(prog_t *p, char *path)
+{
+ char *obj, *cp;
+ int rc;
+ FILE *f;
+
+ /* discover the objs from the srcdir Makefile */
+
+ if((f = fopen(tempfname, "w")) == NULL) {
+ warn("%s", tempfname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(f, ".include \"%s\"\n", path);
+ fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
+ fprintf(f, "OBJS=${PROG}.o\n");
+ fprintf(f, ".endif\n");
+ fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
+ fclose(f);
+
+ sprintf(line, "make -f %s crunchgen_objs 2>&1", 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;
+
+ sprintf(line, "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);
+ }
+ fprintf(cachef, "special %s objpaths", p->name);
+ output_strlst(cachef, p->objpaths);
+ }
+ fclose(cachef);
+}
+
+
+void gen_output_makefile(void)
+{
+ prog_t *p;
+ FILE *outmk;
+
+ sprintf(line, "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);
+
+ 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;
+
+ sprintf(line, "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;
+
+ for(dir=srcdirs; dir != NULL; dir=dir->next) {
+ sprintf(path, "%s/%s", dir->str, progname);
+ if(is_dir(path)) return dir->str;
+ }
+ return NULL;
+}
+
+
+void top_makefile_rules(FILE *outmk)
+{
+ prog_t *p;
+
+ fprintf(outmk, "LIBS=");
+ output_strlst(outmk, libs);
+
+ 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, "%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, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
+ fprintf(outmk, "exe: %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);
+
+ if(p->srcdir && p->objs) {
+ fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
+ fprintf(outmk, "%s_OBJS=", p->ident);
+ output_strlst(outmk, p->objs);
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && make depend && make $(%s_OBJS))\n",
+ p->ident, p->ident);
+ fprintf(outmk, "%s_clean:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && make clean)\n\n", p->ident);
+ }
+ else
+ fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
+ p->ident, p->name);
+
+ fprintf(outmk, "%s_OBJPATHS=", p->ident);
+ output_strlst(outmk, p->objpaths);
+
+ 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)\n",
+ p->name, p->name, p->ident);
+ fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
+ p->name, p->name, p->ident);
+ 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)
+{
+ errx(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) {
+ memset(p2, 0, sizeof(strlst_t));
+ p2->str = strdup(str);
+ }
+ if(!p2 || !p2->str)
+ out_of_memory();
+
+ p2->next = NULL;
+ 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..d7abd44
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/Makefile
@@ -0,0 +1,6 @@
+
+PROG= crunchide
+
+SRCS= crunchide.c exec_aout.c exec_elf32.c exec_elf64.c
+
+.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..464517d
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.1
@@ -0,0 +1,82 @@
+.\"
+.\" 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
+.\"
+.Dd June 14, 1994
+.Dt CRUNCHIDE 1
+.Os BSD 4
+.Sh NAME
+.Nm crunchide
+.Nd hides symbol names from ld, for crunching programs together
+.Sh SYNOPSIS
+.Nm crunchide
+.Op Fl f Ar keep-list-file
+.Op Fl k Ar keep-symbol
+.Op Ar object-file ...
+.Sh DESCRIPTION
+
+.Nm Crunchide
+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
+.Nm Crunchide
+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
+.Nm Crunch
+was written by
+.An James da Silva Aq jds@cs.umd.edu .
+.sp 0
+Copyright (c) 1994 University of Maryland. All Rights Reserved.
+.Pp
+Chris Demetriou <cgd@netbsd.org> reorganized
+.Nm
+so that it supported multiple object formats, and added
+ELF object support and ECOFF object recognition.
+.sp 0
+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..9d2772f
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.c
@@ -0,0 +1,267 @@
+/* $NetBSD: crunchide.c,v 1.8 1997/11/01 06:51:45 lukem 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
+ */
+/*
+ * 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..9f68115
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_aout.c
@@ -0,0 +1,203 @@
+/* $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 $");
+#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 "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)
+
+#ifdef __sparc
+/* is the relocation entry dependent on a symbol? */
+#define IS_SYMBOL_RELOC(rp) \
+ ((rp)->r_extern || \
+ ((rp)->r_type >= RELOC_BASE10 && (rp)->r_type <= RELOC_BASE22) || \
+ (rp)->r_type == RELOC_JMP_TBL)
+#else
+/* is the relocation entry dependent on a symbol? */
+#define IS_SYMBOL_RELOC(rp) \
+ ((rp)->r_extern||(rp)->r_baserel||(rp)->r_jmptable)
+#endif
+
+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..45883f4
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/exec_elf32.c
@@ -0,0 +1,389 @@
+/* $NetBSD: exec_elf32.c,v 1.4 1997/08/12 06:07:24 mikel 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
+__RCSID("$NetBSD: exec_elf32.c,v 1.4 1997/08/12 06:07:24 mikel Exp $");
+#endif
+
+#ifndef ELFSIZE
+#define ELFSIZE 32
+#endif
+
+#include <sys/types.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))
+
+#include <elf.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)))
+
+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;
+
+ /*
+ * 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;
+
+ switch (eh.e_machine) {
+ case EM_386: break;
+ case EM_ALPHA: 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;
+
+ rv = 0;
+ if (xreadatoff(fd, &ehdr, 0, sizeof ehdr, fn) != sizeof ehdr)
+ goto bad;
+
+ shdrsize = ehdr.e_shnum * ehdr.e_shentsize;
+ if ((shdrp = xmalloc(shdrsize, fn, "section header table")) == NULL)
+ goto bad;
+ if (xreadatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
+ goto bad;
+
+ symtabshdr = strtabshdr = NULL;
+ weird = 0;
+ for (i = 0; i < ehdr.e_shnum; i++) {
+ switch (shdrp[i].sh_type) {
+ case SHT_SYMTAB:
+ if (symtabshdr != NULL)
+ weird = 1;
+ symtabshdr = &shdrp[i];
+ strtabshdr = &shdrp[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 = shdrp[i].sh_offset;
+ tmpl->size = 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 = shdrp[i].sh_offset;
+ tmpl->size = 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(symtabshdr->sh_size, fn, "symbol table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, symtabp, symtabshdr->sh_offset, symtabshdr->sh_size,
+ fn) != symtabshdr->sh_size)
+ goto bad;
+
+ /* string table */
+ if ((strtabp = xmalloc(strtabshdr->sh_size, fn, "string table"))
+ == NULL)
+ goto bad;
+ if (xreadatoff(fd, strtabp, strtabshdr->sh_offset, strtabshdr->sh_size,
+ fn) != 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 = symtabshdr->sh_size / symtabshdr->sh_entsize;
+ nlocalsyms = 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 + sp->st_name))
+ continue;
+
+ /* if it's an undefined symbol, keep it */
+ if (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 = 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 =
+#if (ELFSIZE == 32) /* XXX */
+ symfwmap[ELF_R_SYM(relap[ewi].r_info)] << 8 |
+ ELF_R_TYPE(relap[ewi].r_info);
+#elif (ELFSIZE == 64) /* XXX */
+ symfwmap[ELF_R_SYM(relap[ewi].r_info)] << 32 |
+ ELF_R_TYPE(relap[ewi].r_info);
+#endif /* XXX */
+ }
+ }
+
+ /* 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 =
+#if (ELFSIZE == 32) /* XXX */
+ symfwmap[ELF_R_SYM(relp[ewi].r_info)] << 8 |
+ ELF_R_TYPE(relp[ewi].r_info);
+#elif (ELFSIZE == 64) /* XXX */
+ symfwmap[ELF_R_SYM(relp[ewi].r_info)] << 32 |
+ ELF_R_TYPE(relp[ewi].r_info);
+#endif /* XXX */
+ }
+ }
+
+ /*
+ * write new tables to the file
+ */
+ if (xwriteatoff(fd, shdrp, ehdr.e_shoff, shdrsize, fn) != shdrsize)
+ goto bad;
+ if (xwriteatoff(fd, symtabp, symtabshdr->sh_offset,
+ symtabshdr->sh_size, fn) != 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..a3d6586
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/extern.h
@@ -0,0 +1,68 @@
+/* $NetBSD: extern.h,v 1.5 1998/05/06 13:16:57 mycroft 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.
+ */
+
+#ifdef __alpha__
+#define NLIST_ECOFF
+#define NLIST_ELF64
+#else
+#ifdef __mips__
+#define NLIST_ELF32
+#else
+#ifdef __powerpc__
+#define NLIST_ELF32
+#else
+#define NLIST_AOUT
+/* #define NLIST_ECOFF */
+#define NLIST_ELF32
+/* #define NLIST_ELF64 */
+#endif
+#endif
+#endif
+
+#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..861e302
--- /dev/null
+++ b/usr.sbin/crunch/examples/Makefile
@@ -0,0 +1,32 @@
+
+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=
+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..18fd8bf
--- /dev/null
+++ b/usr.sbin/crunch/examples/filesystem.conf
@@ -0,0 +1,31 @@
+# $Id$
+
+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..c0ddbc6
--- /dev/null
+++ b/usr.sbin/crunch/examples/kcopy.conf
@@ -0,0 +1,21 @@
+# $Id$
+
+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..17c7c64
--- /dev/null
+++ b/usr.sbin/crunch/examples/really-big.conf
@@ -0,0 +1,155 @@
+# 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?
+
+# =========================================================================
+
+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_fdesc mount_isofs
+progs mount_kernfs mount_lofs mount_msdos mount_portal 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..89ae78a
--- /dev/null
+++ b/usr.sbin/ctm/Makefile
@@ -0,0 +1,5 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:05:14 peter Exp $
+
+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..2ecf884
--- /dev/null
+++ b/usr.sbin/ctm/Makefile.inc
@@ -0,0 +1,5 @@
+# $Id$
+
+.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..d6b94f5
--- /dev/null
+++ b/usr.sbin/ctm/README
@@ -0,0 +1,97 @@
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> 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: README,v 1.6 1997/02/22 16:05:15 peter Exp $
+#
+
+What will I not find in this file ?
+-----------------------------------
+Instructions on how to obtain FreeBSD via CTM.
+Contact <phk@freefall.cdrom.com> 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 ?
+---------------------------
+Don't. Send me email before you even try. This is yet not quite as trivial
+as I would like. This is not to discourage you from using CTM, it is merely
+to warn you that it is slightly tedious and takes much diskspace.
+
+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.
+
+What features are are planned ?
+-------------------------------
+This list isn't exhaustive, and it isn't sorted in priority.
+ Reconstruct subset of tree.
+ Make tar-copy of things which will be affected.
+ Verify.
+ Internal editor instead of ed(1)
+ Support for mode
+ Support for uid/gid
+ Support for hardlinks
+ Support for symlinks
+
+Isn't this a bit thin yet ?
+---------------------------
+Yes.
+
+Can I say something ?
+---------------------
+Yes, email me: <phk@freefall.cdrom.com>
+
+Poul-Henning
diff --git a/usr.sbin/ctm/ctm/Makefile b/usr.sbin/ctm/ctm/Makefile
new file mode 100644
index 0000000..43fb100
--- /dev/null
+++ b/usr.sbin/ctm/ctm/Makefile
@@ -0,0 +1,25 @@
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> 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$
+#
+
+PROG= ctm
+NOTYET= ctm_ed.c
+SRCS= ctm.c ctm_input.c ctm_pass1.c ctm_pass2.c ctm_pass3.c \
+ ctm_passb.c ctm_syntax.c ctm_ed.c
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+MAN1= ctm.1
+MAN5= ctm.5
+CFLAGS+= -Wall
+
+.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..e1fa52d
--- /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@login.dknet.dk>
+.\"
+.\" $Id: ctm.1,v 1.13 1997/09/17 06:24:44 charnier Exp $
+.\"
+.Dd Mar 25, 1995
+.Os
+.Dt CTM 1
+.Sh NAME
+.Nm ctm
+.Nd source code mirror program
+.Sh SYNOPSIS
+.Nm ctm
+.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
+.Nm Ctm
+was originally
+.Dq Cvs Through eMail ,
+but now instead it seems more fitting to call it
+.Dq Current Through eMail .
+
+.Nm 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.
+
+.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.
+
+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.
+
+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.
+
+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.
+
+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.
+
+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 -" .
+
+Pass 3 will actually apply the delta.
+
+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 ctm .
+
+.Nm Ctm
+will extract the file hierarchy below its working directory. Absolute
+filenames or filenames containing references through
+.Sq \&.
+and
+.Sq \&.\&.
+are explicitly prohibited as a security measure.
+
+.Ss Options
+
+.Bl -tag -width indent -compact
+
+.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
+.Nm usr.sbin/ctm
+source directory and all pathnames under it.
+
+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.
+
+Pathnames can be selected for CTM's consideration using the
+.Fl e
+option.
+
+.El
+
+.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 ctm .
+
+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
+
+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.
+
+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 .
+
+.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..55e6ef2
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.5
@@ -0,0 +1,214 @@
+.\"----------------------------------------------------------------------------
+.\""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@login.dknet.dk>
+.\"
+.\" $Id: ctm.5,v 1.6 1997/09/17 06:24:44 charnier Exp $
+.\"
+.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.
+
+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.
+
+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.
+
+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
+.Pq 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
+.Po
+followed by the letter
+.Sq Z
+meaning this is a UTC timestamp
+.Pc .
+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
+.Pq numerical, decimal ,
+the gid
+.Ar gid
+.Pq numerical, decimal ,
+mode
+.Ar mode
+.Pq numerical, octal ,
+and the MD5 checksum
+.Ar md5 .
+
+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
+.Pq numerical, decimal ,
+the new gid
+.Ar gid
+.Pq numerical, decimal ,
+new mode
+.Ar mode
+.Pq numerical, octal ,
+the old MD5 checksum
+.Ar md5before ,
+and the new MD5 checksum
+.Ar md5after .
+
+The following
+.Ar count
+bytes data are the contents of the new file.
+
+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 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
+.Pq 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 .
+
+.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..a05b2d5
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.c
@@ -0,0 +1,318 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: ctm.c,v 1.16 1997/02/22 16:05:21 peter Exp $
+ *
+ * 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 "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;
+ 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 = tempnam(TmpDir,"CTMclient");
+ FILE *f2 = fopen(fn,"w+");
+ int i;
+
+ if(!f2) {
+ warn("%s", fn);
+ fclose(f);
+ return Exit_Broke;
+ }
+ unlink(fn);
+ if (Verbose > 0)
+ fprintf(stderr,"Writing tmp-file \"%s\"\n",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..c53eb44
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.h
@@ -0,0 +1,164 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: ctm.h,v 1.12 1997/02/22 16:05:21 peter Exp $
+ *
+ */
+
+#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 MAXSIZE (1024*1024*10)
+
+#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..31c5729
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_ed.c
@@ -0,0 +1,114 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: ctm_ed.c,v 1.8 1997/02/22 16:05:23 peter Exp $
+ *
+ */
+
+#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..2bbbbb6
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_input.c
@@ -0,0 +1,138 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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$
+ *
+ */
+
+#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');
+ }
+ if(u_chars > MAXSIZE) {
+ Fatal("Bytecount too large.");
+ return -1;
+ }
+ 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..b98c0d1
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass1.c
@@ -0,0 +1,250 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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$
+ *
+ */
+
+#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..7c38ff0
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass2.c
@@ -0,0 +1,245 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: ctm_pass2.c,v 1.15 1997/02/22 16:05:26 peter Exp $
+ *
+ */
+
+#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;
+ 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;
+
+ 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);
+ 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;
+ }
+ if(j & CTM_Q_MD5_After) {
+ 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(!strcmp(sp->Key,"FN")) {
+ p = tempnam(TmpDir,"CTMclient");
+ 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")) {
+ p = tempnam(TmpDir,"CTMclient");
+ 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..9ca19d8
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass3.c
@@ -0,0 +1,295 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: ctm_pass3.c,v 1.17 1997/02/22 16:05:27 peter Exp $
+ *
+ */
+
+#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..0cf657f
--- /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
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm_passb.c,v 1.3 1997/02/22 16:05:28 peter Exp $
+ *
+ */
+
+#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);
+ sprintf(buf, 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..509aef1
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_syntax.c
@@ -0,0 +1,67 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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$
+ *
+ */
+
+#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..543e77e
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/Makefile
@@ -0,0 +1,8 @@
+PROG= ctm_dequeue
+SRCS= ctm_dequeue.c error.c
+NOMAN= yes
+CFLAGS+= -Wall -I${.CURDIR}/../ctm_rmail
+
+.PATH: ${.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..9e09864
--- /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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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 FTSENT **);
+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 ** a, const FTSENT ** 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", 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..f904368
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/Makefile
@@ -0,0 +1,7 @@
+PROG= ctm_rmail
+SRCS= ctm_rmail.c error.c
+CFLAGS+= -Wall
+MLINKS+= ctm_rmail.1 ctm_smail.1
+MLINKS+= ctm_rmail.1 ctm_dequeue.1
+
+.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..a7d9f8e
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
@@ -0,0 +1,479 @@
+.\" 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
+.\"
+.Dd January 24, 1995
+.Dt CTM_MAIL 1
+.Os
+.Sh NAME
+.Nm ctm_smail, ctm_rmail
+.Nd send and receive
+.Nm ctm
+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.
+.Nm ctm_smail
+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 FreeBSD-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
+.nh
+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 ftpmail, or your good mate at the university.
+.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/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
+on the (non-FreeBSD) machine that this example was taken from.
+.Sh SECURITY
+If you automatically take your mail and pass it to a file tree patcher, you
+might think you are handing the keys to your system to the crackers! Happily,
+the window for mischief is quite small.
+.Nm ctm_rmail
+is careful to write only to the directories given to it (by not believing any
+.Dq /
+characters in the delta name), and the latest
+.Xr ctm
+disallows absolute pathnames and
+.Dq \&\.\.
+in files it manipulates, so the worst you
+could lose are a few source tree files (recoverable from your deltas).
+Since
+.Xr ctm
+requires that a
+.Xr md5
+checksum match before it touches a file, only fellow
+source recipients would be able to generate a fake delta, and they're such
+nice folk that they wouldn't even think of it! :-)
+.Pp
+Even this possibility could be removed by using cryptographic signatures.
+A possible future enhancement would be to use
+.Nm PGP
+to provide a secure wrapper.
+.\" 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.
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" (command return values (to shell) and fprintf/stderr type diagnostics)
+.Sh DIAGNOSTICS
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+return exit status 0 for success, and 1 for various failures.
+.Nm ctm_rmail
+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
+.Nm ctm_dequeue
+will report messages like:
+.Bd -literal -offset indent
+ctm_dequeue: src-cur.0250.gz 1/2 sent
+.Ed
+.Pp
+.Nm ctm_rmail
+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
+.sp \n(Ppu
+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
+turn up here too. Error messages should be self explanatory.
+.\" The next request is for sections 2 and 3 error and signal handling only.
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm 5
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.Sh AUTHORS
+.An Stephen McKay Aq mckay@FreeBSD.org
+.\" .Sh BUGS
+.\" Gosh! No bugs here!
+.\" This message brought to you by the Coalition for More Humour in Man Pages.
+.\"
+.\" $Id: ctm_rmail.1,v 1.13 1998/03/23 08:22:25 charnier Exp $
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..a456cbe
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
@@ -0,0 +1,663 @@
+/*
+ * 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.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.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. */
+
+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;
+
+ 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, "%s %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 ofd;
+ 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;
+
+ if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
+ delta, &pce, &npieces, junk) != 3)
+ continue;
+
+ while ((s = strchr(delta, '/')) != NULL)
+ *s = '_';
+
+ got_one++;
+ strcpy(tname, piece_dir);
+ strcat(tname, "/p.XXXXXX");
+ if ((ofd = mkstemp(tname)) < 0)
+ {
+ err("*mkstemp: '%s'", tname);
+ status++;
+ continue;
+ }
+ if ((ofp = fdopen(ofd, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", 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)
+ {
+ 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 dfd;
+ int i, n, e;
+ char buf[BUFSIZ];
+
+ strcpy(tname, delta_dir);
+ strcat(tname, "/d.XXXXXX");
+ if ((dfd = mkstemp(tname)) < 0)
+ {
+ err("*mkstemp: '%s'", tname);
+ return 0;
+ }
+ if ((dfp = fdopen(dfd, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", 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;
+ }
+
+ /*
+ * 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..724b117
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.c
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#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(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..b8bc452
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.h
@@ -0,0 +1,3 @@
+extern void err_set_log(char *log_file);
+extern void err_prog_name(char *name);
+extern void err(char *fmt, ...);
diff --git a/usr.sbin/ctm/ctm_rmail/options.h b/usr.sbin/ctm/ctm_rmail/options.h
new file mode 100644
index 0000000..18b844c
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/options.h
@@ -0,0 +1,137 @@
+/*
+ * 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).
+ */
+
+static char *O_usage;
+static char *O_name;
+extern long atol();
+
+void
+pusage()
+ {
+ /*
+ * Avoid gratuitously loading stdio.
+ */
+ write(2, "Usage: ", 7);
+ write(2, O_name, strlen(O_name));
+ write(2, " ", 1);
+ write(2, O_usage, strlen(O_usage));
+ write(2, "\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..e9eb846
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/Makefile
@@ -0,0 +1,7 @@
+PROG= ctm_smail
+SRCS= ctm_smail.c error.c
+NOMAN= 1
+CFLAGS+= -Wall -I${.CURDIR}/../ctm_rmail
+.PATH: ${.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..afd7cdd
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/ctm_smail.c
@@ -0,0 +1,469 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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);
+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);
+ 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)
+ {
+ FILE *sfp;
+
+ sfp = open_sendmail();
+ if (sfp == NULL)
+ 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 ftpmail, "
+ "or your good mate at the university.\n");
+
+ if (!close_sendmail(sfp))
+ 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..4e67fcd
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/Makefile
@@ -0,0 +1,25 @@
+
+PROG= mkctm
+SRCS= mkctm.c
+LDADD= -lmd
+NOMAN= 1
+CFLAGS= -g -Wall
+
+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..c8fe67d
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/dequeue
@@ -0,0 +1,6 @@
+#! /bin/sh
+# $Id$
+
+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..a728a78
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkCTM
@@ -0,0 +1,186 @@
+#!/usr/local/bin/tclsh7.4
+
+#############################################################################
+### 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 a 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..55e2361
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkctm.c
@@ -0,0 +1,593 @@
+/* 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"
+ *
+ */
+
+#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 <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 = alloca(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;
+ }
+ }
+ 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;
+ extern char *optarg;
+ extern int optind;
+
+ 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);
+ break;
+ case 'q':
+ verbose--;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ Usage();
+ return (1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!logf)
+ logf = fopen("/dev/null", "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/dev_mkdb/Makefile b/usr.sbin/dev_mkdb/Makefile
new file mode 100644
index 0000000..8737b50
--- /dev/null
+++ b/usr.sbin/dev_mkdb/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= dev_mkdb
+MAN8= dev_mkdb.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dev_mkdb/dev_mkdb.8 b/usr.sbin/dev_mkdb/dev_mkdb.8
new file mode 100644
index 0000000..5488a93
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.8
@@ -0,0 +1,81 @@
+.\" 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.
+.\"
+.\" @(#)dev_mkdb.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Os
+.Dt DEV_MKDB 8
+.Sh NAME
+.Nm dev_mkdb
+.Nd create
+.Pa /dev
+database
+.Sh SYNOPSIS
+.Nm dev_mkdb
+.Sh DESCRIPTION
+The
+.Nm
+command creates a
+.Xr db 3
+hash access method database in
+.Pa /var/run/dev.db
+which contains the names of all of the character and block special
+files in the
+.Pa /dev
+directory, using the file type and the
+.Fa st_rdev
+field as the key.
+.Pp
+Keys are a structure containing a mode_t followed by a dev_t,
+with any padding zero'd out.
+The former is the type of the file (st_mode & S_IFMT),
+the latter is the st_rdev field.
+.Sh FILES
+.Bl -tag -width /var/run/dev.db -compact
+.It Pa /dev
+Device directory.
+.It Pa /var/run/dev.db
+Database file.
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr stat 2 ,
+.Xr db 3 ,
+.Xr devname 3 ,
+.Xr kvm_nlist 3 ,
+.Xr ttyname 3 ,
+.Xr kvm_mkdb 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/dev_mkdb/dev_mkdb.c b/usr.sbin/dev_mkdb/dev_mkdb.c
new file mode 100644
index 0000000..ef113d1
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.c
@@ -0,0 +1,153 @@
+/*-
+ * 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 const 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[] = "@(#)dev_mkdb.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <db.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register DIR *dirp;
+ register struct dirent *dp;
+ struct stat sb;
+ struct {
+ mode_t type;
+ dev_t dev;
+ } bkey;
+ DB *db;
+ DBT data, key;
+ int ch;
+ u_char buf[MAXNAMLEN + 1];
+ char dbtmp[MAXPATHLEN + 1], dbname[MAXPATHLEN + 1];
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch((char)ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ usage();
+
+ if (chdir(_PATH_DEV))
+ err(1, "%s", _PATH_DEV);
+
+ dirp = opendir(".");
+
+ (void)snprintf(dbtmp, sizeof(dbtmp), "%sdev.tmp", _PATH_VARRUN);
+ (void)snprintf(dbname, sizeof(dbtmp), "%sdev.db", _PATH_VARRUN);
+ db = dbopen(dbtmp, O_CREAT|O_EXLOCK|O_RDWR|O_TRUNC,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, DB_HASH, NULL);
+ if (db == NULL)
+ err(1, "%s", dbtmp);
+
+ /*
+ * Keys are a mode_t followed by a dev_t. The former is the type of
+ * the file (mode & S_IFMT), the latter is the st_rdev field. Note
+ * that the structure may contain padding, so we have to clear it
+ * out here.
+ */
+ bzero(&bkey, sizeof(bkey));
+ key.data = &bkey;
+ key.size = sizeof(bkey);
+ data.data = buf;
+ while ((dp = readdir(dirp))) {
+ if (lstat(dp->d_name, &sb)) {
+ warn("%s", dp->d_name);
+ continue;
+ }
+
+ /* Create the key. */
+ if (S_ISCHR(sb.st_mode))
+ bkey.type = S_IFCHR;
+ else if (S_ISBLK(sb.st_mode))
+ bkey.type = S_IFBLK;
+ else
+ continue;
+ bkey.dev = sb.st_rdev;
+
+ /*
+ * Create the data; nul terminate the name so caller doesn't
+ * have to.
+ */
+ bcopy(dp->d_name, buf, dp->d_namlen);
+ buf[dp->d_namlen] = '\0';
+ data.size = dp->d_namlen + 1;
+ if ((db->put)(db, &key, &data, 0))
+ err(1, "dbput %s", dbtmp);
+ }
+ (void)(db->close)(db);
+ if (rename(dbtmp, dbname))
+ err(1, "rename %s to %s", dbtmp, dbname);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: dev_mkdb\n");
+ exit(1);
+}
diff --git a/usr.sbin/diskpart/Makefile b/usr.sbin/diskpart/Makefile
new file mode 100644
index 0000000..bf623cb
--- /dev/null
+++ b/usr.sbin/diskpart/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= diskpart
+MAN8= diskpart.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/diskpart/diskpart.8 b/usr.sbin/diskpart/diskpart.8
new file mode 100644
index 0000000..e13613a
--- /dev/null
+++ b/usr.sbin/diskpart/diskpart.8
@@ -0,0 +1,143 @@
+.\" 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.
+.\"
+.\" @(#)diskpart.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt DISKPART 8
+.Os BSD 4
+.Sh NAME
+.Nm diskpart
+.Nd calculate default disk partition sizes
+.Sh SYNOPSIS
+.Nm diskpart
+.Op Fl p
+.Op Fl d
+.Op Fl s Ar size
+.Ar disk-type
+.Sh DESCRIPTION
+.Nm Diskpart
+is used to calculate the disk partition sizes based on the
+default rules used at Berkeley.
+.Pp
+Available options and operands:
+.Bl -tag -width Fl
+.It Fl p
+Tables suitable for inclusion in a device driver
+are produced.
+.It Fl d
+An entry suitable for inclusion in the disk
+description file
+.Pa /etc/disktab
+is generated; for example,
+.Xr disktab 5 .
+.It Fl s Ar size
+The size of the disk may be limited to
+.Ar size
+with the
+.Fl s
+option.
+.El
+.Pp
+On disks that use
+.Xr bad144 8
+type of
+bad-sector forwarding,
+space is normally left in the last partition on the disk
+for a bad sector forwarding table, although this space
+is not reflected in the tables produced. The space reserved
+is one track for the replicated copies of the table and
+sufficient tracks to hold a pool of 126 sectors to which bad sectors
+are mapped. For more information, see
+.Xr bad144 8 .
+The
+.Fl s
+option is intended for other controllers which reserve some space at the end
+of the disk for bad-sector replacements or other control areas,
+even if not a multiple of cylinders.
+.Pp
+The disk partition sizes are based on the total amount of
+space on the disk as given in the table below (all values
+are supplied in units of sectors). The
+.Ql c
+partition
+is, by convention, used to access the entire physical disk.
+The device driver tables include
+the space reserved for the bad sector forwarding table in the
+.Ql c
+partition;
+those used in the disktab and default formats exclude reserved tracks.
+In normal operation, either the
+.Ql g
+partition is used, or the
+.Ql d ,
+.Ql e ,
+and
+.Ql f
+partitions are used. The
+.Ql g
+and
+.Ql f
+partitions
+are variable-sized, occupying whatever space remains after allocation
+of the fixed sized partitions.
+If the disk is smaller than 20 Megabytes, then
+.Nm
+aborts with the message
+.Dq Li disk too small, calculate by hand .
+.Bl -column Partition 20-60\ MB 61-205\ MB 206-355\ MB 356+\ MB
+Partition 20-60 MB 61-205 MB 206-355 MB 356+ MB
+a 15884 15884 15884 15884
+b 10032 33440 33440 66880
+d 15884 15884 15884 15884
+e unused 55936 55936 307200
+h unused unused 291346 291346
+.El
+.Pp
+If an unknown disk type is specified,
+.Nm
+will prompt for the required disk geometry information.
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr bad144 8
+.Sh BUGS
+Most default partition sizes are based on historical artifacts
+(like the RP06), and may result in unsatisfactory layouts.
+.Pp
+When using the
+.Fl d
+flag, alternate disk names are not included
+in the output.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/diskpart/diskpart.c b/usr.sbin/diskpart/diskpart.c
new file mode 100644
index 0000000..9b36e46
--- /dev/null
+++ b/usr.sbin/diskpart/diskpart.c
@@ -0,0 +1,496 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)diskpart.c 8.3 (Berkeley) 11/30/94";
+#endif
+static const char rcsid[] =
+ "$Id: diskpart.c,v 1.9 1997/09/17 06:27:23 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Program to calculate standard disk partition sizes.
+ */
+#include <sys/param.h>
+#define DKTYPENAMES
+#include <sys/disklabel.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+
+#define for_now /* show all of `c' partition for disklabel */
+#define NPARTITIONS 8
+#define PART(x) (x - 'a')
+
+/*
+ * Default partition sizes, where they exist.
+ */
+#define NDEFAULTS 4
+int defpart[NDEFAULTS][NPARTITIONS] = {
+ { 15884, 66880, 0, 15884, 307200, 0, 0, 291346 }, /* ~ 356+ Mbytes */
+ { 15884, 33440, 0, 15884, 55936, 0, 0, 291346 }, /* ~ 206-355 Mbytes */
+ { 15884, 33440, 0, 15884, 55936, 0, 0, 0 }, /* ~ 61-205 Mbytes */
+ { 15884, 10032, 0, 15884, 0, 0, 0, 0 }, /* ~ 20-60 Mbytes */
+};
+
+/*
+ * Each array defines a layout for a disk;
+ * that is, the collection of partitions totally
+ * covers the physical space on a disk.
+ */
+#define NLAYOUTS 3
+char layouts[NLAYOUTS][NPARTITIONS] = {
+ { 'a', 'b', 'h', 'g' },
+ { 'a', 'b', 'h', 'd', 'e', 'f' },
+ { 'c' },
+};
+
+/*
+ * Default disk block and disk block fragment
+ * sizes for each file system. Those file systems
+ * with zero block and frag sizes are special cases
+ * (e.g. swap areas or for access to the entire device).
+ */
+struct partition defparam[NPARTITIONS] = {
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* a */
+ { 0, 0, 1024, FS_SWAP, 8, 0 }, /* b */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* c */
+ { 0, 0, 512, FS_UNUSED, 8, 0 }, /* d */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* e */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* f */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* g */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 } /* h */
+};
+
+/*
+ * Each disk has some space reserved for a bad sector
+ * forwarding table. DEC standard 144 uses the first
+ * 5 even numbered sectors in the last track of the
+ * last cylinder for replicated storage of the bad sector
+ * table; another 126 sectors past this is needed as a
+ * pool of replacement sectors.
+ */
+int badsecttable = 126; /* # sectors */
+
+int pflag; /* print device driver partition tables */
+int dflag; /* print disktab entry */
+
+struct disklabel *promptfordisk();
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct disklabel *dp;
+ register int curcyl, spc, def, part, layout, j;
+ int threshhold, numcyls[NPARTITIONS], startcyl[NPARTITIONS];
+ int totsize = 0;
+ char *lp, *tyname;
+
+ argc--, argv++;
+ if (argc < 1)
+ usage();
+ if (argc > 0 && strcmp(*argv, "-p") == 0) {
+ pflag++;
+ argc--, argv++;
+ }
+ if (argc > 0 && strcmp(*argv, "-d") == 0) {
+ dflag++;
+ argc--, argv++;
+ }
+ if (argc > 1 && strcmp(*argv, "-s") == 0) {
+ totsize = atoi(argv[1]);
+ argc += 2, argv += 2;
+ }
+ dp = getdiskbyname(*argv);
+ if (dp == NULL) {
+ if (isatty(0))
+ dp = promptfordisk(*argv);
+ if (dp == NULL)
+ errx(2, "%s: unknown disk type", *argv);
+ } else {
+ if (dp->d_flags & D_REMOVABLE)
+ tyname = "removable";
+ else if (dp->d_flags & D_RAMDISK)
+ tyname = "simulated";
+ else
+ tyname = "winchester";
+ }
+ spc = dp->d_secpercyl;
+ /*
+ * Bad sector table contains one track for the replicated
+ * copies of the table and enough full tracks preceding
+ * the last track to hold the pool of free blocks to which
+ * bad sectors are mapped.
+ * If disk size was specified explicitly, use specified size.
+ */
+ if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT &&
+ totsize == 0) {
+ badsecttable = dp->d_nsectors +
+ roundup(badsecttable, dp->d_nsectors);
+ threshhold = howmany(spc, badsecttable);
+ } else {
+ badsecttable = 0;
+ threshhold = 0;
+ }
+ /*
+ * If disk size was specified, recompute number of cylinders
+ * that may be used, and set badsecttable to any remaining
+ * fraction of the last cylinder.
+ */
+ if (totsize != 0) {
+ dp->d_ncylinders = howmany(totsize, spc);
+ badsecttable = spc * dp->d_ncylinders - totsize;
+ }
+
+ /*
+ * Figure out if disk is large enough for
+ * expanded swap area and 'd', 'e', and 'f'
+ * partitions. Otherwise, use smaller defaults
+ * based on RK07.
+ */
+ for (def = 0; def < NDEFAULTS; def++) {
+ curcyl = 0;
+ for (part = PART('a'); part < NPARTITIONS; part++)
+ curcyl += howmany(defpart[def][part], spc);
+ if (curcyl < dp->d_ncylinders - threshhold)
+ break;
+ }
+ if (def >= NDEFAULTS)
+ errx(3, "%s: disk too small, calculate by hand", *argv);
+
+ /*
+ * Calculate number of cylinders allocated to each disk
+ * partition. We may waste a bit of space here, but it's
+ * in the interest of (very backward) compatibility
+ * (for mixed disk systems).
+ */
+ for (curcyl = 0, part = PART('a'); part < NPARTITIONS; part++) {
+ numcyls[part] = 0;
+ if (defpart[def][part] != 0) {
+ numcyls[part] = howmany(defpart[def][part], spc);
+ curcyl += numcyls[part];
+ }
+ }
+ numcyls[PART('f')] = dp->d_ncylinders - curcyl;
+ numcyls[PART('g')] =
+ numcyls[PART('d')] + numcyls[PART('e')] + numcyls[PART('f')];
+ numcyls[PART('c')] = dp->d_ncylinders;
+ defpart[def][PART('f')] = numcyls[PART('f')] * spc - badsecttable;
+ defpart[def][PART('g')] = numcyls[PART('g')] * spc - badsecttable;
+ defpart[def][PART('c')] = numcyls[PART('c')] * spc;
+#ifndef for_now
+ if (totsize || !pflag)
+#else
+ if (totsize)
+#endif
+ defpart[def][PART('c')] -= badsecttable;
+
+ /*
+ * Calculate starting cylinder number for each partition.
+ * Note the 'h' partition is physically located before the
+ * 'g' or 'd' partition. This is reflected in the layout
+ * arrays defined above.
+ */
+ for (layout = 0; layout < NLAYOUTS; layout++) {
+ curcyl = 0;
+ for (lp = layouts[layout]; *lp != 0; lp++) {
+ startcyl[PART(*lp)] = curcyl;
+ curcyl += numcyls[PART(*lp)];
+ }
+ }
+
+ if (pflag) {
+ printf("}, %s_sizes[%d] = {\n", dp->d_typename, NPARTITIONS);
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ if (numcyls[part] == 0) {
+ printf("\t0,\t0,\n");
+ continue;
+ }
+ if (dp->d_type != DTYPE_MSCP) {
+ printf("\t%d,\t%d,\t\t/* %c=cyl %d thru %d */\n",
+ defpart[def][part], startcyl[part],
+ 'A' + part, startcyl[part],
+ startcyl[part] + numcyls[part] - 1);
+ continue;
+ }
+ printf("\t%d,\t%d,\t\t/* %c=sectors %d thru %d */\n",
+ defpart[def][part], spc * startcyl[part],
+ 'A' + part, spc * startcyl[part],
+ spc * startcyl[part] + defpart[def][part] - 1);
+ }
+ exit(0);
+ }
+ if (dflag) {
+ int nparts;
+
+ /*
+ * In case the disk is in the ``in-between'' range
+ * where the 'g' partition is smaller than the 'h'
+ * partition, reverse the frag sizes so the /usr partition
+ * is always set up with a frag size larger than the
+ * user's partition.
+ */
+ if (defpart[def][PART('g')] < defpart[def][PART('h')]) {
+ int temp;
+
+ temp = defparam[PART('h')].p_fsize;
+ defparam[PART('h')].p_fsize =
+ defparam[PART('g')].p_fsize;
+ defparam[PART('g')].p_fsize = temp;
+ }
+ printf("%s:\\\n", dp->d_typename);
+ printf("\t:ty=%s:ns#%d:nt#%d:nc#%d:", tyname,
+ dp->d_nsectors, dp->d_ntracks, dp->d_ncylinders);
+ if (dp->d_secpercyl != dp->d_nsectors * dp->d_ntracks)
+ printf("sc#%d:", dp->d_secpercyl);
+ if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT)
+ printf("sf:");
+ printf("\\\n\t:dt=%s:", dktypenames[dp->d_type]);
+ for (part = NDDATA - 1; part >= 0; part--)
+ if (dp->d_drivedata[part])
+ break;
+ for (j = 0; j <= part; j++)
+ printf("d%d#%d:", j, dp->d_drivedata[j]);
+ printf("\\\n");
+ for (nparts = 0, part = PART('a'); part < NPARTITIONS; part++)
+ if (defpart[def][part] != 0)
+ nparts++;
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ if (defpart[def][part] == 0)
+ continue;
+ printf("\t:p%c#%d:", 'a' + part, defpart[def][part]);
+ printf("o%c#%d:b%c#%d:f%c#%d:",
+ 'a' + part, spc * startcyl[part],
+ 'a' + part,
+ defparam[part].p_frag * defparam[part].p_fsize,
+ 'a' + part, defparam[part].p_fsize);
+ if (defparam[part].p_fstype == FS_SWAP)
+ printf("t%c=swap:", 'a' + part);
+ nparts--;
+ printf("%s\n", nparts > 0 ? "\\" : "");
+ }
+#ifdef for_now
+ defpart[def][PART('c')] -= badsecttable;
+ part = PART('c');
+ printf("#\t:p%c#%d:", 'a' + part, defpart[def][part]);
+ printf("o%c#%d:b%c#%d:f%c#%d:\n",
+ 'a' + part, spc * startcyl[part],
+ 'a' + part,
+ defparam[part].p_frag * defparam[part].p_fsize,
+ 'a' + part, defparam[part].p_fsize);
+#endif
+ exit(0);
+ }
+ printf("%s: #sectors/track=%d, #tracks/cylinder=%d #cylinders=%d\n",
+ dp->d_typename, dp->d_nsectors, dp->d_ntracks,
+ dp->d_ncylinders);
+ printf("\n Partition\t Size\t Offset\t Range\n");
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ printf("\t%c\t", 'a' + part);
+ if (numcyls[part] == 0) {
+ printf(" unused\n");
+ continue;
+ }
+ printf("%7d\t%7d\t%4d - %d%s\n",
+ defpart[def][part], startcyl[part] * spc,
+ startcyl[part], startcyl[part] + numcyls[part] - 1,
+ defpart[def][part] % spc ? "*" : "");
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: disktab [-p] [-d] [-s size] disk-type\n");
+ exit(1);
+}
+
+struct disklabel disk;
+
+struct field {
+ char *f_name;
+ char *f_defaults;
+ u_int32_t *f_location;
+} fields[] = {
+ { "sector size", "512", &disk.d_secsize },
+ { "#sectors/track", 0, &disk.d_nsectors },
+ { "#tracks/cylinder", 0, &disk.d_ntracks },
+ { "#cylinders", 0, &disk.d_ncylinders },
+ { 0, 0, 0 },
+};
+
+char *
+mygets(buf)
+ char *buf;
+{
+ size_t len;
+
+ if (fgets(buf, BUFSIZ, stdin) == NULL)
+ buf[0] = '\0';
+ len = strlen(buf);
+ if (len != 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ return (buf);
+}
+
+struct disklabel *
+promptfordisk(name)
+ char *name;
+{
+ struct disklabel *dp = &disk;
+ struct field *fp;
+ int i;
+ char buf[BUFSIZ], **tp, *cp;
+
+ strncpy(dp->d_typename, name, sizeof(dp->d_typename));
+ fprintf(stderr,
+ "%s: unknown disk type, want to supply parameters (y/n)? ",
+ name);
+ (void) mygets(buf);
+ if (*buf != 'y')
+ return ((struct disklabel *)0);
+ for (;;) {
+ fprintf(stderr, "Disk/controller type (%s)? ", dktypenames[1]);
+ (void) mygets(buf);
+ if (buf[0] == 0) {
+ dp->d_type = 1;
+ break;
+ }
+ if ((i = gettype(buf, dktypenames)) >= 0) {
+ dp->d_type = i;
+ break;
+ }
+ fprintf(stderr, "%s: unrecognized controller type\n", buf);
+ fprintf(stderr, "use one of:\n");
+ for (tp = dktypenames; *tp; tp++)
+ if (index(*tp, ' ') == 0)
+ fprintf(stderr, "\t%s\n", *tp);
+ }
+gettype:
+ dp->d_flags = 0;
+ fprintf(stderr, "type (winchester|removable|simulated)? ");
+ (void) mygets(buf);
+ if (strcmp(buf, "removable") == 0)
+ dp->d_flags = D_REMOVABLE;
+ else if (strcmp(buf, "simulated") == 0)
+ dp->d_flags = D_RAMDISK;
+ else if (strcmp(buf, "winchester")) {
+ fprintf(stderr, "%s: bad disk type\n", buf);
+ goto gettype;
+ }
+ strncpy(dp->d_typename, buf, sizeof(dp->d_typename));
+ fprintf(stderr, "(type <cr> to get default value, if only one)\n");
+ if (dp->d_type == DTYPE_SMD)
+ fprintf(stderr, "Do %ss support bad144 bad block forwarding (yes)? ",
+ dp->d_typename);
+ (void) mygets(buf);
+ if (*buf != 'n')
+ dp->d_flags |= D_BADSECT;
+ for (fp = fields; fp->f_name != NULL; fp++) {
+again:
+ fprintf(stderr, "%s ", fp->f_name);
+ if (fp->f_defaults != NULL)
+ fprintf(stderr, "(%s)", fp->f_defaults);
+ fprintf(stderr, "? ");
+ cp = mygets(buf);
+ if (*cp == '\0') {
+ if (fp->f_defaults == NULL) {
+ fprintf(stderr, "no default value\n");
+ goto again;
+ }
+ cp = fp->f_defaults;
+ }
+ *fp->f_location = atol(cp);
+ if (*fp->f_location == 0) {
+ fprintf(stderr, "%s: bad value\n", cp);
+ goto again;
+ }
+ }
+ fprintf(stderr, "sectors/cylinder (%d)? ",
+ dp->d_nsectors * dp->d_ntracks);
+ (void) mygets(buf);
+ if (buf[0] == 0)
+ dp->d_secpercyl = dp->d_nsectors * dp->d_ntracks;
+ else
+ dp->d_secpercyl = atol(buf);
+ fprintf(stderr, "Drive-type-specific parameters, <cr> to terminate:\n");
+ for (i = 0; i < NDDATA; i++) {
+ fprintf(stderr, "d%d? ", i);
+ (void) mygets(buf);
+ if (buf[0] == 0)
+ break;
+ dp->d_drivedata[i] = atol(buf);
+ }
+ return (dp);
+}
+
+gettype(t, names)
+ char *t;
+ char **names;
+{
+ register char **nm;
+
+ for (nm = names; *nm; nm++)
+ if (ustrcmp(t, *nm) == 0)
+ return (nm - names);
+ if (isdigit(*t))
+ return (atoi(t));
+ return (-1);
+}
+
+ustrcmp(s1, s2)
+ register char *s1, *s2;
+{
+#define lower(c) (islower(c) ? (c) : tolower(c))
+
+ for (; *s1; s1++, s2++) {
+ if (*s1 == *s2)
+ continue;
+ if (isalpha(*s1) && isalpha(*s2) &&
+ lower(*s1) == lower(*s2))
+ continue;
+ return (*s2 - *s1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/dpt/Makefile b/usr.sbin/dpt/Makefile
new file mode 100644
index 0000000..63b2ffb
--- /dev/null
+++ b/usr.sbin/dpt/Makefile
@@ -0,0 +1,5 @@
+# $Id: Makefile,v 1.2 1998/01/21 07:46:46 ShimonR Exp ShimonR $
+
+SUBDIR= dpt_ctlinfo dpt_ctls dpt_dm dpt_led dpt_sig dpt_softc dpt_sysinfo
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/dpt/Makefile.inc b/usr.sbin/dpt/Makefile.inc
new file mode 100644
index 0000000..d0c4b25
--- /dev/null
+++ b/usr.sbin/dpt/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id: Makefile.inc,v 1.1 1998/01/26 06:20:35 julian Exp $
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/dpt/dpt_ctlinfo/Makefile b/usr.sbin/dpt/dpt_ctlinfo/Makefile
new file mode 100644
index 0000000..4f9c4c8
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctlinfo/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.3 1998/09/19 22:42:06 obrien Exp $
+
+PROG= dpt_ctlinfo
+SRCS= dpt_ctlinfo.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_ctlinfo.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.8 b/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.8
new file mode 100644
index 0000000..f9d1530
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.8
@@ -0,0 +1,3 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.c b/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.c
new file mode 100644
index 0000000..9a3e39b
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctlinfo/dpt_ctlinfo.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_ctlinfo.c: Dunp a DPT HBA Information Block */
+
+#ident "$Id: dpt_ctlinfo.c,v 1.1 1998/01/26 06:20:37 julian Exp $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#define DPT_MEASURE_PERFORMANCE
+
+#include <sys/dpt.h>
+
+
+int
+main(int argc, char **argv, char **argp)
+{
+ eata_pt_t pass_thru;
+ dpt_compat_ha_t compat_softc;
+
+ int result;
+ int fd;
+ int ndx;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ pass_thru.eataID[0] = 'E';
+ pass_thru.eataID[1] = 'A';
+ pass_thru.eataID[2] = 'T';
+ pass_thru.eataID[3] = 'A';
+ pass_thru.command = DPT_CTRLINFO;
+ pass_thru.command_buffer = (u_int8_t *)&compat_softc;
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SEND, &pass_thru)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL "
+ "%lx - %s\n",
+ argv[0], DPT_IOCTL_SEND,
+ strerror(errno));
+ exit(1);
+ }
+
+ (void)fprintf(stdout, "%x:", compat_softc.ha_state);
+
+ for (ndx = 0; ndx < MAX_CHANNELS; ndx++)
+ (void)fprintf(stdout, (ndx == (MAX_CHANNELS - 1)) ? "%d:" :
+ "%d,",
+ compat_softc.ha_id[ndx]);
+
+ (void)fprintf(stdout, "%d:", compat_softc.ha_vect);
+ (void)fprintf(stdout, "%x:", compat_softc.ha_base);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_max_jobs);
+
+ switch (compat_softc.ha_cache) {
+ case DPT_NO_CACHE:
+ (void)fprintf(stdout, "No Cache:");
+ break;
+ case DPT_CACHE_WRITETHROUGH:
+ (void)fprintf(stdout, "WriteThrough:");
+ break;
+ case DPT_CACHE_WRITEBACK:
+ (void)fprintf(stdout, "WriteBack:");
+ break;
+ default:
+ (void)fprintf(stdout, "UnKnown (%d):", compat_softc.ha_cache);
+ }
+
+ (void)fprintf(stdout, "%d:", compat_softc.ha_cachesize);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_nbus);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_ntargets);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_nluns);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_tshift);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_bshift);
+
+ (void)fprintf(stdout, "%d:", compat_softc.ha_npend);
+ (void)fprintf(stdout, "%d:", compat_softc.ha_active_jobs);
+
+ (void)fprintf(stdout, "%s\n", compat_softc.ha_fw_version);
+
+
+
+ return(0);
+}
diff --git a/usr.sbin/dpt/dpt_ctls/Makefile b/usr.sbin/dpt/dpt_ctls/Makefile
new file mode 100644
index 0000000..aa092a1
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctls/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:10 eivind Exp $
+
+PROG= dpt_ctls
+SRCS= dpt_ctls.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_ctls.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_ctls/dpt_ctls.8 b/usr.sbin/dpt/dpt_ctls/dpt_ctls.8
new file mode 100644
index 0000000..f9d1530
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctls/dpt_ctls.8
@@ -0,0 +1,3 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_ctls/dpt_ctls.c b/usr.sbin/dpt/dpt_ctls/dpt_ctls.c
new file mode 100644
index 0000000..3c66da5
--- /dev/null
+++ b/usr.sbin/dpt/dpt_ctls/dpt_ctls.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_ctls.c: Dunp a the number of configured DPT HBAs */
+
+#ident "$Id: dpt_ctls.c,v 1.1 1998/01/26 06:20:39 julian Exp $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#include <sys/dpt.h>
+
+int
+main(int argc, char **argv, char **argp)
+{
+ eata_pt_t pass_thru;
+ int controllers_present;
+
+ int result;
+ int fd;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ pass_thru.eataID[0] = 'E';
+ pass_thru.eataID[1] = 'A';
+ pass_thru.eataID[2] = 'T';
+ pass_thru.eataID[3] = 'A';
+ pass_thru.command = DPT_NUMCTRLS;
+ pass_thru.command_buffer = (u_int8_t *)&controllers_present;
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SEND, &pass_thru)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL "
+ "%lx - %s\n",
+ argv[0], DPT_IOCTL_SEND,
+ strerror(errno));
+ exit(1);
+ }
+
+ (void)fprintf(stdout, "%d\n", controllers_present);
+ return(0);
+}
diff --git a/usr.sbin/dpt/dpt_dm/Makefile b/usr.sbin/dpt/dpt_dm/Makefile
new file mode 100644
index 0000000..547511c
--- /dev/null
+++ b/usr.sbin/dpt/dpt_dm/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:10 eivind Exp $
+
+PROG= dpt_dm
+SRCS= dpt_dm.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_dm.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_dm/dpt_dm.8 b/usr.sbin/dpt/dpt_dm/dpt_dm.8
new file mode 100644
index 0000000..d04a43e
--- /dev/null
+++ b/usr.sbin/dpt/dpt_dm/dpt_dm.8
@@ -0,0 +1,6 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_dm/dpt_dm.c b/usr.sbin/dpt/dpt_dm/dpt_dm.c
new file mode 100644
index 0000000..a2d1b12
--- /dev/null
+++ b/usr.sbin/dpt/dpt_dm/dpt_dm.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_dm.c: Dump a DPT metrics structure */
+
+#ident "$Id: dpt_dm.c,v 1.1 1998/08/03 16:43:36 root Exp root $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#define DPT_MEASURE_PERFORMANCE
+
+#include <sys/dpt.h>
+
+char *
+scsi_cmd_name(u_int8_t cmd)
+{
+ switch (cmd) {
+ case 0x40:
+ return ("Change Definition [7.1]");
+ break;
+ case 0x39:
+ return ("Compare [7,2]");
+ break;
+ case 0x18:
+ return ("Copy [7.3]");
+ break;
+ case 0x3a:
+ return ("Copy and Verify [7.4]");
+ break;
+ case 0x04:
+ return ("Format Unit [6.1.1]");
+ break;
+ case 0x12:
+ return ("Inquiry [7.5]");
+ break;
+ case 0x36:
+ return ("lock/Unlock Cache [6.1.2]");
+ break;
+ case 0x4c:
+ return ("Log Select [7.6]");
+ break;
+ case 0x4d:
+ return ("Log Sense [7.7]");
+ break;
+ case 0x15:
+ return ("Mode select (6) [7.8]");
+ break;
+ case 0x55:
+ return ("Mode Select (10) [7.9]");
+ break;
+ case 0x1a:
+ return ("Mode Sense (6) [7.10]");
+ break;
+ case 0x5a:
+ return ("Mode Sense (10) [7.11]");
+ break;
+ case 0xa7:
+ return ("Move Medium Attached [SMC]");
+ break;
+ case 0x5e:
+ return ("Persistent Reserve In [7.12]");
+ break;
+ case 0x5f:
+ return ("Persistent Reserve Out [7.13]");
+ break;
+ case 0x1e:
+ return ("Prevent/Allow Medium Removal [7.14]");
+ break;
+ case 0x08:
+ return ("Read, Receive (6) [6.1.5]");
+ break;
+ case 0x28:
+ return ("Read (10) [6.1.5]");
+ break;
+ case 0xa8:
+ return ("Read (12) [6.1.5]");
+ break;
+ case 0x3c:
+ return ("Read Buffer [7.15]");
+ break;
+ case 0x25:
+ return ("Read Capacity [6.1.6]");
+ break;
+ case 0x37:
+ return ("Read Defect Data (10) [6.1.7]");
+ break;
+ case 0xb7:
+ return ("Read Defect Data (12) [6.2.5]");
+ break;
+ case 0xb4:
+ return ("Read Element Status Attached [SMC]");
+ break;
+ case 0x3e:
+ return ("Read Long [6.1.8]");
+ break;
+ case 0x07:
+ return ("Reassign Blocks [6.1.9]");
+ break;
+ case 0x81:
+ return ("Rebuild [6.1.10]");
+ break;
+ case 0x1c:
+ return ("Receive Diagnostics Result [7.16]");
+ break;
+ case 0x82:
+ return ("Regenerate [6.1.11]");
+ break;
+ case 0x17:
+ return ("Release(6) [7.17]");
+ break;
+ case 0x57:
+ return ("Release(10) [7.18]");
+ break;
+ case 0xa0:
+ return ("Report LUNs [7.19]");
+ break;
+ case 0x03:
+ return ("Request Sense [7.20]");
+ break;
+ case 0x16:
+ return ("Resereve (6) [7.21]");
+ break;
+ case 0x56:
+ return ("Reserve(10) [7.22]");
+ break;
+ case 0x2b:
+ return ("Reserve(10) [6.1.12]");
+ break;
+ case 0x1d:
+ return ("Send Disagnostics [7.23]");
+ break;
+ case 0x33:
+ return ("Set Limit (10) [6.1.13]");
+ break;
+ case 0xb3:
+ return ("Set Limit (12) [6.2.8]");
+ break;
+ case 0x1b:
+ return ("Start/Stop Unit [6.1.14]");
+ break;
+ case 0x35:
+ return ("Synchronize Cache [6.1.15]");
+ break;
+ case 0x00:
+ return ("Test Unit Ready [7.24]");
+ break;
+ case 0x3d:
+ return ("Update Block (6.2.9");
+ break;
+ case 0x2f:
+ return ("Verify (10) [6.1.16, 6.2.10]");
+ break;
+ case 0xaf:
+ return ("Verify (12) [6.2.11]");
+ break;
+ case 0x0a:
+ return ("Write, Send (6) [6.1.17, 9.2]");
+ break;
+ case 0x2a:
+ return ("Write (10) [6.1.18]");
+ break;
+ case 0xaa:
+ return ("Write (12) [6.2.13]");
+ break;
+ case 0x2e:
+ return ("Write and Verify (10) [6.1.19, 6.2.14]");
+ break;
+ case 0xae:
+ return ("Write and Verify (12) [6.1.19, 6.2.15]");
+ break;
+ case 0x03b:
+ return ("Write Buffer [7.25]");
+ break;
+ case 0x03f:
+ return ("Write Long [6.1.20]");
+ break;
+ case 0x041:
+ return ("Write Same [6.1.21]");
+ break;
+ case 0x052:
+ return ("XD Read [6.1.22]");
+ break;
+ case 0x050:
+ return ("XD Write [6.1.22]");
+ break;
+ case 0x080:
+ return ("XD Write Extended [6.1.22]");
+ break;
+ case 0x051:
+ return ("XO Write [6.1.22]");
+ break;
+ default:
+ return ("Unknown SCSI Command");
+ }
+}
+
+static const char *
+tmpsprintf(int buffer, const char *format, ...)
+{
+ static char buffers[16][16];
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(buffers[buffer], 16, format, ap);
+ va_end(ap);
+ return buffers[buffer];
+}
+
+
+int
+main(int argc, char **argv, char **argp)
+{
+ dpt_perf_t metrics;
+ int result;
+ int fd;
+ int ndx;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ if ( (result = ioctl(fd, DPT_IOCTL_INTERNAL_METRICS, &metrics)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL %lx - %s\n",
+ argv[0], DPT_IOCTL_INTERNAL_METRICS,
+ strerror(errno));
+ exit(2);
+ }
+
+ /* Interrupt related measurements */
+ (void)fprintf(stdout, "Interrupts:%u:%u:%s:%u\n\nCommands:\n",
+ metrics.aborted_interrupts,
+ metrics.spurious_interrupts,
+ metrics.min_intr_time == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(0, "%u", metrics.min_intr_time),
+ metrics.max_intr_time);
+
+ /* SCSI Commands, can be no more than 256 of them */
+ for (ndx = 0; ndx < 256; ndx++) {
+ if (metrics.command_count[ndx] != 0) {
+ (void)fprintf(stdout, "%u:%s:%u:%s:%d\n",
+ ndx,
+ scsi_cmd_name((u_int8_t)ndx),
+ metrics.command_count[ndx],
+ metrics.min_command_time[ndx] == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(0, "%u", metrics.min_command_time[ndx]),
+ metrics.max_command_time[ndx]);
+ }
+ }
+
+ (void)fprintf(stdout, "\nREAD by size:\n");
+
+ /* READ/WRITE statistics, per block size */
+
+ for ( ndx = 0; ndx < 10; ndx++) {
+ if (metrics.read_by_size_count[ndx] != 0) {
+ char* block_size;
+
+ switch ( ndx ) {
+ case SIZE_512:
+ block_size = "512";
+ break;
+ case SIZE_1K:
+ block_size = "1K";
+ break;
+ case SIZE_2K:
+ block_size = "2K";
+ break;
+ case SIZE_4K:
+ block_size = "4K";
+ break;
+ case SIZE_8K:
+ block_size = "8K";
+ break;
+ case SIZE_16K:
+ block_size = "16K";
+ break;
+ case SIZE_32K:
+ block_size = "32K";
+ break;
+ case SIZE_64K:
+ block_size = "64K";
+ break;
+ case SIZE_BIGGER:
+ block_size = "BIGGER";
+ break;
+ case SIZE_OTHER:
+ block_size = "OTHER";
+ break;
+ default:
+ block_size = "Gcc, shut up!";
+ }
+
+ (void)fprintf(stdout, "%s:%u:%u:%u\n", block_size,
+ metrics.read_by_size_count[ndx],
+ metrics.read_by_size_min_time[ndx],
+ metrics.read_by_size_max_time[ndx]);
+ }
+ }
+
+ (void)fprintf(stdout, "\nWRITE by size:\n");
+
+ for ( ndx = 0; ndx < 10; ndx++) {
+ if (metrics.write_by_size_count[ndx] != 0) {
+ char* block_size;
+
+ switch ( ndx ) {
+ case SIZE_512:
+ block_size = "512";
+ break;
+ case SIZE_1K:
+ block_size = "1K";
+ break;
+ case SIZE_2K:
+ block_size = "2K";
+ break;
+ case SIZE_4K:
+ block_size = "4K";
+ break;
+ case SIZE_8K:
+ block_size = "8K";
+ break;
+ case SIZE_16K:
+ block_size = "16K";
+ break;
+ case SIZE_32K:
+ block_size = "32K";
+ break;
+ case SIZE_64K:
+ block_size = "64K";
+ break;
+ case SIZE_BIGGER:
+ block_size = "BIGGER";
+ break;
+ case SIZE_OTHER:
+ block_size = "OTHER";
+ break;
+ default:
+ block_size = "Gcc, shut up!";
+ }
+
+ (void)fprintf(stdout, "%s:%u:%u:%u\n", block_size,
+ metrics.write_by_size_count[ndx],
+ metrics.write_by_size_min_time[ndx],
+ metrics.write_by_size_max_time[ndx]);
+ }
+
+ }
+
+ (void)fprintf(stdout, "\nQueues:%u:%s:%u:%u:%s:%u:%u:%s:%u\n",
+ metrics.max_waiting_count,
+ metrics.min_waiting_time == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(0, "%u", metrics.min_waiting_time),
+ metrics.max_waiting_time,
+ metrics.max_submit_count,
+ metrics.min_submit_time == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(1, "%u", metrics.min_submit_time),
+ metrics.max_submit_time,
+ metrics.max_complete_count,
+ metrics.min_complete_time == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(2, "%u", metrics.min_complete_time),
+ metrics.max_complete_time);
+
+ (void)fprintf(stdout, "Hardware Ports:%u:%u:%u:%s\n",
+ metrics.command_collisions,
+ metrics.command_too_busy,
+ metrics.max_eata_tries,
+ metrics.min_eata_tries == BIG_ENOUGH ?
+ "N/A" :
+ tmpsprintf(0, "%u", metrics.min_eata_tries));
+
+ return(0);
+}
diff --git a/usr.sbin/dpt/dpt_led/Makefile b/usr.sbin/dpt/dpt_led/Makefile
new file mode 100644
index 0000000..0f8bd24
--- /dev/null
+++ b/usr.sbin/dpt/dpt_led/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:10 eivind Exp $
+
+PROG= dpt_led
+SRCS= dpt_led.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_led.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_led/dpt_led.8 b/usr.sbin/dpt/dpt_led/dpt_led.8
new file mode 100644
index 0000000..f9d1530
--- /dev/null
+++ b/usr.sbin/dpt/dpt_led/dpt_led.8
@@ -0,0 +1,3 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_led/dpt_led.c b/usr.sbin/dpt/dpt_led/dpt_led.c
new file mode 100644
index 0000000..9304950
--- /dev/null
+++ b/usr.sbin/dpt/dpt_led/dpt_led.c
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_led.c: Show the blinking LED array status of a DPT HBAs */
+
+#ident "$Id: dpt_led.c,v 1.1 1998/01/26 06:26:41 julian Exp $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#include <sys/dpt.h>
+
+int
+main(int argc, char **argv, char **argp)
+{
+ eata_pt_t pass_thru;
+ int led;
+
+ int result;
+ int fd;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ pass_thru.eataID[0] = 'E';
+ pass_thru.eataID[1] = 'A';
+ pass_thru.eataID[2] = 'T';
+ pass_thru.eataID[3] = 'A';
+ pass_thru.command = DPT_BLINKLED;
+ pass_thru.command_buffer = (u_int8_t *)&led;
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SEND, &pass_thru)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL %lx "
+ "- %s\n",
+ argv[0], DPT_IOCTL_SEND,
+ strerror(errno));
+ exit(1);
+ }
+
+ (void)fprintf(stdout, "%s\n", i2bin((unsigned int)led, 16));
+ return(0);
+}
+
+/*
+ * and this one presents an integer as ones and zeros
+ */
+static char i2bin_bitmap[48]; /* Used for binary dump of registers */
+
+char *
+i2bin(unsigned int no, int length)
+{
+ int ndx, rind;
+
+ for (ndx = 0, rind = 0; ndx < 32; ndx++, rind++) {
+ i2bin_bitmap[rind] = (((no << ndx) & 0x80000000) ? '1' : '0');
+
+ if (((ndx % 4) == 3))
+ i2bin_bitmap[++rind] = ' ';
+ }
+
+ if ((ndx % 4) == 3)
+ i2bin_bitmap[rind - 1] = '\0';
+ else
+ i2bin_bitmap[rind] = '\0';
+
+ switch (length) {
+ case 8:
+ return (i2bin_bitmap + 30);
+ break;
+ case 16:
+ return (i2bin_bitmap + 20);
+ break;
+ case 24:
+ return (i2bin_bitmap + 10);
+ break;
+ case 32:
+ return (i2bin_bitmap);
+ default:
+ return ("i2bin: Invalid length Specs");
+ break;
+ }
+}
+
diff --git a/usr.sbin/dpt/dpt_sig/Makefile b/usr.sbin/dpt/dpt_sig/Makefile
new file mode 100644
index 0000000..2ed62f9
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sig/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:10 eivind Exp $
+
+PROG= dpt_sig
+SRCS= dpt_sig.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_sig.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_sig/dpt_sig.8 b/usr.sbin/dpt/dpt_sig/dpt_sig.8
new file mode 100644
index 0000000..f9d1530
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sig/dpt_sig.8
@@ -0,0 +1,3 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_sig/dpt_sig.c b/usr.sbin/dpt/dpt_sig/dpt_sig.c
new file mode 100644
index 0000000..76e648b
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sig/dpt_sig.c
@@ -0,0 +1,598 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_sig.c: Dunp a DPT Signature */
+
+#ident "$Id: dpt_sig.c,v 1.1 1998/01/26 06:20:45 julian Exp $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#define DPT_MEASURE_PERFORMANCE
+
+#include <sys/dpt.h>
+
+/* A primitive subset of isgraph. Used by hex_dump below */
+#define IsGraph(val) ( (((val) >= ' ') && ((val) <= '~')) )
+
+/*
+ * This function dumps bytes to the screen in hex format.
+ */
+void
+hex_dump(u_int8_t * data, int length, char *name, int no)
+{
+ int line, column, ndx;
+
+ (void)fprintf(stdout, "Kernel Hex Dump for %s-%d at %p (%d bytes)\n",
+ name, no, data, length);
+
+ /* Zero out all the counters and repeat for as many bytes as we have */
+ for (ndx = 0, column = 0, line = 0; ndx < length; ndx++) {
+ /* Print relative offset at the beginning of every line */
+ if (column == 0)
+ (void)fprintf(stdout, "%04x ", ndx);
+
+ /* Print the byte as two hex digits, followed by a space */
+ (void)fprintf(stdout, "%02x ", data[ndx]);
+
+ /* Split the row of 16 bytes in half */
+ if (++column == 8) {
+ (void)fprintf(stdout, " ");
+ }
+ /* St the end of each row of 16 bytes, put a space ... */
+ if (column == 16) {
+ (void)fprintf(stdout, " ");
+
+ /* ... and then print the ASCII-visible on a line. */
+ for (column = 0; column < 16; column++) {
+ int ascii_pos = ndx - 15 + column;
+
+ /*
+ * Non-printable and non-ASCII are just a
+ * dot. ;-(
+ */
+ if (IsGraph(data[ascii_pos]))
+ (void)fprintf(stdout, "%c", data[ascii_pos]);
+ else
+ (void)fprintf(stdout, ".");
+ }
+
+ /* Each line ends with a new line */
+ (void)fprintf(stdout, "\n");
+ column = 0;
+
+ /*
+ * Every 256 bytes (16 lines of 16 bytes each) have
+ * an empty line, separating them from the next
+ * ``page''. Yes, I programmed on a Z-80, where a
+ * page was 256 bytes :-)
+ */
+ if (++line > 15) {
+ (void)fprintf(stdout, "\n");
+ line = 0;
+ }
+ }
+ }
+
+ /*
+ * We are basically done. We do want, however, to handle the ASCII
+ * translation of fractional lines.
+ */
+ if ((ndx == length) && (column != 0)) {
+ int modulus = 16 - column, spaces = modulus * 3, skip;
+
+ /*
+ * Skip to the right, as many spaces as there are bytes
+ * ``missing'' ...
+ */
+ for (skip = 0; skip < spaces; skip++)
+ (void)fprintf(stdout, " ");
+
+ /* ... And the gap separating the hex dump from the ASCII */
+ (void)fprintf(stdout, " ");
+
+ /*
+ * Do not forget the extra space that splits the hex dump
+ * vertically
+ */
+ if (column < 8)
+ (void)fprintf(stdout, " ");
+
+ for (column = 0; column < (16 - modulus); column++) {
+ int ascii_pos = ndx - (16 - modulus) + column;
+
+ if (IsGraph(data[ascii_pos]))
+ (void)fprintf(stdout, "%c", data[ascii_pos]);
+ else
+ (void)fprintf(stdout, ".");
+ }
+ (void)fprintf(stdout, "\n");
+ }
+}
+
+
+int
+main(int argc, char **argv, char **argp)
+{
+ eata_pt_t pass_thru;
+ dpt_sig_t signature;
+ char *sp1;
+ char *sp2;
+
+ int result;
+ int fd;
+ int ndx;
+
+ /* If we do not do that, gcc complains about uninitialized usage (?) */
+ sp1 = "Unknown";
+ sp2 = "Unknown";
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" "
+ "- %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ pass_thru.eataID[0] = 'E';
+ pass_thru.eataID[1] = 'A';
+ pass_thru.eataID[2] = 'T';
+ pass_thru.eataID[3] = 'A';
+ pass_thru.command = DPT_SIGNATURE;
+ pass_thru.command_buffer = (u_int8_t *)&signature;
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SEND, &pass_thru)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL "
+ "%lx - %s\n",
+ argv[0], DPT_IOCTL_SEND,
+ strerror(errno));
+ exit(1);
+ }
+
+ /* dsSignature is not null terminated! */
+ for (ndx = 0; ndx < sizeof(signature.dsSignature); ndx++)
+ (void)fputc(signature.dsSignature[ndx], stdout);
+
+ (void)fprintf(stdout, ":%x:", signature.SigVersion);
+
+ switch (signature.ProcessorFamily) {
+ case PROC_INTEL:
+ sp1 = "Intel";
+ switch ( signature.Processor ) {
+ case PROC_8086:
+ sp2 = "8086";
+ break;
+ case PROC_286:
+ sp2 = "80286";
+ break;
+ case PROC_386:
+ sp2 = "386";
+ break;
+ case PROC_486:
+ sp2 = "486";
+ break;
+ case PROC_PENTIUM:
+ sp2 = "Pentium";
+ break;
+ case PROC_P6:
+ sp2 = "PentiumPro";
+ break;
+ default:
+ sp2 = "Unknown Processor";
+ break;
+ }
+ break;
+ case PROC_MOTOROLA:
+ sp1 = "Motorola";
+ switch ( signature.Processor ) {
+ case PROC_68000:
+ sp2 = "68000";
+ break;
+ case PROC_68020:
+ sp2 = "68020";
+ break;
+ case PROC_68030:
+ sp2 = "68030";
+ break;
+ case PROC_68040:
+ sp2 = "68040";
+ break;
+ default:
+ sp2 = "Unknown Processor";
+ break;
+ }
+ break;
+ case PROC_MIPS4000:
+ sp1 = "MIPS/SGI";
+ break;
+ case PROC_ALPHA:
+ sp1 = "DEC Alpha";
+ break;
+ default:
+ sp1 = "Unknown Processor Family";
+ break;
+ }
+
+ (void)fprintf(stdout, "%s:%s:", sp1, sp2);
+
+ switch ( signature.Filetype ) {
+ case FT_EXECUTABLE:
+ sp1 = "Executable";
+ break;
+ case FT_SCRIPT:
+ sp1 = "Script";
+ break;
+ case FT_HBADRVR:
+ sp1 = "HBA Driver";
+ break;
+ case FT_OTHERDRVR:
+ sp1 = "Other Driver";
+ break;
+ case FT_IFS:
+ sp1 = "Installable FileSystem";
+ break;
+ case FT_ENGINE:
+ sp1 = "DPT Engine";
+ break;
+ case FT_COMPDRVR:
+ sp1 = "Compressed Driver";
+ break;
+ case FT_LANGUAGE:
+ sp1 = "Language File";
+ break;
+ case FT_FIRMWARE:
+ sp1 = "DownLoadable Firmware";
+ break;
+ case FT_COMMMODL:
+ sp1 = "Communications Module";
+ break;
+ case FT_INT13:
+ sp1 = "INT13 Type HBA Driver";
+ break;
+ case FT_HELPFILE:
+ sp1 = "Help File";
+ break;
+ case FT_LOGGER:
+ sp1 = "Event Logger";
+ break;
+ case FT_INSTALL:
+ sp1 = "Installation Procedure";
+ break;
+ case FT_LIBRARY:
+ sp1 = "Storage Manager Real-Mode Call";
+ break;
+ case FT_RESOURCE:
+ sp1 = "Storage Manager Resource File";
+ break;
+ case FT_MODEM_DB:
+ sp1 = "Storage Manager Modem Database";
+ break;
+ default:
+ sp1 = "Unknown File Type";
+ break;
+ }
+
+ switch ( signature.FiletypeFlags ) {
+ case FTF_DLL:
+ sp2 = "Dynamically Linked Library";
+ break;
+ case FTF_NLM:
+ sp2 = "NetWare Loadable Module";
+ break;
+ case FTF_OVERLAYS:
+ sp2 = "Uses Overlays";
+ break;
+ case FTF_DEBUG:
+ sp2 = "Debug Version";
+ break;
+ case FTF_TSR:
+ sp2 = "DOS Terminate-n-Stay Resident Thing";
+ break;
+ case FTF_SYS:
+ sp2 = "DOS Loadable Driver";
+ break;
+ case FTF_PROTECTED:
+ sp2 = "Runs in Protected Mode";
+ break;
+ case FTF_APP_SPEC:
+ sp2 = "Application Specific";
+ break;
+ default:
+ sp2 = "Unknown File Type Flag";
+ break;
+ }
+
+ (void)fprintf(stdout, "%s:%s:", sp1, sp2);
+
+ switch ( signature.OEM ) {
+ case OEM_DPT:
+ sp1 = "DPT";
+ break;
+ case OEM_ATT:
+ sp1 = "AT&T";
+ break;
+ case OEM_NEC:
+ sp1 = "NEC";
+ break;
+ case OEM_ALPHA:
+ sp1 = "Alphatronix";
+ break;
+ case OEM_AST:
+ sp1 = "AST";
+ break;
+ case OEM_OLIVETTI:
+ sp1 = "Olivetti";
+ break;
+ case OEM_SNI:
+ sp1 = "Siemens/Nixdorf";
+ break;
+ default:
+ sp1 = "Unknown OEM";
+ break;
+ }
+
+ switch ( signature.OS ) {
+ case OS_DOS:
+ sp2 = "DOS";
+ break;
+ case OS_WINDOWS:
+ sp2 = "Microsoft Windows 3.x";
+ break;
+ case OS_WINDOWS_NT:
+ sp2 = "Microsoft Windows NT";
+ break;
+ case OS_OS2M:
+ sp2 = "OS/2 1.2.x,MS 1.3.0,IBM 1.3.x";
+ break;
+ case OS_OS2L:
+ sp2 = "Microsoft OS/2 1.301 - LADDR";
+ break;
+ case OS_OS22x:
+ sp2 = "IBM OS/2 2.x";
+ break;
+ case OS_NW286:
+ sp2 = "Novell NetWare 286";
+ break;
+ case OS_NW386:
+ sp2 = "Novell NetWare 386";
+ break;
+ case OS_GEN_UNIX:
+ sp2 = "Generic Unix";
+ break;
+ case OS_SCO_UNIX:
+ sp2 = "SCO Unix";
+ break;
+ case OS_ATT_UNIX:
+ sp2 = "AT&T Unix";
+ break;
+ case OS_UNIXWARE:
+ sp2 = "UnixWare Unix";
+ break;
+ case OS_INT_UNIX:
+ sp2 = "Interactive Unix";
+ break;
+ case OS_SOLARIS:
+ sp2 = "SunSoft Solaris";
+ break;
+ case OS_QNX:
+ sp2 = "QNX for Tom Moch";
+ break;
+ case OS_NEXTSTEP:
+ sp2 = "NeXTSTEP";
+ break;
+ case OS_BANYAN:
+ sp2 = "Banyan Vines";
+ break;
+ case OS_OLIVETTI_UNIX:
+ sp2 = "Olivetti Unix";
+ break;
+ case OS_FREEBSD:
+ sp2 = "FreeBSD 2.2 and later";
+ break;
+ case OS_OTHER:
+ sp2 = "Other";
+ break;
+ default:
+ sp2 = "Unknown O/S";
+ break;
+ }
+
+ (void)fprintf(stdout, "%s:%s:\n", sp1, sp2);
+
+ if ( signature.Capabilities & CAP_RAID0 )
+ (void)fprintf(stdout, "RAID-0:");
+
+ if ( signature.Capabilities & CAP_RAID1 )
+ (void)fprintf(stdout, "RAID-1:");
+
+ if ( signature.Capabilities & CAP_RAID3 )
+ (void)fprintf(stdout, "RAID-3:");
+
+ if ( signature.Capabilities & CAP_RAID5 )
+ (void)fprintf(stdout, "RAID-5:");
+
+ if ( signature.Capabilities & CAP_SPAN )
+ (void)fprintf(stdout, "SPAN:");
+
+ if ( signature.Capabilities & CAP_PASS )
+ (void)fprintf(stdout, "PASS:");
+
+ if ( signature.Capabilities & CAP_OVERLAP )
+ (void)fprintf(stdout, "OVERLAP:");
+
+ if ( signature.Capabilities & CAP_ASPI )
+ (void)fprintf(stdout, "ASPI:");
+
+ if ( signature.Capabilities & CAP_ABOVE16MB )
+ (void)fprintf(stdout, "ISA16MB:");
+
+ if ( signature.Capabilities & CAP_EXTEND )
+ (void)fprintf(stdout, "ISA16MB:");
+
+ (void)fprintf(stdout, "\n");
+
+ if ( signature.DeviceSupp & DEV_DASD )
+ (void)fprintf(stdout, "DASD:");
+
+ if ( signature.DeviceSupp & DEV_TAPE )
+ (void)fprintf(stdout, "Tape:");
+
+ if ( signature.DeviceSupp & DEV_PRINTER )
+ (void)fprintf(stdout, "Printer:");
+
+ if ( signature.DeviceSupp & DEV_PROC )
+ (void)fprintf(stdout, "CPU:");
+
+ if ( signature.DeviceSupp & DEV_WORM )
+ (void)fprintf(stdout, "WORM:");
+
+ if ( signature.DeviceSupp & DEV_CDROM )
+ (void)fprintf(stdout, "CDROM:");
+
+ if ( signature.DeviceSupp & DEV_SCANNER )
+ (void)fprintf(stdout, "Scanner:");
+
+ if ( signature.DeviceSupp & DEV_OPTICAL )
+ (void)fprintf(stdout, "Optical:");
+
+ if ( signature.DeviceSupp & DEV_JUKEBOX )
+ (void)fprintf(stdout, "Jukebox:");
+
+ if ( signature.DeviceSupp & DEV_COMM )
+ (void)fprintf(stdout, "Comm:");
+
+ if ( signature.DeviceSupp & DEV_OTHER )
+ (void)fprintf(stdout, "Other:");
+
+ if ( signature.DeviceSupp & DEV_ALL )
+ (void)fprintf(stdout, "All:");
+
+ (void)fprintf(stdout, "\n");
+
+ if ( signature.AdapterSupp & ADF_2001 )
+ (void)fprintf(stdout, "PM2001:");
+
+ if ( signature.AdapterSupp & ADF_2012A )
+ (void)fprintf(stdout, "PM2012A:");
+
+ if ( signature.AdapterSupp & ADF_PLUS_ISA )
+ (void)fprintf(stdout, "PM2011+PM2021:");
+
+ if ( signature.AdapterSupp & ADF_PLUS_EISA )
+ (void)fprintf(stdout, "PM2012B+PM2022:");
+
+ if ( signature.AdapterSupp & ADF_SC3_ISA )
+ (void)fprintf(stdout, "PM2021:");
+
+ if ( signature.AdapterSupp & ADF_SC3_EISA )
+ (void)fprintf(stdout, "PM2022+PM2122:");
+
+ if ( signature.AdapterSupp & ADF_SC3_PCI )
+ (void)fprintf(stdout, "SmartCache III PCI:");
+
+ if ( signature.AdapterSupp & ADF_SC4_ISA )
+ (void)fprintf(stdout, "SmartCache IV ISA:");
+
+ if ( signature.AdapterSupp & ADF_SC4_EISA )
+ (void)fprintf(stdout, "SmartCache IV EISA:");
+
+ if ( signature.AdapterSupp & ADF_SC4_PCI )
+ (void)fprintf(stdout, "SmartCache IV PCI:");
+
+ if ( signature.AdapterSupp & ADF_ALL_MASTER )
+ (void)fprintf(stdout, "All Bus Mastering:");
+
+ if ( signature.AdapterSupp & ADF_ALL_CACHE )
+ (void)fprintf(stdout, "All Caching:");
+
+ if ( signature.AdapterSupp & ADF_ALL )
+ (void)fprintf(stdout, "All HBAs:");
+
+ (void)fprintf(stdout, "\n");
+
+ if ( signature.Application & APP_DPTMGR )
+ (void)fprintf(stdout, "DPTMGR:");
+
+ if ( signature.Application & APP_ENGINE )
+ (void)fprintf(stdout, "Engine:");
+
+ if ( signature.Application & APP_SYTOS )
+ (void)fprintf(stdout, "Systron Sytos Plus:");
+
+ if ( signature.Application & APP_CHEYENNE )
+ (void)fprintf(stdout, "Cheyenne ARCServe + ARCSolo:");
+
+ if ( signature.Application & APP_MSCDEX )
+ (void)fprintf(stdout, "Microsoft CD-ROM extensions:");
+
+ if ( signature.Application & APP_NOVABACK )
+ (void)fprintf(stdout, "NovaStor Novaback:");
+
+ if ( signature.Application & APP_AIM )
+ (void)fprintf(stdout, "Archive Information Manager:");
+
+ (void)fprintf(stdout, "\n");
+
+ if ( signature.Requirements & REQ_SMARTROM )
+ (void)fprintf(stdout, "SmartROM:");
+
+ if ( signature.Requirements & REQ_DPTDDL )
+ (void)fprintf(stdout, "DPTDDL.SYS:");
+
+ if ( signature.Requirements & REQ_HBA_DRIVER )
+ (void)fprintf(stdout, "HBA Driver:");
+
+ if ( signature.Requirements & REQ_ASPI_TRAN )
+ (void)fprintf(stdout, "ASPI Transport Modules:");
+
+ if ( signature.Requirements & REQ_ENGINE )
+ (void)fprintf(stdout, "DPT Engine:");
+
+ if ( signature.Requirements & REQ_COMM_ENG )
+ (void)fprintf(stdout, "DPT Comm Engine:");
+
+ (void)fprintf(stdout, "\n");
+
+ (void)fprintf(stdout, "%x.%x.%x:%d.%d.%d\n",
+ signature.Version, signature.Revision,
+ signature.SubRevision,
+ signature.Month, signature.Day, signature.Year + 1980);
+
+ return(0);
+}
diff --git a/usr.sbin/dpt/dpt_softc/Makefile b/usr.sbin/dpt/dpt_softc/Makefile
new file mode 100644
index 0000000..ad6483a
--- /dev/null
+++ b/usr.sbin/dpt/dpt_softc/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:11 eivind Exp $
+
+PROG= dpt_softc
+SRCS= dpt_softc.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_softc.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_softc/dpt_softc.8 b/usr.sbin/dpt/dpt_softc/dpt_softc.8
new file mode 100644
index 0000000..6dfeb0f
--- /dev/null
+++ b/usr.sbin/dpt/dpt_softc/dpt_softc.8
@@ -0,0 +1,4 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
+
diff --git a/usr.sbin/dpt/dpt_softc/dpt_softc.c b/usr.sbin/dpt/dpt_softc/dpt_softc.c
new file mode 100644
index 0000000..4564791
--- /dev/null
+++ b/usr.sbin/dpt/dpt_softc/dpt_softc.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_softc.c: Dunp a DPT control structure */
+
+#ident "$Id: dpt_softc.c,v 1.1 1998/01/26 06:20:46 julian Exp $"
+
+#include <stdlib.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+/*
+ * The following two defines alter the size and composition of dpt_softc_t.
+ * If useland does not match the kenel, disaster will ensue.
+ * Since we do not know how to pick up kernel options from here,
+ * and since we always use these options, we will enable them here.
+ *
+ * If you build a kernel without these options, edit here and recompile.
+ */
+#define DPT_MEASURE_PERFORMANCE
+#define DEVFS
+
+#include <sys/dpt.h>
+
+static char i2bin_bitmap[48]; /* Used for binary dump of registers */
+
+char *
+i2bin(unsigned int no, int length)
+{
+ int ndx, rind;
+
+ for (ndx = 0, rind = 0; ndx < 32; ndx++, rind++) {
+ i2bin_bitmap[rind] = (((no << ndx) & 0x80000000) ? '1' : '0');
+
+ if (((ndx % 4) == 3))
+ i2bin_bitmap[++rind] = ' ';
+ }
+
+ if ((ndx % 4) == 3)
+ i2bin_bitmap[rind - 1] = '\0';
+ else
+ i2bin_bitmap[rind] = '\0';
+
+ switch (length) {
+ case 8:
+ return (i2bin_bitmap + 30);
+ break;
+ case 16:
+ return (i2bin_bitmap + 20);
+ break;
+ case 24:
+ return (i2bin_bitmap + 10);
+ break;
+ case 32:
+ return (i2bin_bitmap);
+ default:
+ return ("i2bin: Invalid length Specs");
+ break;
+ }
+}
+int
+main(int argc, char **argv, char **argp)
+{
+ dpt_user_softc_t udpt;
+ int result;
+ int fd;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SOFTC, &udpt)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL %lx - %s\n",
+ argv[0], DPT_IOCTL_SOFTC,
+ strerror(errno));
+ exit(2);
+ }
+
+ (void)fprintf(stdout, "Counters:%d:%d:%d:%d:%d:%d:%d\n",
+ udpt.total_ccbs_count,
+ udpt.free_ccbs_count,
+ udpt.waiting_ccbs_count,
+ udpt.submitted_ccbs_count,
+ udpt.completed_ccbs_count,
+ udpt.commands_processed,
+ udpt.lost_interrupts);
+
+ (void)fprintf(stdout, "Queue Status:%s\n",
+ i2bin(udpt.queue_status, sizeof(udpt.queue_status) * 8));
+
+ (void)fprintf(stdout, "Free lock:%s\n",
+ i2bin(udpt.free_lock, sizeof(udpt.free_lock) * 8));
+
+ (void)fprintf(stdout, "Waiting lock:%s\n",
+ i2bin(udpt.waiting_lock, sizeof(udpt.waiting_lock) * 8));
+
+ (void)fprintf(stdout, "Submitted lock:%s\n",
+ i2bin(udpt.submitted_lock, sizeof(udpt.submitted_lock) * 8));
+
+ (void)fprintf(stdout, "Completed lock:%s\n",
+ i2bin(udpt.completed_lock, sizeof(udpt.completed_lock) * 8));
+
+ (void)fprintf(stdout, "Configuration:%s:%d:%d:%d:%x:%d:%d\n",
+ udpt.handle_interrupts ? "Yes" : "No",
+ udpt.max_id,
+ udpt.max_lun,
+ udpt.channels,
+ udpt.io_base,
+ udpt.irq,
+ udpt.dma_channel);
+
+ (void)fprintf(stdout, "ID:%x:%x:%s:%s:%s:%s:%x\n",
+ udpt.board_data.deviceType,
+ udpt.board_data.rm_dtq,
+ udpt.board_data.vendor,
+ udpt.board_data.modelNum,
+ udpt.board_data.firmware,
+ udpt.board_data.protocol,
+ udpt.EATA_revision);
+
+ (void)fprintf(stdout,"Capabilities:%x:%d:%s:%s:%s:%s:%s\n",
+ udpt.bustype,
+ udpt.channels,
+ i2bin((u_int32_t)udpt.state, sizeof(udpt.state) * 8),
+ udpt.primary ? "Yes" : "No",
+ udpt.more_support ? "Yes" : "No",
+ udpt.immediate_support ? "Yes" : "No",
+ udpt.broken_INQUIRY ? "Yes" : "No");
+
+ (void)fprintf(stdout,"More Config:%d:%d:%d:%d:%d:%d:%d:%d:%d:%d\n",
+ udpt.resetlevel[0],
+ udpt.resetlevel[1],
+ udpt.resetlevel[2],
+ udpt.cplen,
+ udpt.cppadlen,
+ udpt.queuesize,
+ udpt.sgsize,
+ udpt.hostid[0],
+ udpt.hostid[1],
+ udpt.hostid[2]);
+
+ (void)fprintf(stdout,"Cache:%s:%d\n",
+ (udpt.cache_type == DPT_NO_CACHE)
+ ? "None"
+ : (udpt.cache_type == DPT_CACHE_WRITETHROUGH)
+ ? "Write-Through" : "Write-Back",
+ udpt.cache_size);
+
+ return(0);
+}
diff --git a/usr.sbin/dpt/dpt_sysinfo/Makefile b/usr.sbin/dpt/dpt_sysinfo/Makefile
new file mode 100644
index 0000000..51eba46
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sysinfo/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id: Makefile,v 1.2 1998/08/06 21:19:11 eivind Exp $
+
+PROG= dpt_sysinfo
+SRCS= dpt_sysinfo.c
+
+CFLAGS+=-Wall -I${.CURDIR}/../../../sys
+BINMODE=500
+MAN8= dpt_sysinfo.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.8 b/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.8
new file mode 100644
index 0000000..f9d1530
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.8
@@ -0,0 +1,3 @@
+.\" Copyright (c) 1997 Simon Shapiro. All rights reserved.
+.\"
+.\" There is nothing here yet, but an empty file will not patch
diff --git a/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.c b/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.c
new file mode 100644
index 0000000..1e6ab14
--- /dev/null
+++ b/usr.sbin/dpt/dpt_sysinfo/dpt_sysinfo.c
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 1997 by Simon Shapiro
+ * All Rights Reserved
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that 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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* dpt_ctlinfo.c: Dunp a DPT HBA Information Block */
+
+#ident "$Id: dpt_sysinfo.c,v 1.1 1998/01/26 06:20:48 julian Exp $"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/queue.h>
+#include <sys/ioctl.h>
+#include <scsi/scsi_all.h>
+#include <scsi/scsi_message.h>
+#include <scsi/scsiconf.h>
+
+#define DPT_MEASURE_PERFORMANCE
+
+#include <sys/dpt.h>
+
+
+int
+main(int argc, char **argv, char **argp)
+{
+ eata_pt_t pass_thru;
+ dpt_sysinfo_t sysinfo;
+
+ int result;
+ int fd;
+ int ndx;
+
+ if ( (fd = open(argv[1], O_RDWR, S_IRUSR | S_IWUSR)) == -1 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to open \"%s\" - %s\n",
+ argv[0], argv[1], strerror(errno));
+ exit(1);
+ }
+
+ pass_thru.eataID[0] = 'E';
+ pass_thru.eataID[1] = 'A';
+ pass_thru.eataID[2] = 'T';
+ pass_thru.eataID[3] = 'A';
+ pass_thru.command = DPT_SYSINFO;
+ pass_thru.command_buffer = (u_int8_t *)&sysinfo;
+
+ if ( (result = ioctl(fd, DPT_IOCTL_SEND, &pass_thru)) != 0 ) {
+ (void)fprintf(stderr, "%s ERROR: Failed to send IOCTL %lx - %s\n",
+ argv[0], DPT_IOCTL_SEND,
+ strerror(errno));
+ exit(1);
+ }
+
+ (void)fprintf(stdout, "%x:%x:%d:",
+ sysinfo.drive0CMOS, sysinfo.drive1CMOS, sysinfo.numDrives);
+
+ switch (sysinfo.processorFamily) {
+ case PROC_INTEL:
+ (void)fprintf(stdout, "Intel:");
+ switch (sysinfo.processorType) {
+ case PROC_8086:
+ (void)fprintf(stdout, "8086:");
+ break;
+ case PROC_286:
+ (void)fprintf(stdout, "80286:");
+ break;
+ case PROC_386:
+ (void)fprintf(stdout, "i386:");
+ break;
+ case PROC_486:
+ (void)fprintf(stdout, "80486:");
+ break;
+ case PROC_PENTIUM:
+ (void)fprintf(stdout, "Pentium:");
+ break;
+ case PROC_P6:
+ (void)fprintf(stdout, "Pentium-Pro:");
+ break;
+ default:
+ (void)fprintf(stdout, "Unknown (%d):", sysinfo.processorType);
+ }
+ break;
+ case PROC_MOTOROLA:
+ (void)fprintf(stdout, "Motorola:");
+ switch (sysinfo.processorType) {
+ case PROC_68000:
+ (void)fprintf(stdout, "M68000");
+ break;
+ case PROC_68020:
+ (void)fprintf(stdout, "M68020");
+ break;
+ case PROC_68030:
+ (void)fprintf(stdout, "M68030");
+ break;
+ case PROC_68040:
+ (void)fprintf(stdout, "M68040");
+ break;
+ default:
+ (void)fprintf(stdout, "Unknown (%d):", sysinfo.processorType);
+ }
+ break;
+ case PROC_MIPS4000:
+ (void)fprintf(stdout, "MIPS:Any:");
+ break;
+ case PROC_ALPHA:
+ (void)fprintf(stdout, "Alpha:Any:");
+ break;
+ default:
+ (void)fprintf(stdout, "Unknown (%d):Any:", sysinfo.processorFamily);
+ }
+
+ (void)fprintf(stdout, "%d.%d.%d:",
+ sysinfo.smartROMMajorVersion,
+ sysinfo.smartROMMinorVersion,
+ sysinfo.smartROMRevision);
+
+ (void)fprintf(stdout, "%c%c%c%c%c%c%c%c%c%c%c:",
+ (sysinfo.flags & SI_CMOS_Valid) ? '+' : '-',
+ (sysinfo.flags & SI_NumDrivesValid) ? '+' : '-',
+ (sysinfo.flags & SI_ProcessorValid) ? '+' : '-',
+ (sysinfo.flags & SI_MemorySizeValid) ? '+' : '-',
+ (sysinfo.flags & SI_DriveParamsValid) ? '+' : '-',
+ (sysinfo.flags & SI_SmartROMverValid) ? '+' : '-',
+ (sysinfo.flags & SI_OSversionValid) ? '+' : '-',
+ (sysinfo.flags & SI_OSspecificValid) ? '+' : '-',
+ (sysinfo.flags & SI_BusTypeValid) ? '+' : '-',
+ (sysinfo.flags & SI_ALL_VALID) ? '+' : '-',
+ (sysinfo.flags & SI_NO_SmartROM) ? '+' : '-');
+
+ (void)fprintf(stdout, "%d:", sysinfo.conventionalMemSize);
+ (void)fprintf(stdout, "%d:", sysinfo.extendedMemSize);
+
+ switch (sysinfo.osType) {
+ case OS_DOS:
+ (void)fprintf(stdout, "DOS:");
+ break;
+ case OS_WINDOWS:
+ (void)fprintf(stdout, "Win3.1:");
+ break;
+ case OS_WINDOWS_NT:
+ (void)fprintf(stdout, "NT:");
+ break;
+ case OS_OS2M:
+ (void)fprintf(stdout, "OS/2-std:");
+ break;
+ case OS_OS2L:
+ (void)fprintf(stdout, "OS/2-LADDR:");
+ break;
+ case OS_OS22x:
+ (void)fprintf(stdout, "OS/2-2.x:");
+ break;
+ case OS_NW286:
+ (void)fprintf(stdout, "NetWare-286:");
+ break;
+ case OS_NW386:
+ (void)fprintf(stdout, "NetWare-386:");
+ break;
+ case OS_GEN_UNIX:
+ (void)fprintf(stdout, "Unix:");
+ break;
+ case OS_SCO_UNIX:
+ (void)fprintf(stdout, "SCO Unix:");
+ break;
+ case OS_ATT_UNIX:
+ (void)fprintf(stdout, "AT&T Unix:");
+ break;
+ case OS_UNIXWARE:
+ (void)fprintf(stdout, "UnixWare:");
+ break;
+ case OS_INT_UNIX:
+ (void)fprintf(stdout, "IAC Unix:");
+ break;
+ case OS_SOLARIS:
+ (void)fprintf(stdout, "Solaris:");
+ break;
+ case OS_QNX:
+ (void)fprintf(stdout, "Qnx:");
+ break;
+ case OS_NEXTSTEP:
+ (void)fprintf(stdout, "NextStep:");
+ break;
+ case OS_BANYAN:
+ (void)fprintf(stdout, "Banyan:");
+ break;
+ case OS_OLIVETTI_UNIX:
+ (void)fprintf(stdout, "Olivetti Unix:");
+ break;
+ case OS_FREEBSD:
+ (void)fprintf(stdout, "FreeBSD:");
+ break;
+ case OS_OTHER:
+ (void)fprintf(stdout, "Other (%d):", sysinfo.osType);
+ break;
+ default:
+ (void)fprintf(stdout, "Unknown (%d):", sysinfo.osType);
+ }
+
+ (void)fprintf(stdout, "%d.%d.%d.%d:", sysinfo.osMajorVersion,
+ sysinfo.osMinorVersion, sysinfo.osRevision,
+ sysinfo.osSubRevision);
+
+ switch (sysinfo.busType) {
+ case HBA_BUS_ISA:
+ (void)fprintf(stdout, "ISA:");
+ break;
+ case HBA_BUS_EISA:
+ (void)fprintf(stdout, "EISA:");
+ break;
+ case HBA_BUS_PCI:
+ (void)fprintf(stdout, "PCI:");
+ break;
+ default:
+ (void)fprintf(stdout, "Unknown (%d):", sysinfo.busType);
+ }
+
+ for (ndx = 0; ndx < 16; ndx++) {
+ if (sysinfo.drives[ndx].cylinders == 0)
+ continue;
+ (void)fprintf(stdout, "d%dc%dh%ds%d:", ndx,
+ sysinfo.drives[ndx].cylinders,
+ sysinfo.drives[ndx].heads,
+ sysinfo.drives[ndx].sectors);
+ }
+
+ (void)fprintf(stdout, "\n");
+
+ return(0);
+}
diff --git a/usr.sbin/edquota/Makefile b/usr.sbin/edquota/Makefile
new file mode 100644
index 0000000..1eb5de9
--- /dev/null
+++ b/usr.sbin/edquota/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= edquota
+MAN8= edquota.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/edquota/edquota.8 b/usr.sbin/edquota/edquota.8
new file mode 100644
index 0000000..5909425
--- /dev/null
+++ b/usr.sbin/edquota/edquota.8
@@ -0,0 +1,174 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt EDQUOTA 8
+.Os
+.Sh NAME
+.Nm edquota
+.Nd edit user quotas
+.Sh SYNOPSIS
+.Nm edquota
+.Op Fl u
+.Op Fl p Ar proto-username
+.Ar username ...
+.Nm edquota
+.Fl g
+.Op Fl p Ar proto-groupname
+.Ar groupname ...
+.Nm edquota
+.Fl t
+.Op Fl u
+.Nm edquota
+.Fl t
+.Fl g
+.br
+.Sh DESCRIPTION
+.Nm Edquota
+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 filesystems 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 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.
+.Pp
+Users are permitted to exceed their soft limits
+for a grace period that may be specified per filesystem.
+Once the grace period has expired,
+the soft limit is enforced as a hard limit.
+The default grace period for a filesystem is specified in
+.Pa /usr/include/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 filesystems with user
+quotas specified in
+.Pa /etc/fstab .
+When invoked with the
+.Fl g
+flag the grace period is
+set for all the filesystems 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 filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+to find filesystem names and locations
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr fstab 2 ,
+.Xr quotactl 2 ,
+.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..0d983a9
--- /dev/null
+++ b/usr.sbin/edquota/edquota.c
@@ -0,0 +1,756 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)edquota.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: edquota.c,v 1.7 1997/09/17 06:29:23 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * 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"
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+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 __P((char *s));
+int cvtatos __P((time_t, char *, time_t *));
+char *cvtstoa __P((time_t));
+int editit __P((char *));
+void freeprivs __P((struct quotause *));
+int getentry __P((char *, int));
+struct quotause *getprivs __P((long, int));
+int hasquota __P((struct fstab *, int, char **));
+void putprivs __P((long, int, struct quotause *));
+int readprivs __P((struct quotause *, char *));
+int readtimes __P((struct quotause *, char *));
+static void usage __P((void));
+int writetimes __P((struct quotause *, int, int));
+int writeprivs __P((struct quotause *, int, char *, int));
+
+int
+main(argc, argv)
+ register char **argv;
+ int argc;
+{
+ register struct quotause *qup, *protoprivs, *curprivs;
+ register long id, protoid;
+ register int quotatype, tmpfd;
+ register uid_t startuid, enduid;
+ char *protoname, *cp, ch;
+ int tflag = 0, pflag = 0;
+ char buf[30];
+
+ if (argc < 2)
+ usage();
+ if (getuid())
+ errx(1, "permission denied");
+ quotatype = USRQUOTA;
+ while ((ch = getopt(argc, argv, "ugtp:")) != -1) {
+ switch(ch) {
+ case 'p':
+ protoname = optarg;
+ pflag++;
+ break;
+ case 'g':
+ quotatype = GRPQUOTA;
+ break;
+ case 'u':
+ quotatype = USRQUOTA;
+ break;
+ case 't':
+ tflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (pflag) {
+ if ((protoid = getentry(protoname, quotatype)) == -1)
+ exit(1);
+ protoprivs = getprivs(protoid, quotatype);
+ for (qup = protoprivs; qup; qup = qup->next) {
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ while (argc-- > 0) {
+ if (isdigit(*argv[0]) &&
+ (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);
+ for ( ; startuid <= enduid; startuid++) {
+ snprintf(buf, sizeof(buf), "%d",
+ startuid);
+ if ((id = getentry(buf, quotatype)) < 0)
+ continue;
+ putprivs(id, quotatype, protoprivs);
+ }
+ continue;
+ }
+ if ((id = getentry(*argv++, quotatype)) < 0)
+ continue;
+ putprivs(id, quotatype, protoprivs);
+ }
+ exit(0);
+ }
+ tmpfd = mkstemp(tmpfil);
+ fchown(tmpfd, getuid(), getgid());
+ if (tflag) {
+ protoprivs = getprivs(0, quotatype);
+ 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);
+ 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",
+ "usage: edquota [-u] [-p username] username ...",
+ " edquota -g [-p groupname] groupname ...",
+ " edquota [-u] -t",
+ " edquota -g -t");
+ 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)
+ 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)
+ register long id;
+ int quotatype;
+{
+ 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 (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(tmpfile)
+ char *tmpfile;
+{
+ long omask;
+ int pid, stat;
+
+ 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 char *ed;
+
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = _PATH_VI;
+ execlp(ed, ed, tmpfile, 0);
+ err(1, "%s", ed);
+ }
+ waitpid(pid, &stat, 0);
+ sigsetmask(omask);
+ if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 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, "blocks 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:", qup->dqblk.dqb_curinodes,
+ qup->dqblk.dqb_isoftlimit, 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;
+ 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,
+ " blocks in use: %lu, limits (soft = %lu, hard = %lu)",
+ &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
+ &dqblk.dqb_bhardlimit);
+ if (cnt != 3) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ dqblk.dqb_curblocks = btodb((off_t)dqblk.dqb_curblocks * 1024);
+ dqblk.dqb_bsoftlimit = btodb((off_t)dqblk.dqb_bsoftlimit
+ * 1024);
+ dqblk.dqb_bhardlimit = btodb((off_t)dqblk.dqb_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)",
+ &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
+ &dqblk.dqb_ihardlimit);
+ if (cnt != 3) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ 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;
+ 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",
+ &btime, bunits, &itime, iunits);
+ if (cnt != 4) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ if (cvtatos(btime, bunits, &bseconds) == 0)
+ return (0);
+ if (cvtatos(itime, iunits, &iseconds) == 0)
+ return (0);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ qup->dqblk.dqb_btime = bseconds;
+ qup->dqblk.dqb_itime = iseconds;
+ qup->flags |= FOUND;
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * reset default grace periods for any filesystems
+ * that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert seconds to ASCII times.
+ */
+char *
+cvtstoa(time)
+ time_t time;
+{
+ static char buf[20];
+
+ if (time % (24 * 60 * 60) == 0) {
+ time /= 24 * 60 * 60;
+ sprintf(buf, "%ld day%s", time, time == 1 ? "" : "s");
+ } else if (time % (60 * 60) == 0) {
+ time /= 60 * 60;
+ sprintf(buf, "%ld hour%s", time, time == 1 ? "" : "s");
+ } else if (time % 60 == 0) {
+ time /= 60;
+ sprintf(buf, "%ld minute%s", time, time == 1 ? "" : "s");
+ } else
+ sprintf(buf, "%ld second%s", time, time == 1 ? "" : "s");
+ return (buf);
+}
+
+/*
+ * Convert ASCII input times to seconds.
+ */
+int
+cvtatos(time, units, seconds)
+ time_t time;
+ char *units;
+ time_t *seconds;
+{
+
+ if (bcmp(units, "second", 6) == 0)
+ *seconds = time;
+ else if (bcmp(units, "minute", 6) == 0)
+ *seconds = time * 60;
+ else if (bcmp(units, "hour", 4) == 0)
+ *seconds = time * 60 * 60;
+ else if (bcmp(units, "day", 3) == 0)
+ *seconds = time * 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 char *s;
+{
+ register 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/fdcontrol/Makefile b/usr.sbin/fdcontrol/Makefile
new file mode 100644
index 0000000..068cade
--- /dev/null
+++ b/usr.sbin/fdcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG = fdcontrol
+MAN8 = fdcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdcontrol/fdcontrol.8 b/usr.sbin/fdcontrol/fdcontrol.8
new file mode 100644
index 0000000..edc02f3
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.8
@@ -0,0 +1,98 @@
+.\"
+.\" Copyright (C) 1994 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.
+.\"
+.Dd May 22, 1994
+.Os
+.Dt FDCONTROL 8
+.Sh NAME
+.Nm fdcontrol
+.Nd modify floppy disk parameters
+.Sh SYNOPSIS
+.Nm fdcontrol
+.Op Fl d Ar 0|1
+.Ar device
+.Nm fdcontrol
+.Op Fl s
+.Ar device
+.Sh DESCRIPTION
+.Nm Fdcontrol
+allows the modification of the run-time behavior of the floppy
+disk device specified by
+.Ar device .
+.Ar Device
+should be a character device.
+.Pp
+.Nm Fdcontrol
+currently supports the specification of device parameters for the
+floppy disk drive
+.Po
+.Fl s ,
+also default mode
+.Pc ,
+or it allows the modification of the driver debug level, in case the
+floppy driver has been compiled into the kernel with the
+.Em DEBUG
+option set
+.Pq Fl d .
+.Pp
+Since the implications of such actions are considered harmful, the
+underlying
+.Xr ioctl 2
+command is restricted to the super-user.
+.Pp
+When requesting a new parameter specification, the command asks the
+user for each individual tunable parameter, defaulting to the
+currently used value.
+.Sh DIAGNOSTICS
+Error codes for the underlying
+.Xr ioctl 2
+commands are printed by the
+.Xr perror 3
+facility.
+.Sh BUGS
+The
+.Nm
+command is currently under development. It's user interface is rather
+silly and likely to change in future, options should be provided to
+allow anything being modified from the command line.
+.Pp
+The driver does actually support only two debug levels
+.Pq 0 and 1 ,
+where debug level 1 will generate huge amounts of output. It is likely
+to overflow the syslog if not used with extreme care.
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr perror 3 ,
+.Xr fdc 4
+.Sh HISTORY
+.Nm Fdcontrol
+is currently under development. It's user interface and overall
+functionality are subjects to future improvements and changes.
+.Sh AUTHORS
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t J\(:org Wunsch,
+Dresden.
diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c
new file mode 100644
index 0000000..c914b93
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 1994 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <machine/ioctl_fd.h>
+#include <sys/file.h>
+
+int
+getnumber(void)
+{
+ int i;
+ char b[80];
+
+ fgets(b, 80, stdin);
+ if(b[0] == '\n') return -1;
+
+ sscanf(b, " %i", &i);
+ return i;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: fdcontrol [-d 0|1] | [-s] device-node\n");
+ exit(2);
+}
+
+
+#define ask(name, fmt) \
+printf(#name "? [" fmt "]: ", ft.name); fflush(stdout); \
+if((i = getnumber()) != -1) ft.name = i
+
+int
+main(int argc, char **argv)
+{
+ struct fd_type ft;
+ int fd, i;
+ int debug = -1, settype = 1;
+
+ while((i = getopt(argc, argv, "d:s")) != -1)
+ switch(i)
+ {
+ case 'd':
+ debug = atoi(optarg);
+ settype = 0;
+ break;
+
+ case 's':
+ debug = -1;
+ settype = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if((fd = open(argv[0], 0)) < 0)
+ {
+ warn("open(floppy)");
+ return 1;
+ }
+
+ if(debug != -1)
+ {
+ if(ioctl(fd, FD_DEBUG, &debug) < 0)
+ {
+ warn("ioctl(FD_DEBUG)");
+ return 1;
+ }
+ return 0;
+ }
+
+ if(settype)
+ {
+ if(ioctl(fd, FD_GTYPE, &ft) < 0)
+ {
+ warn("ioctl(FD_GTYPE)");
+ return 1;
+ }
+
+ ask(sectrac, "%d");
+ ask(secsize, "%d");
+ ask(datalen, "0x%x");
+ ask(gap, "0x%x");
+ ask(tracks, "%d");
+ ask(size, "%d");
+ ask(steptrac, "%d");
+ ask(trans, "%d");
+ ask(heads, "%d");
+ ask(f_gap, "0x%x");
+ ask(f_inter, "%d");
+
+ if(ioctl(fd, FD_STYPE, &ft) < 0)
+ {
+ warn("ioctl(FD_STYPE)");
+ return 1;
+ }
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/fdformat/Makefile b/usr.sbin/fdformat/Makefile
new file mode 100644
index 0000000..e489ebc
--- /dev/null
+++ b/usr.sbin/fdformat/Makefile
@@ -0,0 +1,9 @@
+#
+PROG = fdformat
+
+# the -I's seem to be confusing, but necessery this way
+# (so the right <unistd.h> will be found in /usr/include, and the
+# "../i386/isa/ic/nec765.h" included from fdreg.h is accessible, too)
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdformat/fdformat.1 b/usr.sbin/fdformat/fdformat.1
new file mode 100644
index 0000000..55b3d46
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.1
@@ -0,0 +1,161 @@
+.\" Copyright (C) 1993, 1994, 1995 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.
+.\"
+.Dd September 16, 1993
+.Os
+.Dt FDFORMAT 1
+.Sh NAME
+.Nm fdformat
+.Nd format floppy disks
+.Sh SYNOPSIS
+.Nm fdformat
+.Op Fl q
+.Op Fl v | Fl n
+.Op Fl f Ar capacity
+.Op Fl c Ar cyls
+.Op Fl s Ar secs
+.Op Fl h Ar heads
+.br
+.Op Fl r Ar rate
+.Op Fl g Ar gap3len
+.Op Fl i Ar intleave
+.Op Fl S Ar secshft
+.Op Fl F Ar fillbyte
+.Op Fl t Ar steps_per_track
+.Ar device_name
+.Sh DESCRIPTION
+.Nm Fdformat
+formats a floppy disk at device
+.Ar device_name .
+.Ar Device_name
+should be a character device; it may be given either with a full path
+name of a raw device node for a floppy disk drive
+.Pq e.\ g. Pa /dev/rfd0 ,
+or default name in an abbreviated form
+.Pq e.\ g. Em fd0 .
+In the latter case, the name is constructed by prepending
+.Pa /dev/r
+and appending a
+.Em .capacity
+to the
+.Ar device_name .
+Note that any geometry constraints of the device node
+.Pq minor device number
+are meaningless, since they're overridden by
+.Nm fdformat .
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl q
+Suppress any normal output from the command, and don't ask the
+user for a confirmation whether to format the floppy disk at
+.Ar device_name .
+.It Fl f Ar capacity
+The normal way to specify the desired formatting parameters.
+.Ar Capacity
+is the number of kilobytes to format. Valid choices are 360, 720, 800, 820,
+1200, 1440, 1480 or 1720.
+.It Fl n
+Don't verify floppy after formatting.
+.It Fl v
+Don't format, verify only.
+.It Fl c Ar cyls
+Number of cylinders: 40 or 80.
+.It Fl s Ar secs
+Number of sectors per track: 9, 10, 15 or 18.
+.It Fl h Ar heads
+Number of floppy heads: 1 or 2.
+.It Fl r Ar rate
+Data rate: 250, 300 or 500 kbps.
+.It Fl g Ar gap3len
+Gap length.
+.It Fl i Ar intleave
+Interleave factor.
+.It Fl S Ar secshft
+Sector size: 0=128, 1=256, 2=512 bytes.
+.It Fl F Ar fillbyte
+Fill byte.
+.It Fl t Ar steps_per_track
+Number of steps per track.
+An alternate method to specify the geometry data to write to the floppy disk.
+.El
+
+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
+.Dq y
+must be given.
+.Pp
+Note that
+.Nm
+does only perform low-level formatting. In case you wish to create
+a file system on the medium, see the commands
+.Xr newfs 8
+for an
+.Em ufs
+file system, or
+.Xr newfs_msdos 8
+for an
+.Em 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
+.Sq Em F
+is printed when the track(s) is being formatted, then a
+.Sq Em V
+while it's being verified, and if an error has been detected, it
+will finally change to
+.Sq Em E .
+.Pp
+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 newfs 8 ,
+.Xr newfs_msdos 8
+.Sh HISTORY
+.Nm Fdformat
+has been developed for 386BSD 0.1
+and upgraded to the new
+.Xr fdc 4
+floppy disk driver. It later became part of the
+.Fx 1.1
+system.
+.Sh AUTHORS
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t 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..a2014be
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 1992-1994 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:
+ * format a floppy disk
+ *
+ * Added FD_GTYPE ioctl, verifying, proportional indicators.
+ * Serge Vakulenko, vak@zebub.msk.su
+ * Sat Dec 18 17:45:47 MSK 1993
+ *
+ * Final adaptation, change format/verify logic, add separate
+ * format gap/interleave values
+ * Andrew A. Chernov, ache@astral.msk.su
+ * Thu Jan 27 00:47:24 MSK 1994
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <machine/ioctl_fd.h>
+
+static void
+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[FD_MAX_NSEC + 1];
+
+ 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;
+ }
+ if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
+ err(1, "ioctl(FD_FORM)");
+}
+
+static int
+verify_track(int fd, int track, int tracksize)
+{
+ static char *buf = 0;
+ static int bufsz = 0;
+ 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) {
+ if (buf)
+ free (buf);
+ bufsz = tracksize;
+ buf = 0;
+ }
+ if (! buf)
+ buf = malloc (bufsz);
+ if (! buf)
+ errx(2, "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 const char *
+makename(const char *arg, const char *suffix)
+{
+ static char namebuff[20]; /* big enough for "/dev/rfd0a"... */
+
+ memset(namebuff, 0, 20);
+ if(*arg == '\0') /* ??? */
+ return arg;
+ if(*arg == '/') /* do not convert absolute pathnames */
+ return arg;
+ strcpy(namebuff, "/dev/r");
+ strncat(namebuff, arg, 3);
+ strcat(namebuff, suffix);
+ return namebuff;
+}
+
+static void
+usage (void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: fdformat [-q] [-n | -v] [-f #] [-c #] [-s #] [-h #]",
+ " [-r #] [-g #] [-i #] [-S #] [-F #] [-t #] devname");
+ exit(2);
+}
+
+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)
+{
+ int format = -1, cyls = -1, secs = -1, heads = -1, intleave = -1;
+ int rate = -1, gaplen = -1, secsize = -1, steps = -1;
+ int fill = 0xf6, quiet = 0, verify = 1, verify_only = 0;
+ int fd, c, track, error, tracks_per_dot, bytes_per_track, errs;
+ const char *devname, *suffix;
+ struct fd_type fdt;
+
+ while((c = getopt(argc, argv, "f:c:s:h:r:g:S:F:t:i:qvn")) != -1)
+ switch(c) {
+ case 'f': /* format in kilobytes */
+ format = atoi(optarg);
+ break;
+
+ case 'c': /* # of cyls */
+ cyls = atoi(optarg);
+ break;
+
+ case 's': /* # of secs per track */
+ secs = atoi(optarg);
+ break;
+
+ case 'h': /* # of heads */
+ heads = atoi(optarg);
+ break;
+
+ case 'r': /* transfer rate, kilobyte/sec */
+ rate = atoi(optarg);
+ break;
+
+ case 'g': /* length of GAP3 to format with */
+ gaplen = atoi(optarg);
+ break;
+
+ case 'S': /* sector size shift factor (1 << S)*128 */
+ secsize = atoi(optarg);
+ break;
+
+ case 'F': /* fill byte, C-like notation allowed */
+ fill = (int)strtol(optarg, (char **)0, 0);
+ break;
+
+ case 't': /* steps per track */
+ steps = atoi(optarg);
+ break;
+
+ case 'i': /* interleave factor */
+ intleave = atoi(optarg);
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'n':
+ verify = 0;
+ break;
+
+ case 'v':
+ verify = 1;
+ verify_only = 1;
+ break;
+
+ case '?': default:
+ usage();
+ }
+
+ if(optind != argc - 1)
+ usage();
+
+ switch(format) {
+ default:
+ errx(2, "bad floppy size: %dK", format);
+ case -1: suffix = ""; break;
+ case 360: suffix = ".360"; break;
+ case 720: suffix = ".720"; break;
+ case 800: suffix = ".800"; break;
+ case 820: suffix = ".820"; break;
+ case 1200: suffix = ".1200"; break;
+ case 1440: suffix = ".1440"; break;
+ case 1480: suffix = ".1480"; break;
+ case 1720: suffix = ".1720"; break;
+ }
+
+ devname = makename(argv[optind], suffix);
+
+ if((fd = open(devname, O_RDWR)) < 0)
+ err(1, "%s", devname);
+
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(1, "not a floppy disk: %s", devname);
+
+ switch(rate) {
+ case -1: break;
+ case 250: fdt.trans = FDC_250KBPS; break;
+ case 300: fdt.trans = FDC_300KBPS; break;
+ case 500: fdt.trans = FDC_500KBPS; break;
+ default:
+ errx(2, "invalid transfer rate: %d", rate);
+ }
+
+ if (cyls >= 0) fdt.tracks = cyls;
+ if (secs >= 0) fdt.sectrac = secs;
+ if (fdt.sectrac > FD_MAX_NSEC)
+ errx(2, "too many sectors per track, max value is %d", FD_MAX_NSEC);
+ if (heads >= 0) fdt.heads = heads;
+ if (gaplen >= 0) fdt.f_gap = gaplen;
+ if (secsize >= 0) fdt.secsize = secsize;
+ if (steps >= 0) fdt.steptrac = steps;
+ if (intleave >= 0) fdt.f_inter = intleave;
+
+ bytes_per_track = fdt.sectrac * (1<<fdt.secsize) * 128;
+ tracks_per_dot = fdt.tracks * fdt.heads / 40;
+
+ if (verify_only) {
+ if(!quiet)
+ printf("Verify %dK floppy `%s'.\n",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ devname);
+ }
+ else if(!quiet) {
+ printf("Format %dK floppy `%s'? (y/n): ",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ devname);
+ if(! yes ()) {
+ printf("Not confirmed.\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Formatting.
+ */
+ if(!quiet) {
+ printf("Processing ----------------------------------------\r");
+ printf("Processing ");
+ 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);
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ putchar('F');
+ fflush(stdout);
+ }
+ }
+ if (verify) {
+ if (verify_track(fd, track, bytes_per_track) < 0)
+ error = errs = 1;
+ 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");
+
+ return errs;
+}
+/*
+ * Local Variables:
+ * c-indent-level: 8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * c-brace-offset: -8
+ * c-brace-imaginary-offset: 0
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c++-hanging-braces: 1
+ * c++-access-specifier-offset: -8
+ * c++-empty-arglist-indent: 8
+ * c++-friend-offset: 0
+ * End:
+ */
diff --git a/usr.sbin/fdwrite/Makefile b/usr.sbin/fdwrite/Makefile
new file mode 100644
index 0000000..7af331d
--- /dev/null
+++ b/usr.sbin/fdwrite/Makefile
@@ -0,0 +1,16 @@
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dkuug.dk> 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$
+#
+#
+
+PROG = fdwrite
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdwrite/fdwrite.1 b/usr.sbin/fdwrite/fdwrite.1
new file mode 100644
index 0000000..3e8cf35
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.1
@@ -0,0 +1,121 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@login.dkuug.dk> 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: fdwrite.1,v 1.8 1998/06/21 04:33:36 mph Exp $
+.\"
+.\"
+.Dd September 16, 1993
+.Os FreeBSD
+.Dt FDWRITE 1
+.Sh NAME
+.Nm fdwrite
+.Nd format and write floppy disks
+.Sh SYNOPSIS
+.Nm fdwrite
+.Op Fl v
+.Op Fl y
+.Op Fl f Ar inputfile
+.Op Fl d Ar device
+.Sh DESCRIPTION
+.Nm Fdwrite
+formats and writes one and more floppy disks.
+Any floppy disk device capable of formatting can be used.
+
+.Nm Fdwrite
+will ask the user
+.Pq on /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 .
+
+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 ``/dev/rfd0''.
+.El
+
+.Nm Fdwrite
+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.
+
+The parameters returned from
+.Ar device
+are used for formatting.
+If custom formatting is needed, please use
+.Xr fdformat 1
+instead.
+
+.Sh EXAMPLE
+.Nm Fdwrite
+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:
+
+.ce 1
+tar cf - . | gzip -9 | fdwrite -d /dev/rfd0.1720 -v
+
+The main difference from using
+.Xr tar 1 '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
+.Nm Fdwrite
+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@login.dknet.dk .
+.Sh BUGS
+Diagnostics are less than complete at present.
+
+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.
+
+This concept could be extended to cover non-seekable input also
+by employing a temporary file.
+
+An option (defaulting to zero) should allow the user to ask for
+retries in case of failure.
+
+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..e0c176b
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.c
@@ -0,0 +1,194 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dkuug.dk> 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: fdwrite.c,v 1.6 1997/02/22 16:05:49 peter Exp $
+ *
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <machine/ioctl_fd.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;
+ char *device= "/dev/rfd0", *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("/dev/tty","r+");
+ if(!tty)
+ err(1, "/dev/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);
+
+ 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/i4b/Makefile b/usr.sbin/i4b/Makefile
new file mode 100644
index 0000000..c1d71e7
--- /dev/null
+++ b/usr.sbin/i4b/Makefile
@@ -0,0 +1,4 @@
+SUBDIR = isdntrace isdndebug isdnd alawulaw man isdntest \
+ isdntel isdntelctl isdnmonitor isdndecode dtmfdecode
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/i4b/Makefile.inc b/usr.sbin/i4b/Makefile.inc
new file mode 100644
index 0000000..691d738
--- /dev/null
+++ b/usr.sbin/i4b/Makefile.inc
@@ -0,0 +1,14 @@
+# 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/alawulaw/Makefile b/usr.sbin/i4b/alawulaw/Makefile
new file mode 100644
index 0000000..1daa481
--- /dev/null
+++ b/usr.sbin/i4b/alawulaw/Makefile
@@ -0,0 +1,7 @@
+PROG = alaw2ulaw
+SRC = alaw2ulaw.c
+CFLAGS += -Wall -g -DDEBUG
+LINKS = ${BINDIR}/alaw2ulaw ${BINDIR}/ulaw2alaw
+MAN1 = alaw2ulaw.1 ulaw2alaw.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/alawulaw/alaw2ulaw.1 b/usr.sbin/i4b/alawulaw/alaw2ulaw.1
new file mode 100644
index 0000000..a99f3ce
--- /dev/null
+++ b/usr.sbin/i4b/alawulaw/alaw2ulaw.1
@@ -0,0 +1,70 @@
+.\"
+.\" Copyright (c) 1998, 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: alaw2ulaw.1,v 1.4 1999/02/14 09:44:55 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:01:36 1999]
+.\"
+.\" -hm writing manual pages
+.\"
+.\"
+.Dd January 23, 1998
+.Dt alaw2ulaw 1
+.Sh NAME
+.Nm alaw2ulaw
+.Nd convert sound data
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm alaw2ulaw
+is part of the isdn4bsd package and is used convert sound data between
+uLaw coded data to ALaw coded data and vice versa.
+.Pp
+It reads data from stdin and outputs converted data to stdout.
+.Pp
+In case it is run as
+.Em alaw2ulaw
+it converts ALaw input data to uLaw output.
+.Pp
+In case it is run as
+.Em ulaw2alaw
+it converts uLaw input data to ALaw output.
+.Pp
+
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+alaw2ulaw <file.alaw >file.ulaw
+.Ed
+.Pp
+converts ALaw input data file file.alaw to uLaw output file file.ulaw.
+
+.Sh STANDARDS
+ITU Recommendations G.711
+
+.Sh AUTHOR
+The
+.Nm
+utility and this man page were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/alawulaw/alaw2ulaw.c b/usr.sbin/i4b/alawulaw/alaw2ulaw.c
new file mode 100644
index 0000000..80f1e19
--- /dev/null
+++ b/usr.sbin/i4b/alawulaw/alaw2ulaw.c
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * convert a-law / u-law sound files
+ * ---------------------------------
+ *
+ * last edit-date: [Tue Feb 16 11:23:27 1999]
+ *
+ * $Id: alaw2ulaw.c,v 1.4 1999/02/16 10:40:18 hm Exp $
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <string.h>
+#include <unistd.h>
+
+#define BUF_SIZ 2048
+
+static unsigned char raw_ulaw[];
+static unsigned char ulaw_raw[];
+
+
+int main(int argc, char *argv[])
+{
+ int i, j;
+ unsigned char buffer[BUF_SIZ];
+ char *p;
+ unsigned char *cp;
+
+ if((p = rindex(*argv, '/')) != NULL)
+ p++;
+ else
+ p = *argv;
+
+ if(!strcmp(p, "ulaw2alaw"))
+ {
+ cp = ulaw_raw;
+ }
+ else if(!strcmp(p, "alaw2ulaw"))
+ {
+ cp = raw_ulaw;
+ }
+ else
+ {
+ return(1);
+ }
+
+ while(((j = read(0, buffer, BUF_SIZ)) > 0))
+ {
+ for(i = 0; i < j; i++)
+ buffer[i] = cp[buffer[i]];
+ write(1, buffer, j);
+ }
+ return(0);
+}
+
+
+/*---------------------------------------------------------------------------*
+ * Line format to mu-law conversion
+ *---------------------------------------------------------------------------*/
+static unsigned char raw_ulaw[256] = {
+ 0x2a, 0xa9, 0x62, 0xe1, 0x0a, 0x8a, 0x48, 0xc8, 0x39, 0xb9, 0x75, 0xf3,0x1a, 0x9a, 0x56, 0xd6,
+ 0x22, 0xa1, 0x5d, 0xdc, 0x02, 0x82, 0x40, 0xc0, 0x31, 0xb1, 0x6a, 0xe9,0x12, 0x92, 0x4f, 0xcf,
+ 0x2e, 0xad, 0x66, 0xe5, 0x0e, 0x8e, 0x4c, 0xcc, 0x3d, 0xbd, 0x7d, 0xfb,0x1e, 0x9e, 0x5a, 0xda,
+ 0x26, 0xa5, 0x5f, 0xde, 0x06, 0x86, 0x44, 0xc4, 0x35, 0xb5, 0x6e, 0xed,0x16, 0x96, 0x52, 0xd2,
+ 0x28, 0xa7, 0x60, 0xdf, 0x08, 0x88, 0x46, 0xc6, 0x37, 0xb7, 0x71, 0xef,0x18, 0x98, 0x54, 0xd4,
+ 0x20, 0x9f, 0x5c, 0xdb, 0x00, 0x80, 0x3f, 0xbf, 0x2f, 0xaf, 0x68, 0xe7,0x10, 0x90, 0x4e, 0xce,
+ 0x2c, 0xab, 0x64, 0xe3, 0x0c, 0x8c, 0x4a, 0xca, 0x3b, 0xbb, 0x79, 0xf7,0x1c, 0x9c, 0x58, 0xd8,
+ 0x24, 0xa3, 0x5e, 0xdd, 0x04, 0x84, 0x42, 0xc2, 0x33, 0xb3, 0x6c, 0xeb,0x14, 0x94, 0x50, 0xd0,
+ 0x2b, 0xaa, 0x63, 0xe2, 0x0b, 0x8b, 0x49, 0xc9, 0x3a, 0xba, 0x77, 0xf5,0x1b, 0x9b, 0x57, 0xd7,
+ 0x23, 0xa2, 0x5d, 0xdd, 0x03, 0x83, 0x41, 0xc1, 0x32, 0xb2, 0x6b, 0xea,0x13, 0x93, 0x4f, 0xcf,
+ 0x2f, 0xae, 0x67, 0xe6, 0x0f, 0x8f, 0x4d, 0xcd, 0x3e, 0xbe, 0xff, 0xfd,0x1f, 0x9f, 0x5b, 0xdb,
+ 0x27, 0xa6, 0x5f, 0xdf, 0x07, 0x87, 0x45, 0xc5, 0x36, 0xb6, 0x6f, 0xee,0x17, 0x97, 0x53, 0xd3,
+ 0x29, 0xa8, 0x61, 0xe0, 0x09, 0x89, 0x47, 0xc7, 0x38, 0xb8, 0x73, 0xf1,0x19, 0x99, 0x55, 0xd5,
+ 0x21, 0xa0, 0x5c, 0xdc, 0x01, 0x81, 0x3f, 0xbf, 0x30, 0xb0, 0x69, 0xe8,0x11, 0x91, 0x4e, 0xce,
+ 0x2d, 0xac, 0x65, 0xe4, 0x0d, 0x8d, 0x4b, 0xcb, 0x3c, 0xbc, 0x7b, 0xf9,0x1d, 0x9d, 0x59, 0xd9,
+ 0x25, 0xa4, 0x5e, 0xde, 0x05, 0x85, 0x43, 0xc3, 0x34, 0xb4, 0x6d, 0xec,0x15, 0x95, 0x51, 0xd1
+};
+
+
+/*---------------------------------------------------------------------------*
+ * mu-law to line format conversion
+*---------------------------------------------------------------------------*/
+static unsigned char ulaw_raw[256] = {
+ 0x54, 0xd4, 0x14, 0x94, 0x74, 0xf4, 0x34, 0xb4, 0x44, 0xc4, 0x04, 0x84,0x64, 0xe4, 0x24, 0xa4,
+ 0x5c, 0xdc, 0x1c, 0x9c, 0x7c, 0xfc, 0x3c, 0xbc, 0x4c, 0xcc, 0x0c, 0x8c,0x6c, 0xec, 0x2c, 0xac,
+ 0xd0, 0x10, 0x90, 0x70, 0xf0, 0x30, 0xb0, 0x40, 0xc0, 0x00, 0x80, 0x60,0xe0, 0x20, 0xa0, 0x58,
+ 0xd8, 0x18, 0x98, 0x78, 0xf8, 0x38, 0xb8, 0x48, 0xc8, 0x08, 0x88, 0x68,0xe8, 0x28, 0xa8, 0xd6,
+ 0x16, 0x96, 0x76, 0xf6, 0x36, 0xb6, 0x46, 0xc6, 0x06, 0x86, 0x66, 0xe6,0x26, 0xa6, 0xde, 0x9e,
+ 0x7e, 0xfe, 0x3e, 0xbe, 0x4e, 0xce, 0x0e, 0x8e, 0x6e, 0xee, 0x2e, 0xae,0xd2, 0x92, 0xf2, 0xb2,
+ 0xc2, 0x02, 0x82, 0x62, 0xe2, 0x22, 0xa2, 0x5a, 0xda, 0x1a, 0x9a, 0x7a,0xfa, 0x3a, 0xba, 0x4a,
+ 0x4a, 0xca, 0xca, 0x0a, 0x0a, 0x8a, 0x8a, 0x6a, 0x6a, 0xea, 0xea, 0x2a,0x2a, 0xaa, 0xab, 0xab,
+ 0x55, 0xd5, 0x15, 0x95, 0x75, 0xf5, 0x35, 0xb5, 0x45, 0xc5, 0x05, 0x85,0x65, 0xe5, 0x25, 0xa5,
+ 0x5d, 0xdd, 0x1d, 0x9d, 0x7d, 0xfd, 0x3d, 0xbd, 0x4d, 0xcd, 0x0d, 0x8d,0x6d, 0xed, 0x2d, 0xad,
+ 0x51, 0xd1, 0x11, 0x91, 0x71, 0xf1, 0x31, 0xb1, 0x41, 0xc1, 0x01, 0x81,0x61, 0xe1, 0x21, 0xa1,
+ 0xd9, 0x19, 0x99, 0x79, 0xf9, 0x39, 0xb9, 0x49, 0xc9, 0x09, 0x89, 0x69,0xe9, 0x29, 0xa9, 0x57,
+ 0x17, 0x97, 0x77, 0xf7, 0x37, 0xb7, 0x47, 0xc7, 0x07, 0x87, 0x67, 0xe7,0x27, 0xa7, 0x5f, 0x1f,
+ 0x7f, 0xff, 0x3f, 0xbf, 0x4f, 0xcf, 0x0f, 0x8f, 0x6f, 0xef, 0x2f, 0xaf,0x53, 0x13, 0x73, 0x33,
+ 0x43, 0xc3, 0x03, 0x83, 0x63, 0xe3, 0x23, 0xa3, 0x5b, 0xdb, 0x1b, 0x9b,0x7b, 0xfb, 0x3b, 0xbb,
+ 0xbb, 0x4b, 0x4b, 0xcb, 0xcb, 0x0b, 0x0b, 0x8b, 0x8b, 0x6b, 0x6b, 0xeb,0xeb, 0x2b, 0x2b, 0xab
+};
+
+/* EOF */
diff --git a/usr.sbin/i4b/alawulaw/ulaw2alaw.1 b/usr.sbin/i4b/alawulaw/ulaw2alaw.1
new file mode 100644
index 0000000..358e73b
--- /dev/null
+++ b/usr.sbin/i4b/alawulaw/ulaw2alaw.1
@@ -0,0 +1,70 @@
+.\"
+.\" Copyright (c) 1998, 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: ulaw2alaw.1,v 1.4 1999/02/14 09:44:55 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:01:51 1999]
+.\"
+.\" -hm writing manual pages
+.\"
+.\"
+.Dd January 23, 1998
+.Dt ulaw2alaw 1
+.Sh NAME
+.Nm ulaw2alaw
+.Nd convert sound data
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm ulaw2alaw
+is part of the isdn4bsd package and is used convert sound data between
+uLaw coded data to ALaw coded data and vice versa.
+.Pp
+It reads data from stdin and outputs converted data to stdout.
+.Pp
+In case it is run as
+.Em alaw2ulaw
+it converts ALaw input data to uLaw output.
+.Pp
+In case it is run as
+.Em ulaw2alaw
+it converts uLaw input data to ALaw output.
+.Pp
+
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+ulaw2alaw <file.ulaw >file.alaw
+.Ed
+.Pp
+converts uLaw input data file file.ulaw to ALaw output file file.alaw.
+
+.Sh STANDARDS
+ITU Recommendations G.711
+
+.Sh AUTHOR
+The
+.Nm
+utility and this man page were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/dtmfdecode/Makefile b/usr.sbin/i4b/dtmfdecode/Makefile
new file mode 100644
index 0000000..e814285
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/Makefile
@@ -0,0 +1,15 @@
+#---------------------------------------------------------------------------
+#
+# $Id: Makefile,v 1.1 1999/02/15 19:13:47 hm Exp $
+#
+# last edit-date: [Mon Feb 15 20:04:40 1999]
+#
+#---------------------------------------------------------------------------
+
+PROG = dtmfdecode
+SRC = dtmfdecode.c
+LDADD += -lm
+CFLAGS += -Wall -g -DDEBUG
+MAN1 = dtmfdecode.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/dtmfdecode/README b/usr.sbin/i4b/dtmfdecode/README
new file mode 100644
index 0000000..d25cbd2
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/README
@@ -0,0 +1,44 @@
+
+ [Note: the version included in i4b does not output any data you can
+ plot, but will just print the values of the tones it detected. -hm]
+
+
+Poul-Henning Kamp wrote:
+------------------------
+
+I remember that somebody asked about this long time ago, so I sat
+down and hacked a digital filter for that.
+
+The following piece of code will read a ".g711a" file, and output
+9 columns of data. The first is the linear value of the sample,
+the other 8 are strength of the 8 DTMF tones.
+
+Try to run the "beep.g711a" file from i4b through it, and plot the
+output columns with gnuplot. It seems Hellmuth pressed a '1' :-)
+
+The implementation is a recursive resonance filter, actually 8 of
+them, one for each frequency, done in floating point. With a little
+attention to rounding, it can be done just as good, and much faster
+in integer math, in fact 16 bit should be enough, but may not be
+faster than 32bit.
+
+The "POLRAD" quantity determines the resonance width of the filters,
+if you make it too low, it will confuse tones and recognize them
+where they are not. If you make it too high (never, ever >= 1.0!)
+it will take longer to react and maybe not catch a slightly offbeat
+tone. If you set it above or equal to 1.0 you get a tone generator.
+
+This could also be a good basis for a 300Baud FSK modem emulation.
+
+It seems that the .g711a files are bit-flipped, therefore the flip[]
+array trick in this code. The alaw->linear converter is lifted from
+sox.
+
+Now, who writes the answering-machine to end all answering machines
+for i4b ?
+
+Poul-Henning
+--
+Poul-Henning Kamp FreeBSD coreteam member
+phk@FreeBSD.ORG "Real hackers run -current on their laptop."
+"ttyv0" -- What UNIX calls a $20K state-of-the-art, 3D, hi-res color terminal
diff --git a/usr.sbin/i4b/dtmfdecode/dtmfdecode.1 b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
new file mode 100644
index 0000000..64c6fb3
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.1
@@ -0,0 +1,61 @@
+.\"
+.\" 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.1 1999/02/15 19:13:47 hm Exp $
+.\"
+.\" last edit-date: [Mon Feb 15 20:11:30 1999]
+.\"
+.\"
+.Dd February, 15 1999
+.Dt dtmfdecode 1
+.Sh NAME
+.Nm dtmfdecode
+.Nd decodes DTMF tones from i4b .g711a files
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm dtmfdecode
+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 charcters to stdout.
+.Pp
+
+.Sh EXAMPLES
+The command:
+.Bd -literal -offset indent
+dtmfdecode < beep.g711a
+.Ed
+.Pp
+will print a "1" to stdout.
+
+.Sh STANDARDS
+ITU Recommendations G.711
+
+.Sh AUTHOR
+The
+.Nm
+utility was written by Poul-Henning Kamp, phk@freebsd.org. This man page
+was written by Hellmuth Michaelis, 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..2b53fa6
--- /dev/null
+++ b/usr.sbin/i4b/dtmfdecode/dtmfdecode.c
@@ -0,0 +1,158 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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.1 1999/02/15 19:13:47 hm Exp $
+ *
+ * Extract DTMF signalling from a .g711a file from ISDN4BSD
+ *
+ * A-Law to linear conversion from the sox package.
+ *
+ */
+
+#include <stdio.h>
+#include <math.h>
+
+/*
+ * g711.c
+ *
+ * u-law, A-law and linear PCM conversions.
+ */
+#define SIGN_BIT (0x80) /* Sign bit for a A-law byte. */
+#define QUANT_MASK (0xf) /* Quantization field mask. */
+#define NSEGS (8) /* Number of A-law segments. */
+#define SEG_SHIFT (4) /* Left shift for segment number. */
+#define SEG_MASK (0x70) /* Segment field mask. */
+
+/*
+ * alaw2linear() - Convert an A-law value to 16-bit linear PCM
+ *
+ */
+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);
+}
+
+int flip[256];
+
+double dtmf[8] = {697, 770, 852, 941, 1209, 1336, 1477, 1633};
+double p1[8];
+
+/* This is the Q of the filter (pole radius). must be less than 1.0 */
+#define POLRAD .99
+
+#define P2 (POLRAD*POLRAD)
+
+#define NNN 100
+
+char key [256];
+
+int
+main(int argc, char **argv)
+{
+ int i, j, kk, nn, s, so = 0;
+ double x, a[8], b[8], c[8], d[8], e[8], f[8], g[8], h[8], k[8], l[8], m[8], n[8], y[8];
+
+
+ for (kk = 0; kk < 8; kk++) {
+ y[kk] = g[kk] = k[kk] = 0.0;
+ p1[kk] = (-cos(2 * 3.141592 * dtmf[kk] / 8000.0));
+ }
+
+ for (i=0;i<256;i++) {
+ key[i] = '\0';
+ flip[i] = (i & 1) << 7;
+ flip[i] |= (i & 2) << 5;
+ flip[i] |= (i & 4) << 3;
+ flip[i] |= (i & 8) << 1;
+ flip[i] |= (i & 16) >> 1;
+ flip[i] |= (i & 32) << 3;
+ flip[i] |= (i & 64) << 5;
+ flip[i] |= (i & 128) << 7;
+ }
+
+ 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';
+
+ x = 0.0;
+ nn = 0;
+ while ((i = getchar()) != EOF) {
+ i = flip[i];
+ j = alaw2linear(i);
+
+ x = j / 32768.0;
+ s = 0;
+ for(kk = 0; kk < 8; kk++) {
+ a[kk] = x;
+ h[kk] = g[kk];
+ l[kk] = k[kk];
+
+ b[kk] = a[kk] - l[kk];
+ c[kk] = P2 * b[kk];
+ d[kk] = a[kk] + c[kk];
+ e[kk] = d[kk] - h[kk];
+ f[kk] = p1[kk] * e[kk];
+ g[kk] = f[kk] + d[kk];
+ k[kk] = h[kk] + f[kk];
+ m[kk] = l[kk] + c[kk];
+ n[kk] = a[kk] - m[kk];
+
+ y[kk] += (fabs(n[kk]) - y[kk]) / 20.0;
+ if (y[kk] > .1)
+ s |= 1 << kk;
+ }
+ if (s != so)
+ nn = 0;
+ else
+ nn++;
+ if (nn == NNN) {
+ if (key[s])
+ putchar(key[s]);
+ }
+ so = s;
+ }
+ printf("\n");
+ return (0);
+}
diff --git a/usr.sbin/i4b/isdnd/Makefile b/usr.sbin/i4b/isdnd/Makefile
new file mode 100644
index 0000000..37d3471
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/Makefile
@@ -0,0 +1,34 @@
+# $Id$
+
+PROG = isdnd
+
+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
+
+COPTS += -I${.CURDIR}/../isdnmonitor -I${.CURDIR}/../isdntel -I${.OBJDIR}
+
+# compile debug support
+COPTS += -DDEBUG
+
+# enable rtprio usage
+COPTS += -DUSE_RTPRIO
+
+MAN8 = isdnd.8
+MAN5 = isdnd.rc.5 isdnd.rates.5 isdnd.acct.5
+
+.if !defined(I4B_WITHOUT_CURSES)
+COPTS += -DUSE_CURSES
+DPADD = ${LIBCURSES} ${LIBTERMCAP}
+LDADD = -lcurses -ltermcap
+.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..cf5089e
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/alias.c
@@ -0,0 +1,191 @@
+/*
+ * 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.6 1999/02/14 09:44:55 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:03 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;
+ char buffer[MAXBUFSZ + 1];
+ char number[MAXBUFSZ + 1];
+ char name[MAXBUFSZ + 1];
+ 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..1adc03c
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/config.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 - compile time configuration header file
+ * ---------------------------------------------------
+ *
+ * $Id: config.h,v 1.7 1999/02/14 09:44:55 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:10 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _CONFIG_H_
+#define _CONFIG_H_
+
+/* general values */
+
+#define UMASK 022 /* file creation perm mask */
+#define CFG_ENTRY_MAX 32 /* 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..d34c067
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/controller.c
@@ -0,0 +1,329 @@
+/*
+ * 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 - controller state support routines
+ * ----------------------------------------------
+ *
+ * $Id: controller.c,v 1.12 1999/02/15 16:48:04 hm Exp $
+ *
+ * last edit-date: [Mon Feb 15 16:37:55 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+/*--------------------------------------------------------------------------*
+ * init controller state table entry
+ *--------------------------------------------------------------------------*/
+int
+init_controller_state(int controller, int ctrl_type, int card_type, int tei)
+{
+ if((controller < 0) || (controller >= ncontroller))
+ {
+ log(LL_ERR, "init_controller_state: invalid controller number [%d]!", controller);
+ return(ERROR);
+ }
+
+ /* init controller tab */
+
+ if(ctrl_type == 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;
+ isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
+ isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
+ isdn_ctrl_tab[controller].freechans = MAX_CHANCTRL;
+ isdn_ctrl_tab[controller].tei = tei;
+ DBGL(DL_RCCF, (log(LL_DBG, "init_controller_state: controller %d is %s",
+ controller,
+ name_of_controller(isdn_ctrl_tab[controller].ctrl_type,
+ isdn_ctrl_tab[controller].card_type))));
+ }
+ else
+ {
+ log(LL_ERR, "init_controller_state: unknown card type %d", card_type);
+ return(ERROR);
+ }
+
+ }
+ else if(ctrl_type == 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;
+ isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
+ isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
+ isdn_ctrl_tab[controller].freechans = MAX_CHANCTRL;
+ isdn_ctrl_tab[controller].tei = -1;
+ 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));
+ }
+ else
+ {
+ /* XXX active controller init here !!! */
+
+ log(LL_ERR, "init_controller_state: unknown controller type %d", ctrl_type);
+ return(ERROR);
+ }
+ return(GOOD);
+}
+
+/*--------------------------------------------------------------------------*
+ * 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 < MAX_CHANCTRL)
+ {
+ (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 2 free chans!", controller);
+ 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);
+ }
+
+ switch(channel)
+ {
+ case CHAN_B1:
+ if(isdn_ctrl_tab[controller].stateb1 == CHAN_RUN)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B1 already busy!", controller)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb1 = CHAN_RUN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B1 set to BUSY!", controller)));
+ }
+ break;
+
+ case CHAN_B2:
+ if(isdn_ctrl_tab[controller].stateb2 == CHAN_RUN)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B2 already busy!", controller)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb2 = CHAN_RUN;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_busy: controller [%d] channel B2 set to BUSY!", controller)));
+ }
+ break;
+
+ default:
+ log(LL_ERR, "set_channel_busy: controller [%d], invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ break;
+ }
+ 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);
+ }
+
+ switch(channel)
+ {
+ case CHAN_B1:
+ if(isdn_ctrl_tab[controller].stateb1 == CHAN_IDLE)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B1 already idle!", controller)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb1 = CHAN_IDLE;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B1 set to IDLE!", controller)));
+ }
+ break;
+
+ case CHAN_B2:
+ if(isdn_ctrl_tab[controller].stateb2 == CHAN_IDLE)
+ {
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B2 already idle!", controller)));
+ }
+ else
+ {
+ isdn_ctrl_tab[controller].stateb2 = CHAN_IDLE;
+ DBGL(DL_CNST, (log(LL_DBG, "set_channel_idle: controller [%d] channel B2 set to IDLE!", controller)));
+ }
+ break;
+
+ default:
+ log(LL_ERR, "set_channel_idle: controller [%d], invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ break;
+ }
+ 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);
+ }
+
+ switch(channel)
+ {
+ case CHAN_B1:
+ return(isdn_ctrl_tab[controller].stateb1);
+ break;
+
+ case CHAN_B2:
+ return(isdn_ctrl_tab[controller].stateb2);
+ break;
+
+ default:
+ log(LL_ERR, "ret_channel_state: controller [%d], invalid channel [%d]!", controller, channel);
+ return(ERROR);
+ break;
+ }
+ return(ERROR);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/curses.c b/usr.sbin/i4b/isdnd/curses.c
new file mode 100644
index 0000000..ad7bb95
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/curses.c
@@ -0,0 +1,633 @@
+/*
+ * 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 - curses fullscreen output
+ * -------------------------------------
+ *
+ * $Id: curses.c,v 1.28 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:24 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifdef USE_CURSES
+
+#include "isdnd.h"
+
+#define CHPOS(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
+
+/*---------------------------------------------------------------------------*
+ * 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);
+
+ sprintf(buffer, "----- isdn controller channel state ------------- isdnd %02d.%02d.%d [pid %d] -", VERSION, REL, STEP, (int)getpid());
+
+ 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");
+
+ 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();
+
+ move(uheight+5, 0);
+ addstr("Date Time Typ Information Description");
+
+ 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 - (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'+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':
+ rereadconfig(42);
+ 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:
+ rereadconfig(42);
+ 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 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)
+ sprintf(buffer, "%s", get_alias(cep->real_phone_incoming));
+ else
+ sprintf(buffer, "%s", get_alias(cep->remote_phone_dialout));
+ }
+ else
+ {
+ if(cep->direction == DIR_IN)
+ sprintf(buffer, "%s/%s", cep->name, cep->real_phone_incoming);
+ else
+ sprintf(buffer, "%s/%s", cep->name, cep->remote_phone_dialout);
+ }
+
+ 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)
+ {
+ 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((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ {
+ 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;
+ }
+
+ 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);
+}
+
+#endif
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/dial.c b/usr.sbin/i4b/isdnd/dial.c
new file mode 100644
index 0000000..747a8c7
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/dial.c
@@ -0,0 +1,153 @@
+/*
+ * 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 - dial handling routines
+ * -----------------------------------
+ *
+ * $Id: dial.c,v 1.7 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:30 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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->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, cep->remote_numbers[0].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: only one no, no = %s", cep->remote_phone_dialout)));
+ cep->last_remote_number = 0;
+ return;
+ }
+
+ if(cep->remote_numbers_handling == RNH_FIRST)
+ {
+ strcpy(cep->remote_phone_dialout, cep->remote_numbers[0].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use first, no = %s", cep->remote_phone_dialout)));
+ 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, cep->remote_numbers[i].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use last, no = %s", cep->remote_phone_dialout)));
+ cep->last_remote_number = i;
+ return;
+ }
+ else
+ {
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+
+ strcpy(cep->remote_phone_dialout, cep->remote_numbers[i].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_first_dialno: use next, no = %s", cep->remote_phone_dialout)));
+ cep->last_remote_number = i;
+ return;
+ }
+ }
+
+ if(++i >= cep->remote_numbers_count)
+ i = 0;
+ }
+ strcpy(cep->remote_phone_dialout, 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)));
+ 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, cep->remote_numbers[0].number);
+ DBGL(DL_DIAL, (log(LL_DBG, "select_next_dialno: only one no, no = %s", cep->remote_phone_dialout)));
+ 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, 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..2d95e21
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/exec.c
@@ -0,0 +1,280 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * exec.h - supplemental program/script execution
+ * ----------------------------------------------
+ *
+ * $Id: exec.c,v 1.11 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:35 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.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, waitpid: %s", strerror(errno));
+ do_exit(1);
+ }
+ 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+1];
+ pid_t pid;
+ int a;
+
+ sprintf(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));
+ do_exit(1);
+ case 0: /* child */
+ break;
+ default: /* parent */
+ return(pid);
+ }
+
+ /* this is the child now */
+
+ 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);
+ sprintf(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);
+
+ sprintf(devicename, "/dev/i4b%s%d", 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)
+ argv[4] = cep->local_phone_incoming;
+
+ /* if source telephone number avail, add it as argument */
+
+ if(*cep->real_phone_incoming)
+ argv[6] = cep->real_phone_incoming;
+
+ 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;
+ }
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/fsm.c b/usr.sbin/i4b/isdnd/fsm.c
new file mode 100644
index 0000000..463f41a
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/fsm.c
@@ -0,0 +1,446 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * FSM for isdnd
+ * -------------
+ *
+ * $Id: fsm.c,v 1.16 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:10:41 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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)
+ {
+ /* 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)
+ {
+ /* 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_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_DRQ, ST_WAITDISCI}, {F_NcNa,ST_WAITDISCI}, {F_ill, ST_ILL}, {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, "FSM: event > N_EVENTS");
+ do_exit(1);
+ }
+
+ currstate = cep->state;
+
+ if(currstate > N_STATES)
+ {
+ log(LL_ERR, "FSM: currstate > N_STATES");
+ do_exit(1);
+ }
+
+ newstate = state_tab[event][currstate].newstate;
+
+ if(newstate > N_STATES)
+ {
+ log(LL_ERR, "FSM: newstate > N_STATES");
+ do_exit(1);
+ }
+
+ 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/isdnd.8 b/usr.sbin/i4b/isdnd/isdnd.8
new file mode 100644
index 0000000..950614c
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.8
@@ -0,0 +1,417 @@
+.\"
+.\" 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.8,v 1.24 1999/02/23 16:25:49 hm Exp $
+.\"
+.\" last edit-date: [Tue Feb 23 16:46:26 1999]
+.\"
+.Dd February 23, 1999
+.Dt isdnd 8
+.Sh NAME
+.Nm isdnd
+.Nd isdn4bsd ISDN connection management daemon
+.Sh SYNOPSIS
+.Nm isdnd
+.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
+.Nm Isdnd
+is the isdn4bsd package demon 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 isdnd
+instead of the default file
+.Li /etc/isdn/isdnd.rc .
+.It Fl d
+If debugging support is compiled into
+.Nm isdnd
+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.
+.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 isdnd
+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
+daemon 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.
+.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 isdnd
+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 isdnd
+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 isdnd
+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
+.Pp
+.Sh INTERACTION WITH THE KERNEL
+.Nm Isdnd
+communicates with the kernel part of isdn4bsd by receiving status and
+event messages (
+.Xr read 2
+from device /dev/i4b ) and by transmitting commands and responses (
+.Xr ioctl 2
+from device /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 (ioctl's) 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
+daemon.
+.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 site 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 site 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
+
+.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.
+
+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 isdnd :
+.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 -compact
+.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.
+.Nm Isdnd
+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 isdnd
+with reasonable debugging messages enabled, full-screen mode of operation,
+full-screen display redirected to /dev/ttyv03 and using a termcap entry
+for vt100 on this display.
+
+.Sh DIAGNOSTICS
+Exit status is 0 on success, 1 on error.
+.Pp
+
+.Sh SEE ALSO
+.Xr syslogd 8 ,
+.Xr isdntrace 8 ,
+.Xr isdntel 8 ,
+.Xr isdnd.rc 5 ,
+.Xr isdnd.rates 5 ,
+.Xr i4bisppp 4 ,
+.Xr i4bipr 4
+
+.Sh BUGS
+Still one or more left.
+
+.Sh AUTHOR
+The
+.Nm
+daemon and this manual page were written by Hellmuth Michaelis. He can
+be contacted at hm@kts.org or hm@hcs.de.
diff --git a/usr.sbin/i4b/isdnd/isdnd.acct.5 b/usr.sbin/i4b/isdnd/isdnd.acct.5
new file mode 100644
index 0000000..34e8cff
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.acct.5
@@ -0,0 +1,108 @@
+.\"
+.\" 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.8 1999/02/15 16:48:04 hm Exp $
+.\"
+.\" last edit-date: [Mon Feb 15 16:54:23 1999]
+.\"
+.Dd September 11, 1998
+.Dt isdnd.acct 5
+.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.
+
+.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 8 ,
+.Xr isdnd.rc 5
+
+.Sh AUTHOR
+The
+.Xr isdnd 8
+daemon and this manual page were written by Hellmuth Michaelis.
+He can be contacted at hm@kts.org or hm@hcs.de.
+
diff --git a/usr.sbin/i4b/isdnd/isdnd.h b/usr.sbin/i4b/isdnd/isdnd.h
new file mode 100644
index 0000000..e4033ff
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.h
@@ -0,0 +1,754 @@
+/*
+ * 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 - main header file
+ * -----------------------------
+ *
+ * $Id: isdnd.h,v 1.59 1999/02/15 15:02:58 hm Exp $
+ *
+ * last edit-date: [Mon Feb 15 15:42:37 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#ifndef _ISDND_H_
+#define _ISDND_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <strings.h>
+#include <ctype.h>
+#include <syslog.h>
+#include <regex.h>
+
+#ifdef USE_CURSES
+#include <curses.h>
+#endif
+
+#include <fcntl.h>
+#include <errno.h>
+#include <signal.h>
+
+#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 <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 */
+
+#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 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 */
+
+/*---------------------------------------------------------------------------*
+ * 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 */
+};
+
+/*---------------------------------------------------------------------------*
+ * 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]; /* remote number to dial */
+ 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 */
+} 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 */
+#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 */
+
+ char local_phone_dialout[TELNO_MAX]; /* our number to tell remote*/
+ char local_phone_incoming[TELNO_MAX]; /* answer calls for this local number */
+
+#define MAX_INCOMING 8
+ int incoming_numbers_count; /* number of incoming allowed numbers */
+ 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 idle_time_in; /* max idle time incoming calls */
+ int idle_time_out; /* max idle time outgoing calls */
+
+ msg_shorthold_algorithm_t 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
+
+/*===========================================================================*/
+/*============ 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 */
+
+ int 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 */
+
+ char real_phone_incoming[TELNO_MAX]; /* real remote telno in case of wildcard */
+
+ int last_remote_number; /* index of last used dialout number*/
+
+ char remote_phone_dialout[TELNO_MAX]; /* 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];
+} cfg_entry_t;
+
+/*---------------------------------------------------------------------------*
+ * this struct describes state of controller with 2 b channels
+ *---------------------------------------------------------------------------*/
+typedef struct isdn_ctrl_state {
+ int ctrl_type; /* type: active/passive */
+ int card_type; /* manufacturer (CARD_XXXX) */
+ int state; /* controller state */
+#define CTRL_DOWN 0 /* controller inoparable */
+#define CTRL_UP 1 /* controller may be used */
+ int stateb1; /* B-channel 1 */
+ int stateb2; /* B-channel 2 */
+#define CHAN_IDLE 0 /* channel is free for usage */
+#define CHAN_RUN 1 /* channel is occupied */
+ int freechans; /* number of unused channels */
+#define MAX_CHANCTRL 2 /* free channels per controller */
+ int tei; /* tei or -1 if invalid */
+} 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 {
+ 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 *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 */
+
+#else /* !MAIN */
+
+int isdnfd;
+
+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];
+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;
+
+#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 hangup_channel ( int channel );
+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_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 if_up(cfg_entry_t *cep);
+void if_down(cfg_entry_t *cep);
+void init_controller ( 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_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 );
+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, int cause );
+int sendm_disconnect_req ( cfg_entry_t *cep, int 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 );
+
+/* 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);
+
+/* controller.c */
+
+int init_controller_state(int controller, int ctrl_type, int card_type, int tei);
+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);
+
+#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..4a8cc94
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rates.5
@@ -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: isdnd.rates.5,v 1.7 1999/02/14 09:44:56 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:11:05 1999]
+.\"
+.Dd September 11, 1998
+.Dt isdnd.rates 5
+.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 -filled -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 -filled -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.
+.Pp
+
+.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.
+
+.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 8 ,
+.Xr isdnd.rc 5
+
+.Sh AUTHOR
+The rates subsystem for the
+.Xr isdnd 8
+daemon to which
+.Nm
+belongs was designed and written by Gary Jennejohn.
+.Pp
+The
+.Xr isdnd 8
+daemon and this manual page were written by Hellmuth Michaelis.
+He can be reached at hm@kts.org or hm@hcs.de.
+
diff --git a/usr.sbin/i4b/isdnd/isdnd.rc.5 b/usr.sbin/i4b/isdnd/isdnd.rc.5
new file mode 100644
index 0000000..4e21b46
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/isdnd.rc.5
@@ -0,0 +1,723 @@
+.\"
+.\" 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.rc.5,v 1.31 1999/02/25 19:16:53 hm Exp $
+.\"
+.\" last edit-date: [Thu Feb 25 20:15:38 1999]
+.\"
+.Dd February, 23, 1999
+.Dt isdnd.rc 5
+.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 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 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 -compact
+
+.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 -compact
+
+.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 .
+If this keyword is omitted the system default 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 fullscreen mode, if this parameter is set to
+.Em on ,
+ring the bell when connecting or disconnecting a call.
+
+.It Li isdntime
+If this parameter is set to
+.Em on ,
+date/time information from the exchange (if provided) is written to the
+logfile. The default is off. (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 -offset
+.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 -offset
+.It Ar fullcmd
+.It Ar restrictedcmd
+.It Ar channelstate
+.It Ar logevents
+.It Ar callin
+.It Ar callout
+.El
+
+.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 rtprio
+Specifies the realtime 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 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 -compact
+
+.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 -offset
+.It Ar hdlc
+HDLC framing.
+.It Ar raw
+No framing at all (used for telephony).
+.El
+
+.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 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 -offset
+.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, hangup 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 -offset
+.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. (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 -offset
+.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 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 hangup 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 -offset
+.It Ar fix-unit-size
+idle algorithm which assumes fixed sized changing 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.
+(optional)
+
+.It Li idletime-incoming
+The time in seconds an incoming connection must be idle before hanging up.
+(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
+.Em timeout()
+kernel subroutine to delay the transmittion of the first packet after a
+successfull 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
+.Em timeout()
+kernel subroutine to delay the transmittion of the first packet after a
+successfull 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
+userland 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 ipr interfaces.
+
+.It Li name
+Defines a symbolic name for this configuration entry. It's 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 ratetype
+The rate entry used from the rates file. (optional)
+.br
+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 -offset
+.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-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 unitlength
+The length of a charging unit in seconds. This is used in conjunction with
+the idletime to decide when to hangup a connection. (optional)
+
+.It Li unitlengthsrc
+This keyword is used to specify from which source
+.Xr isdnd 8
+takes the unitlength for shorthold mode. The currently configurable values are:
+.Pp
+.Bl -tag -width Ds -compact -offset
+.It Ar none
+Then unitlength is not specified anywhere.
+.It Ar cmdl
+Use the unitlength specified on the commandline.
+.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 userland interface which is used for interfacing ISDN B channel
+data to the userland. The keyword is mandatory.
+This keyword accepts the following parameters:
+.Pp
+.Bl -tag -width Ds -compact -offset
+.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.
+.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 connectprog
+specifies a program run everytime after a connection is established and
+address negotiation is complete (i.e.: the connection is useable).
+.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 disconnectprog
+specifies a program run everytime 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)
+
+.El
+.El
+
+.Sh IDLETIME CALCULATION AND SHORTHOLD MODE
+.Bl -tag -width incoming calls -compact
+.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 -compact
+.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
+
+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
+
+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
+
+.Pp
+
+.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.
+
+.Sh SEE ALSO
+.Xr isdnd 8
+.Xr isdnmonitor 8
+.Xr regex 3
+.Xr re_format 7
+
+.Sh AUTHOR
+The
+.Xr isdnd 8
+daemon and this manual page were written by Hellmuth Michaelis.
+He can be reached at hm@kts.org.
+.Pp
+Additions to this manual page by Barry Scott (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..cdd4734
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/log.c
@@ -0,0 +1,242 @@
+/*
+ * 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 - logging routines
+ * -----------------------------
+ *
+ * $Id: log.c,v 1.15 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:11:18 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <regex.h>
+
+#include <machine/i4b_ioctl.h>
+
+#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}, /* normal but significant condition, daemon*/
+ {"CHD", LOG_INFO}, /* informational, call handling */
+ {"DBG", LOG_DEBUG} /* debug messages */
+};
+
+/*---------------------------------------------------------------------------*
+ * 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];
+
+ sprintf(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)
+ {
+ 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,
+ COLS-((strlen(dp))+(strlen(logtab[what].text))+2), buffer);
+ wrefresh(lower_w);
+ }
+#endif
+
+#ifdef I4B_EXTERNAL_MONITOR
+ 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..eb6ae5c
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/main.c
@@ -0,0 +1,706 @@
+/*
+ * 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 - main program entry
+ * -------------------------------
+ *
+ * $Id: main.c,v 1.34 1999/02/23 16:25:49 hm Exp $
+ *
+ * last edit-date: [Tue Feb 23 16:47:33 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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\n", DL_RCCF);
+ 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");
+ fprintf(stderr, " -m inhibit network/local monitoring\n");
+ 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
+
+ while ((i = getopt(argc, argv, "bmc:d:fFlL:Pr:s:t:u:?")) != EOF)
+ {
+ switch (i)
+ {
+ case 'b':
+ do_bell = 1;
+ break;
+
+ case 'm':
+ inhibit_monitor = 1;
+ break;
+
+ 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':
+ strcpy(logfile, optarg);
+ 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);
+ }
+
+ /* 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 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
+
+ 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);
+}
+
+/*---------------------------------------------------------------------------*
+ * 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_WRN, "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();
+
+ 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;
+
+ default:
+ log(LL_WRN, "ERROR, unknown message received from /dev/isdn (0x%x)", 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();
+
+ entrycount = -1;
+ nentries = 0;
+
+ /* read runtime configuration file and configure ourselves */
+
+ configure(configfile, 1);
+
+ if(config_error_flag)
+ {
+ log(LL_ERR, "there were %d error(s) in the configuration file, terminating!", config_error_flag);
+ unlink(PIDFILE);
+ exit(1);
+ }
+
+ if(aliasing)
+ {
+ /* reread alias database */
+ free_aliases();
+ init_alias(aliasfile);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * re-open the log/acct files on SIGUSR1
+ *---------------------------------------------------------------------------*/
+void
+reopenfiles(int dummy)
+{
+ if(useacctfile)
+ {
+ fflush(acctfp);
+ fclose(acctfp);
+
+ 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);
+ }
+
+ if(uselogfile)
+ {
+ finish_log();
+
+ 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);
+ }
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdnd/monitor.c b/usr.sbin/i4b/isdnd/monitor.c
new file mode 100644
index 0000000..53beb43
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/monitor.c
@@ -0,0 +1,811 @@
+/*
+ * Copyright (c) 1998 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
+ * ------------------------------------------
+ *
+ * $Id: monitor.c,v 1.8 1999/02/15 16:48:04 hm Exp $
+ *
+ * last edit-date: [Mon Feb 15 16:42:18 1999]
+ *
+ * -mh created
+ *
+ *---------------------------------------------------------------------------*/
+
+#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 "vararray.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
+
+VARA_DECL(struct monitor_rights) rights = VARA_INITIALIZER;
+#define INITIAL_RIGHTS_ALLOC 10 /* most users will have one or two entries */
+
+static int local_rights = -1; /* index of entry for local socket, -1 if none */
+
+/* for each active monitor connection we have one of this: */
+struct monitor_connection {
+ int sock; /* socket for this connection */
+ int rights; /* active rights for this connection */
+ int events; /* bitmask of events client is interested in */
+};
+static VARA_DECL(struct monitor_connection) connections = VARA_INITIALIZER;
+#define INITIAL_CONNECTIONS_ALLOC 30 /* high guess */
+
+/* derive channel number from config pointer */
+#define CHNO(cfgp) (((cfgp)->isdncontrollerused*2) + (cfgp)->isdnchannelused)
+
+/* local prototypes */
+static int cmp_rights(const void *a, const void *b);
+static int monitor_command(int con_index, int fd, int rights);
+static void cmd_dump_rights(int fd, int rights, BYTE *cmd);
+static void cmd_dump_mcons(int fd, int rights, BYTE *cmd);
+static void cmd_reread_cfg(int fd, int rights, BYTE *cmd);
+static void cmd_hangup(int fd, int rights, BYTE *cmd);
+static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes);
+static int anybody(int mask);
+
+/*
+ * 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 int cur_add_entry = -1;
+
+/*
+ * Initialize the monitor server module. This affects only active
+ * connections, the access rights are not modified here!
+ */
+void monitor_init()
+{
+ accepted = 0;
+ VARA_EMPTY(connections);
+}
+
+/*
+ * Prepare for exit
+ */
+void monitor_exit()
+{
+ int i;
+
+ /* Close all open connections. */
+ VARA_FOREACH(connections, i)
+ close(VARA_AT(connections, i).sock);
+
+ /* Remove their descriptions */
+ VARA_EMPTY(connections);
+}
+
+/*
+ * Initialize access rights. No active connections are affected!
+ */
+void monitor_clear_rights()
+{
+ VARA_EMPTY(rights);
+ cur_add_entry = -1;
+}
+
+/*
+ * 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)
+{
+ int i;
+ 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 (VARA_VALID(rights, local_rights))
+ 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 */
+ 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 */
+ VARA_FOREACH(rights, i)
+ if (VARA_AT(rights, i).mask == r.mask &&
+ VARA_AT(rights, i).net == r.net &&
+ VARA_AT(rights, i).local == r.local)
+ return I4BMAR_DUP;
+#endif
+ }
+ r.rights = 0;
+
+ /* entry ok, add it to the collection */
+ cur_add_entry = i = VARA_NUM(rights);
+ VARA_ADD_AT(rights, i, struct monitor_rights, INITIAL_RIGHTS_ALLOC);
+ memcpy(&VARA_AT(rights, i), &r, sizeof r);
+ if (r.local)
+ local_rights = i;
+
+ 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 < 0) return; /* noone under construction */
+
+ VARA_AT(rights, 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()
+{
+ int i;
+
+ /* no more rights may be added to the current entry */
+ cur_add_entry = -1;
+
+ /* sort the rights array */
+ qsort(VARA_PTR(rights), VARA_NUM(rights), sizeof(struct monitor_rights), cmp_rights);
+
+ /* now the local entry may have moved, update its index */
+ if (VARA_VALID(rights, local_rights)) {
+ local_rights = -1;
+ VARA_FOREACH(rights, i) {
+ if (VARA_AT(rights, i).local) {
+ local_rights = i;
+ break;
+ }
+ }
+ }
+}
+
+/* comparator for rights */
+static int cmp_rights(const void *a, const void *b)
+{
+ u_int32_t mask;
+ struct monitor_rights const * pa = (struct monitor_rights const*)a;
+ struct monitor_rights const * pb = (struct monitor_rights const*)b;
+
+ /* 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 = socket(AF_INET, SOCK_STREAM, 0);
+ if (remotesockfd == -1) {
+ log(LL_ERR, "could not create remote monitor socket, errno = %d", errno);
+ exit(1);
+ }
+ val = 1;
+ if (setsockopt(remotesockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val)) {
+ log(LL_ERR, "could not setsockopt, errno = %d", errno);
+ exit(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_ERR, "could not bind remote monitor socket to port %d, errno = %d", portno, errno);
+ exit(1);
+ }
+ if (listen(remotesockfd, 0)) {
+ log(LL_ERR, "could not listen on monitor socket, errno = %d", errno);
+ exit(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()
+{
+ int s;
+ struct sockaddr_un sa;
+
+ /* check for a local entry */
+ if (!VARA_VALID(rights, local_rights))
+ return -1;
+
+ /* create and setup socket */
+ s = socket(AF_LOCAL, SOCK_STREAM, 0);
+ if (s == -1) {
+ log(LL_ERR, "could not create local monitor socket, errno = %d", errno);
+ exit(1);
+ }
+ unlink(VARA_AT(rights, local_rights).name);
+ memset(&sa, 0, sizeof sa);
+ sa.sun_len = sizeof sa;
+ sa.sun_family = AF_LOCAL;
+ strcpy(sa.sun_path, VARA_AT(rights, local_rights).name);
+ if (bind(s, (struct sockaddr *)&sa, sizeof sa)) {
+ log(LL_ERR, "could not bind local monitor socket [%s], errno = %d", VARA_AT(rights, local_rights).name, errno);
+ exit(1);
+ }
+ if (listen(s, 0)) {
+ log(LL_ERR, "could not listen on local monitor socket, errno = %d", errno);
+ exit(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)
+{
+ int i;
+
+ VARA_FOREACH(connections, i) {
+ int fd = VARA_AT(connections, i).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)
+{
+ int i;
+
+ VARA_FOREACH(connections, i) {
+ int fd = VARA_AT(connections, i).sock;
+ if (FD_ISSET(fd, selset)) {
+ /* handle command from this client */
+ if (monitor_command(i, fd, VARA_AT(connections, i).rights) != 0) {
+ /* broken or closed connection */
+ log(LL_DBG, "monitor connection #%d closed", i);
+ VARA_REMOVEAT(connections, i);
+ i--;
+ }
+ }
+ }
+
+ /* all connections gone? */
+ if (VARA_NUM(connections) == 0)
+ 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;
+#ifndef I4B_NOTCPIP_MONITOR
+ struct sockaddr_in ia;
+ u_int32_t ha = 0;
+#endif
+ struct sockaddr_un ua;
+ BYTE idata[I4B_MON_IDATA_SIZE];
+ int fd = -1, s, i, r_mask;
+ char source[FILENAME_MAX];
+
+ /* accept the connection */
+ if (is_local) {
+ s = sizeof ua;
+ fd = accept(sockfd, (struct sockaddr *)&ua, &s);
+ strcpy(source, "local connection");
+#ifndef I4B_NOTCPIP_MONITOR
+ } else {
+ s = sizeof ia;
+ fd = accept(sockfd, (struct sockaddr *)&ia, &s);
+ snprintf(source, sizeof source, "tcp/ip connection from %s\n",
+ 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;
+ VARA_FOREACH(rights, i) {
+ struct monitor_rights *r = &VARA_AT(rights, i);
+ if (r->local) {
+ if (is_local) {
+ r_mask = r->rights;
+ break;
+ }
+#ifndef I4B_NOTCPIP_MONITOR
+ } else {
+ if ((ha & r->mask) == r->net) {
+ r_mask = r->rights;
+ break;
+ }
+#endif
+ }
+ }
+
+ if (r_mask == 0) {
+ /* no rights - go away */
+ log(LL_DBG, "monitor access denied: %s", source);
+ close(fd);
+ return;
+ }
+
+ accepted = 1;
+ i = VARA_NUM(connections);
+ VARA_ADD_AT(connections, i, struct monitor_connection, INITIAL_CONNECTIONS_ALLOC);
+ con = &VARA_AT(connections, i);
+ memset(con, 0, sizeof *con);
+ con->sock = fd;
+ con->rights = r_mask;
+ log(LL_DBG, "monitor access granted, rights = %x, #%d, %s",
+ r_mask, i, source);
+
+ /* 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_4B(idata, I4B_MON_IDATA_CLACCESS, r_mask);
+ write(fd, idata, sizeof idata);
+
+ for (i = 0; i < ncontroller; i++) {
+ BYTE 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);
+ write(fd, ictrl, sizeof ictrl);
+ }
+}
+
+/* dump all monitor rights */
+static void cmd_dump_rights(int fd, int r_mask, BYTE *cmd)
+{
+ int i;
+ BYTE drini[I4B_MON_DRINI_SIZE];
+ BYTE dr[I4B_MON_DR_SIZE];
+
+ I4B_PREP_EVNT(drini, I4B_MON_DRINI_CODE);
+ I4B_PUT_2B(drini, I4B_MON_DRINI_COUNT, VARA_NUM(rights));
+ write(fd, drini, sizeof drini);
+
+ VARA_FOREACH(rights, i) {
+ I4B_PREP_EVNT(dr, I4B_MON_DR_CODE);
+ I4B_PUT_4B(dr, I4B_MON_DR_RIGHTS, VARA_AT(rights, i).rights);
+ I4B_PUT_4B(dr, I4B_MON_DR_NET, VARA_AT(rights, i).net);
+ I4B_PUT_4B(dr, I4B_MON_DR_MASK, VARA_AT(rights, i).mask);
+ I4B_PUT_1B(dr, I4B_MON_DR_LOCAL, VARA_AT(rights, i).local);
+ write(fd, dr, sizeof dr);
+ }
+}
+
+/* rescan config file */
+static void cmd_reread_cfg(int fd, int rights, BYTE *cmd)
+{
+ rereadconfig(42);
+}
+
+/* drop one connection */
+static void cmd_hangup(int fd, int rights, BYTE *cmd)
+{
+ int channel = I4B_GET_4B(cmd, I4B_MON_HANGUP_CHANNEL);
+ hangup_channel(channel);
+}
+
+/* dump all active monitor connections */
+static void cmd_dump_mcons(int fd, int rights, BYTE *cmd)
+{
+ int i;
+ BYTE dcini[I4B_MON_DCINI_SIZE];
+
+ I4B_PREP_EVNT(dcini, I4B_MON_DCINI_CODE);
+ I4B_PUT_2B(dcini, I4B_MON_DCINI_COUNT, VARA_NUM(connections));
+ write(fd, dcini, sizeof dcini);
+
+ VARA_FOREACH(connections, i) {
+#ifndef I4B_NOTCPIP_MONITOR
+ int namelen;
+ struct sockaddr_in name;
+#endif
+ BYTE dc[I4B_MON_DC_SIZE];
+
+ I4B_PREP_EVNT(dc, I4B_MON_DC_CODE);
+ I4B_PUT_4B(dc, I4B_MON_DC_RIGHTS, VARA_AT(connections, i).rights);
+#ifndef I4B_NOTCPIP_MONITOR
+ namelen = sizeof name;
+ if (getpeername(VARA_AT(connections, i).sock, (struct sockaddr*)&name, &namelen) == 0)
+ memcpy(dc+I4B_MON_DC_WHO, &name.sin_addr, sizeof name.sin_addr);
+#endif
+ write(fd, dc, sizeof dc);
+ }
+}
+
+/*
+ * 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(int con_index, 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, BYTE *cmd);
+ 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) {
+ /* 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)
+ return 0; /* errh? something must be wrong... */
+ bytes = I4B_GET_2B(cmd, I4B_MON_CMD_LEN);
+ if (bytes >= sizeof cmd) {
+ close(fd);
+ log(LL_ERR, "garbage on monitor connection #%d, closing it", con_index);
+ return 1;
+ }
+ /* now we know the size, it fits, so lets read it! */
+ if (read(fd, cmd, bytes) <= 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) {
+ /*
+ 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);
+ VARA_AT(connections, con_index).events = events & rights;
+ return 0;
+ }
+
+ if (code < 0 || code >= NUMCMD) {
+ log(LL_ERR, "illegal command from client #%d: code = %d\n",
+ con_index, 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);
+
+ 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)
+{
+ int i;
+
+ VARA_FOREACH(connections, i)
+ if ((VARA_AT(connections, i).events & mask) == mask)
+ return 1;
+
+ return 0;
+}
+
+/*
+ * Send an event to every connection interested in this kind of
+ * event
+ */
+static void monitor_broadcast(int mask, const BYTE *pkt, size_t bytes)
+{
+ int i;
+
+ VARA_FOREACH(connections, i) {
+ if ((VARA_AT(connections, i).events & mask) == mask) {
+ int fd = VARA_AT(connections, i).sock;
+ write(fd, pkt, bytes);
+ }
+ }
+}
+
+/*
+ * Post a logfile event
+ */
+void monitor_evnt_log(int prio, const char * what, const char * msg)
+{
+ BYTE 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 chno = CHNO(cep);
+ int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+ time_t now;
+ BYTE evnt[I4B_MON_CHRG_SIZE];
+
+ 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_CHANNEL, chno);
+ 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)
+{
+ BYTE evnt[I4B_MON_CONNECT_SIZE];
+ char devname[I4B_MAX_MON_STRING];
+ int chno = CHNO(cep);
+ int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+ time_t now;
+
+ 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_CHANNEL, chno);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_CFGNAME, cep->name);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_DEVNAME, devname);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_REMPHONE, cep->real_phone_incoming);
+ I4B_PUT_STR(evnt, I4B_MON_CONNECT_LOCPHONE, cep->remote_phone_dialout);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*
+ * Post a disconnect event
+ */
+void monitor_evnt_disconnect(cfg_entry_t *cep)
+{
+ BYTE evnt[I4B_MON_DISCONNECT_SIZE];
+ int chno = CHNO(cep);
+ int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+ time_t now;
+
+ 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_CHANNEL, chno);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+/*
+ * Post an up/down event
+ */
+void monitor_evnt_updown(cfg_entry_t *cep, int up)
+{
+ BYTE evnt[I4B_MON_UPDOWN_SIZE];
+ int chno = CHNO(cep);
+ int mask = (cep->direction == DIR_IN) ? I4B_CA_EVNT_CALLIN : I4B_CA_EVNT_CALLOUT;
+ time_t now;
+
+ 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_CHANNEL, chno);
+ I4B_PUT_4B(evnt, I4B_MON_UPDOWN_ISUP, up);
+
+ monitor_broadcast(mask, evnt, sizeof evnt);
+}
+
+void hangup_channel(int channel)
+{
+ int i;
+ cfg_entry_t * cep = NULL;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if(isdn_ctrl_tab[i].state != CTRL_UP)
+ continue;
+ if(isdn_ctrl_tab[i].stateb1 != CHAN_IDLE)
+ {
+ cep = get_cep_by_cc(i, 0);
+ if (cep != NULL && CHNO(cep) == channel)
+ goto found;
+ }
+ if(isdn_ctrl_tab[i].stateb2 != CHAN_IDLE)
+ {
+ cep = get_cep_by_cc(i, 1);
+ if (cep != NULL && CHNO(cep) == channel)
+ goto found;
+ }
+ }
+ /* not found */
+ return;
+
+found:
+ cep->hangup = 1;
+ return;
+}
+
+#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..b24ac24
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/msghdl.c
@@ -0,0 +1,985 @@
+/*
+ * 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 - message from kernel handling routines
+ * --------------------------------------------------
+ *
+ * $Id: msghdl.c,v 1.57 1999/02/25 12:45:41 hm Exp $
+ *
+ * last edit-date: [Thu Feb 25 13:43:46 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.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)
+
+ 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);
+ 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(aliasing)
+ {
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+ }
+
+ 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);
+ 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);
+ 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,
+ (CAUSET_I4B << 8) | CAUSE_I4B_NORMAL);
+ /* no state change */
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s callback: 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_NORMAL);
+ 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;
+ }
+#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;
+
+ 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;
+
+ /* 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)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused);
+ }
+ else
+ {
+ log(LL_CHD, "%05d %s incoming call active (ctl %d, ch %d)",
+ cep->cdid, cep->name,
+ cep->isdncontrollerused, cep->isdnchannelused);
+ }
+
+#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)
+{
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_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)));
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming TEIASG_IND message
+ *---------------------------------------------------------------------------*/
+void
+msg_teiasg_ind(msg_teiasg_ind_t *mt)
+{
+#ifdef USE_CURSES
+ if(do_fullscreen)
+ display_tei(mt->controller, mt->tei);
+#endif
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_teiasg_ind: unit %d, tei = %d",
+ mt->controller, mt->tei)));
+}
+
+/*---------------------------------------------------------------------------*
+ * handle incoming L12STAT_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
+
+ DBGL(DL_CNST, (log(LL_DBG, "msg_pdeact_ind: unit %d, persistent deactivation", ctrl)));
+
+ 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->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;
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * 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;
+
+ if((set_channel_idle(cep->saved_call.controller, cep->saved_call.channel)) == ERROR)
+ log(LL_ERR, "msg_disconnect_ind: set_channel_idle failed!");
+
+ 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 */
+
+ if((set_channel_idle(cep->isdncontrollerused, cep->isdnchannelused)) == ERROR)
+ log(LL_ERR, "msg_disconnect_ind: set_channel_idle failed!");
+
+ incr_free_channels(cep->isdncontrollerused);
+
+ cep->connect_time = 0;
+
+ 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->cdid = get_cdid()) == 0)
+ {
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_dialout: get_cdid() returned 0!")));
+ return;
+ }
+
+ cep->charge = 0;
+ cep->last_charge = 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 NOTDEF
+ else
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_accounting: %s%d, ioutb=%d, iinb=%d, outb=%d, inb=%d, outbps=%d, inbps=%d",
+ bdrivername(mp->driver), mp->driver_unit,
+ mp->ioutbytes, mp->iinbytes,
+ mp->outbytes, mp->inbytes,
+ mp->outbps, mp->inbps)));
+#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);
+ do_exit(1);
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "msg_charging: %d unit(s) (%s)",
+ mp->units, cttab[mp->units_type])));
+
+ switch(mp->units_type)
+ {
+ case CHARGE_AOCD:
+ cep->charge = mp->units;
+
+ if((cep->unitlengthsrc == ULSRC_DYN) &&
+ (cep->charge != cep->last_charge))
+ {
+ cep->last_charge = cep->charge;
+ handle_charge(cep);
+ }
+ break;
+
+ case CHARGE_AOCE:
+ cep->charge = mp->units;
+ break;
+
+ case CHARGE_CALC:
+ cep->charge = mp->units;
+#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);
+}
+
+/*---------------------------------------------------------------------------*
+ * 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));
+ do_exit(1);
+ }
+
+ 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.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);
+ strcpy(mcr.src_telno, cep->local_phone_dialout);
+
+ 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));
+ do_exit(1);
+ }
+
+ 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) : cep->local_phone_dialout,
+ aliasing ? get_alias(cep->remote_phone_dialout) : cep->remote_phone_dialout);
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "connect response" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_connect_resp(cfg_entry_t *cep, int cdid, int response, int cause)
+{
+ msg_connect_resp_t mcr;
+ int ret;
+
+ mcr.cdid = cdid;
+
+ mcr.response = response;
+
+ if(response == SETUP_RESP_REJECT)
+ {
+ mcr.cause = cause;
+ }
+ else if(response == SETUP_RESP_ACCEPT)
+ {
+ cep->direction = DIR_IN;
+
+ mcr.txdelay = cep->isdntxdelin;
+
+ mcr.bprot = cep->b1protocol;
+
+ mcr.driver = cep->usrdevicename;
+ mcr.driver_unit = cep->usrdeviceunit;
+
+ mcr.max_idle_time = cep->idle_time_in;
+ }
+
+ if((ret = ioctl(isdnfd, I4B_CONNECT_RESP, &mcr)) < 0)
+ {
+ log(LL_ERR, "sendm_connect_resp: ioctl I4B_CONNECT_RESP failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "sendm_connect_resp: sent CONNECT_RESP")));
+
+ return(ret);
+}
+
+/*---------------------------------------------------------------------------*
+ * send message "disconnect request" to kernel
+ *---------------------------------------------------------------------------*/
+int
+sendm_disconnect_req(cfg_entry_t *cep, int cause)
+{
+ msg_discon_req_t mcr;
+ int ret;
+
+ 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));
+ do_exit(1);
+ }
+ 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));
+ do_exit(1);
+ }
+ 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..f986116
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pathnames.h
@@ -0,0 +1,58 @@
+/*
+ * 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.7 1999/02/14 19:51:01 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:11:43 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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 LIBDIR "/usr/local/lib/isdn"
+
+#define LOG_FILE_DEF "/var/log/isdnd.log"
+#ifdef __bsdi__
+#define ACCT_FILE_DEF "/var/log/isdnd.acct"
+#else
+#define ACCT_FILE_DEF "/var/account/isdnd.acct"
+#endif
+
+#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..31f928d
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/pcause.c
@@ -0,0 +1,227 @@
+/*
+ * 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.10 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:11:49 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];
+
+ sprintf(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",
+ "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..7d8c2c4
--- /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.7 1999/02/14 09:44:56 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:11:54 1999]
+ *
+ * -hm debugging processhandling
+ *
+ *---------------------------------------------------------------------------*/
+
+#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..5b7d8f8
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rates.c
@@ -0,0 +1,507 @@
+/*
+ * 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.9 1999/02/15 09:55:47 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:12:01 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[1024];
+ 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)
+ {
+ sprintf(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
+ {
+ sprintf(error, "rates: invalid rate type %c%c%c in line %d", *bp, *(bp+1), *(bp+2), line);
+ goto rate_error;
+ }
+ if (rateindx >= NRATES)
+ {
+ sprintf(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
+ {
+ sprintf(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)
+ {
+ sprintf(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)
+ {
+ sprintf(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
+ {
+ sprintf(error, "rates: start_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ sprintf(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
+ {
+ sprintf(error, "rates: start_min error in line %d", line);
+ goto rate_error;
+ }
+
+ rt->start_time = hour*60 + min;
+
+ /* minus */
+
+ if(*bp == '-')
+ {
+ bp++;
+ }
+ else
+ {
+ sprintf(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
+ {
+ sprintf(error, "rates: end_hr error in line %d", line);
+ goto rate_error;
+ }
+
+ /* point */
+
+ if(*bp == '.')
+ {
+ bp++;
+ }
+ else
+ {
+ sprintf(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
+ {
+ sprintf(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 )
+ {
+ sprintf(error, "rates: end_time must be greater then start_time %d", line);
+ goto rate_error;
+ }
+
+ /* colon */
+
+ if(*bp == ':')
+ {
+ bp++;
+ }
+ else
+ {
+ sprintf(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
+ {
+ sprintf(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..3fa3ef5
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_config.c
@@ -0,0 +1,1178 @@
+/*
+ * 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 - config file processing
+ * -----------------------------------
+ *
+ * $Id: rc_config.c,v 1.40 1999/02/23 16:25:49 hm Exp $
+ *
+ * last edit-date: [Tue Feb 23 16:43:56 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "isdnd.h"
+#include "y.tab.h"
+
+#include "monitor.h"
+#include "vararray.h"
+
+extern int entrycount;
+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 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(reread)
+ {
+ reset_scanner(yyin);
+ }
+
+ if (yyin == NULL)
+ {
+ log(LL_ERR, "cannot fopen file [%s]", filename);
+ exit(1);
+ }
+
+ 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;
+
+ /* 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;
+ }
+
+ /* 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;
+
+ /* ======== filled in after start, then dynamic */
+
+ cep->cdid = CDID_UNUSED;
+
+ cep->state = ST_IDLE;
+
+ cep->aoc_valid = AOC_INVALID;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * 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 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 BEEPCONNECT:
+ do_bell = yylval.booln;
+ DBGL(DL_RCCF, (log(LL_DBG, "system: beepconnect = %d", yylval.booln)));
+ 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 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 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 = msg_alg__fix_unit_size;
+ }
+ else if(!(strcmp(yylval.str, "var-unit-size")))
+ {
+ cfg_entry_tab[entrycount].shorthold_algorithm = msg_alg__var_unit_size;
+ }
+ 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:
+ switch(yylval.num)
+ {
+ case 0:
+ case -1:
+ cfg_entry_tab[entrycount].isdnchannel = CHAN_ANY;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = any", entrycount)));
+ break;
+ case 1:
+ cfg_entry_tab[entrycount].isdnchannel = CHAN_B1;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = one", entrycount)));
+ break;
+ case 2:
+ cfg_entry_tab[entrycount].isdnchannel = CHAN_B2;
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: isdnchannel = two", entrycount)));
+ break;
+ default:
+ log(LL_DBG, "entry %d: isdnchannel value out of range", entrycount);
+ config_error_flag++;
+ break;
+ }
+ 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, 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, yylval.str);
+ 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 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_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 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 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 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;
+ else if(!strcmp(yylval.str, "ibc"))
+ cfg_entry_tab[entrycount].usrdevicename = BDRV_IBC;
+ 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 USEDOWN:
+ DBGL(DL_RCCF, (log(LL_DBG, "entry %d: usedown = %d", entrycount, yylval.booln)));
+ cfg_entry_tab[entrycount].usedown = yylval.booln;
+ break;
+
+ default:
+ log(LL_ERR, "ERROR parsing config file: unknown keyword at line %d!", lineno);
+ config_error_flag++;
+ break;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * 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 < 0) || (cep->isdncontroller > (ncontroller-1)))
+ {
+ log(LL_ERR, "check_config: isdncontroller out of range in entry %d!", i);
+ error++;
+ }
+
+ /* 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) == 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) == 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(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
+
+ cfg_entry_t *cep = &cfg_entry_tab[0]; /* ptr to config entry */
+ int i, j;
+ extern VARA_DECL(struct monitor_rights) rights;
+ 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);
+
+ if(VARA_NUM(rights))
+ {
+ char *s = "error\n";
+ char b[512];
+
+ VARA_FOREACH(rights, i)
+ {
+ if(VARA_AT(rights, i).local)
+ {
+ fprintf(PFILE, "monitor = \"%s\"\t\t# local socket name for monitoring\n", VARA_AT(rights, i).name);
+ }
+ else
+ {
+ struct in_addr ia;
+ ia.s_addr = ntohl(VARA_AT(rights, i).net);
+
+ switch(VARA_AT(rights, i).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((VARA_AT(rights, i).rights) & I4B_CA_COMMAND_FULL)
+ strcat(b, "fullcmd,");
+ if((VARA_AT(rights, i).rights) & I4B_CA_COMMAND_RESTRICTED)
+ strcat(b, "restrictedcmd,");
+ if((VARA_AT(rights, i).rights) & I4B_CA_EVNT_CHANSTATE)
+ strcat(b, "channelstate,");
+ if((VARA_AT(rights, i).rights) & I4B_CA_EVNT_CALLIN)
+ strcat(b, "callin,");
+ if((VARA_AT(rights, i).rights) & I4B_CA_EVNT_CALLOUT)
+ strcat(b, "callout,");
+ if((VARA_AT(rights, i).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;
+ case CHAN_B1:
+ fprintf(PFILE, "1\t\t# only ISDN B-channel 1 may be used\n");
+ break;
+ case CHAN_B2:
+ fprintf(PFILE, "2\t\t# only ISDN B-channel 2 ay be used\n");
+ 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);
+ 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);
+ 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->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 msg_alg__fix_unit_size: s = "fix-unit-size"; break;
+ case msg_alg__var_unit_size: 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..8e00b78
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_parse.y
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+ * -----------------------------------------
+ *
+ * $Id: rc_parse.y,v 1.18 1999/02/23 16:25:49 hm Exp $
+ *
+ * last edit-date: [Tue Feb 23 16:42:02 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+/* #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 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;
+
+%}
+
+%token ACCTALL
+%token ACCTFILE
+%token ALERT
+%token ALIASING
+%token ALIASFNAME
+%token ANSWERPROG
+%token B1PROTOCOL
+%token BEEPCONNECT
+%token CALLBACKWAIT
+%token CALLEDBACKWAIT
+%token CONNECTPROG
+%token DIALRETRIES
+%token DIALRANDINCR
+%token DIALOUTTYPE
+%token DIRECTION
+%token DISCONNECTPROG
+%token DOWNTRIES
+%token DOWNTIME
+%token EARLYHANGUP
+%token ENTRY
+%token IDLETIME_IN
+%token IDLETIME_OUT
+%token IDLE_ALG_OUT
+%token ISDNCONTROLLER
+%token ISDNCHANNEL
+%token ISDNTIME
+%token ISDNTXDELIN
+%token ISDNTXDELOUT
+%token LOCAL_PHONE_DIALOUT
+%token LOCAL_PHONE_INCOMING
+%token MONITORSW
+%token MONITORPORT
+%token MONITOR
+%token MONITORACCESS
+%token FULLCMD
+%token RESTRICTEDCMD
+%token CHANNELSTATE
+%token CALLIN
+%token CALLOUT
+%token LOGEVENTS
+%token NAME
+%token NO
+%token OFF
+%token ON
+%token RATESFILE
+%token RATETYPE
+%token REMOTE_NUMBERS_HANDLING
+%token REMOTE_PHONE_INCOMING
+%token REMOTE_PHONE_DIALOUT
+%token REACTION
+%token RECOVERYTIME
+%token REGEXPR
+%token REGPROG
+%token RTPRIO
+%token SYSTEM
+%token UNITLENGTH
+%token UNITLENGTHSRC
+%token USEACCTFILE
+%token USRDEVICENAME
+%token USRDEVICEUNIT
+%token USEDOWN
+%token YES
+
+%token <str> NUMBERSTR
+
+%token <str> STRING
+
+%type <booln> boolean
+
+%type <num> sysfilekeyword sysnumkeyword sysstrkeyword sysboolkeyword
+%type <num> numkeyword strkeyword boolkeyword monrights monright
+%type <str> filename
+
+%union {
+ int booln;
+ int num;
+ char *str;
+}
+
+%%
+
+config: sections
+ ;
+
+sections: possible_nullentries
+ syssect
+ entrysects
+ ;
+
+possible_nullentries:
+ /* lambda */
+ | possible_nullentries error '\n'
+ | possible_nullentries nullentry
+ ;
+
+nullentry: '\n'
+ ;
+
+entrysects: entrysect
+ | entrysects entrysect
+ ;
+
+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; }
+ ;
+
+sysboolkeyword: USEACCTFILE { $$ = USEACCTFILE; }
+ | ALIASING { $$ = ALIASING; }
+ | ACCTALL { $$ = ACCTALL; }
+ | BEEPCONNECT { $$ = BEEPCONNECT; }
+ | ISDNTIME { $$ = ISDNTIME; }
+ | MONITORSW { $$ = MONITORSW; }
+ ;
+
+sysnumkeyword: MONITORPORT { $$ = MONITORPORT; }
+ | RTPRIO { $$ = RTPRIO; }
+ ;
+
+sysstrkeyword: REGEXPR { $$ = REGEXPR; }
+ | REGPROG { $$ = REGPROG; }
+ ;
+
+entrysect: ENTRY
+ {
+ entrycount++;
+ nentries++;
+ }
+ entries
+ ;
+
+entries: entry
+ | entries entry
+ ;
+
+entry: strentry
+ | numentry
+ | boolentry
+ | nullentry
+ | error '\n'
+ ;
+
+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);
+ }
+ ;
+
+strkeyword: ANSWERPROG { $$ = ANSWERPROG; }
+ | B1PROTOCOL { $$ = B1PROTOCOL; }
+ | CONNECTPROG { $$ = CONNECTPROG; }
+ | DIALOUTTYPE { $$ = DIALOUTTYPE; }
+ | DIRECTION { $$ = DIRECTION; }
+ | DISCONNECTPROG { $$ = DISCONNECTPROG; }
+ | IDLE_ALG_OUT { $$ = IDLE_ALG_OUT; }
+ | LOCAL_PHONE_INCOMING { $$ = LOCAL_PHONE_INCOMING; }
+ | LOCAL_PHONE_DIALOUT { $$ = LOCAL_PHONE_DIALOUT; }
+ | NAME { $$ = NAME; }
+ | REACTION { $$ = REACTION; }
+ | REMOTE_NUMBERS_HANDLING { $$ = REMOTE_NUMBERS_HANDLING; }
+ | REMOTE_PHONE_INCOMING { $$ = REMOTE_PHONE_INCOMING; }
+ | REMOTE_PHONE_DIALOUT { $$ = REMOTE_PHONE_DIALOUT; }
+ | UNITLENGTHSRC { $$ = UNITLENGTHSRC; }
+ | USRDEVICENAME { $$ = USRDEVICENAME; }
+ ;
+
+numkeyword: ALERT { $$ = ALERT; }
+ | 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; }
+ ;
+
+boolkeyword: DIALRANDINCR { $$ = DIALRANDINCR; }
+ | USEDOWN { $$ = USEDOWN; }
+ ;
+
+%%
diff --git a/usr.sbin/i4b/isdnd/rc_scan.l b/usr.sbin/i4b/isdnd/rc_scan.l
new file mode 100644
index 0000000..2a286c9
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/rc_scan.l
@@ -0,0 +1,172 @@
+/*
+ * Copyright (c) 1997 Joerg Wunsch. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+ * ---------------------------------------------------
+ *
+ * $Id: rc_scan.l,v 1.22 1999/02/23 16:25:49 hm Exp $
+ *
+ * last edit-date: [Tue Feb 23 16:41:02 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+%{
+
+#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; }
+alert { return ALERT; }
+aliasing { return ALIASING; }
+aliasfile { return ALIASFNAME; }
+answerprog { return ANSWERPROG; }
+b1protocol { return B1PROTOCOL; }
+beepconnect { return BEEPCONNECT; }
+callbackwait { return CALLBACKWAIT; }
+calledbackwait { return CALLEDBACKWAIT; }
+connectprog { return CONNECTPROG; }
+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; }
+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-phone-incoming { return LOCAL_PHONE_INCOMING; }
+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; }
+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-phone-incoming { return REMOTE_PHONE_INCOMING; }
+rtprio { return RTPRIO; }
+system { return SYSTEM; }
+unitlength { return UNITLENGTH; }
+unitlengthsrc { return UNITLENGTHSRC; }
+useacctfile { return USEACCTFILE; }
+usrdevicename { return USRDEVICENAME; }
+usrdeviceunit { return USRDEVICEUNIT; }
+usedown { return USEDOWN; }
+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..ac3ca1b
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/support.c
@@ -0,0 +1,853 @@
+/*
+ * 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 - misc support routines
+ * ----------------------------------
+ *
+ * $Id: support.c,v 1.48 1999/02/17 14:31:42 hm Exp $
+ *
+ * last edit-date: [Mon Feb 15 16:40:05 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "isdnd.h"
+
+#define SRC (aliasing == 0 ? mp->src_telno : src_tela)
+#define DST (aliasing == 0 ? mp->dst_telno : dst_tela)
+
+/*---------------------------------------------------------------------------*
+ * 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;
+ }
+
+ /* found */
+
+ if(cep->cdid == CDID_UNUSED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d, cdid is CDID_UNUSED!", i)));
+ return(NULL);
+ }
+ else if(cep->cdid == CDID_RESERVED)
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d, cdid is CDID_RESERVED!", i)));
+ 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;
+ }
+
+ /* 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
+ *---------------------------------------------------------------------------*/
+int
+setup_dialout(cfg_entry_t *cep)
+{
+ /* 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_B1:
+ case CHAN_B2:
+ 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;
+
+ case CHAN_ANY:
+ if(((ret_channel_state(cep->isdncontroller, CHAN_B1)) != CHAN_IDLE) &&
+ ((ret_channel_state(cep->isdncontroller, CHAN_B2)) != CHAN_IDLE))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
+ return(ERROR);
+ }
+ cep->isdnchannelused = CHAN_ANY;
+ break;
+
+ default:
+ DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel undefined", cep->name)));
+ return(ERROR);
+ 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;
+ }
+
+ 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;
+ char *src_tela = "ERROR-src_tela";
+ char *dst_tela = "ERROR-dst_tela";
+
+ 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, mp->dst_telno, strlen(cep->local_phone_incoming)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s", i,
+ cep->local_phone_incoming, mp->dst_telno)));
+ continue;
+ }
+
+ /* check all allowed remote number's for this entry */
+
+ for (n = 0; n < cep->incoming_numbers_count; n++)
+ {
+ incoming_number_t *in = &cep->remote_phone_incoming[n];
+ if(in->number[0] == '*')
+ break;
+ if(strncmp(in->number, mp->src_telno, strlen(in->number)))
+ {
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
+ in->number, mp->src_telno)));
+ }
+ else
+ break;
+ }
+ if (n >= cep->incoming_numbers_count)
+ continue;
+
+ /* screening indicator XXX */
+
+ switch(mp->scr_ind)
+ {
+ case SCR_NONE:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: %s - no screening indicator", mp->src_telno)));
+ break;
+
+ case SCR_USR_NOSC:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: %s - screening user provided, not screened", mp->src_telno)));
+ break;
+
+ case SCR_USR_PASS:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: %s - screening user provided, verified & passed", mp->src_telno)));
+ break;
+
+ case SCR_USR_FAIL:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: %s - screening user provided, verified & failed", mp->src_telno)));
+ break;
+
+ case SCR_NET:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: %s - screening network provided", mp->src_telno)));
+ break;
+
+ default:
+ DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: ERROR %s - invalid screening indicator!", mp->src_telno)));
+ break;
+ }
+
+ /* 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 channel he wants */
+
+ switch(mp->channel)
+ {
+ case CHAN_B1:
+ case CHAN_B2:
+ if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
+ {
+ log(LL_CHD, "%05d %s incoming call, channel %s not free!",
+ mp->header.cdid, cep->name, mp->channel == CHAN_B1 ? "B1" : "B2");
+ return(NULL);
+ }
+ break;
+
+ case CHAN_ANY:
+ if(((ret_channel_state(mp->controller, CHAN_B1)) != CHAN_IDLE) &&
+ ((ret_channel_state(mp->controller, CHAN_B2)) != CHAN_IDLE))
+ {
+ 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:
+ log(LL_CHD, "%05d %s incoming call, ERROR, channel undefined!",
+ mp->header.cdid, cep->name);
+ return(NULL);
+ break;
+ }
+
+ /* 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, 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));
+ do_exit(1);
+ }
+
+ cep->down_retry_count = 0;
+ cep->state = ST_IDLE;
+ }
+ return(cep);
+ }
+
+ if(aliasing)
+ {
+ src_tela = get_alias(mp->src_telno);
+ dst_tela = get_alias(mp->dst_telno);
+ }
+ log(LL_CHD, /* A number not listed in /etc/isdn/isdnd.rc */
+ ( (!aliasing) ?
+ "%05d <unknown> incoming call from %s to %s" :
+ /* Probably a phone call, likely from someone
+ in phone book /etc/isdn/isdntel.alias, so
+ avoid looking silly by saying "unknown",
+ & allow more space to print names.
+ */
+ "%05d Call from %s to %s"
+ ) ,
+ mp->header.cdid, SRC, DST, mp->dst_telno);
+
+ 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 != CHAN_B1) && (chan != CHAN_B2))
+ 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);
+}
+
+/*---------------------------------------------------------------------------*
+ * 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 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"
+ };
+ static char *daic_card[] = {
+ "EICON.Diehl S",
+ "EICON.Diehl SX/SXn",
+ "EICON.Diehl SCOM",
+ "EICON.Diehl QUADRO",
+ };
+
+ 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];
+ }
+
+ 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)) == 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)));
+}
+
+/*---------------------------------------------------------------------------*
+ * return b channel driver type name string
+ *---------------------------------------------------------------------------*/
+char *
+bdrivername(int drivertype)
+{
+ static char *bdtab[] = {
+ "rbch",
+ "tel",
+ "ipr",
+ "isp",
+ "ibc"
+ };
+
+ if(drivertype >= BDRV_RBCH && drivertype <= BDRV_IBC)
+ 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
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void
+unitlen_chkupd(cfg_entry_t *cep)
+{
+ msg_timeout_upd_t tupd;
+
+/* XXX check if the values are possible, if not, adjust them */
+
+ tupd.cdid = cep->cdid;
+
+ /* init the short hold data based on the shorthold algorithm type */
+
+ switch( cep->shorthold_algorithm )
+ {
+
+ case msg_alg__fix_unit_size:
+ tupd.shorthold_data.shorthold_algorithm = msg_alg__fix_unit_size;
+ 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 msg_alg__var_unit_size:
+ tupd.shorthold_data.shorthold_algorithm = msg_alg__var_unit_size;
+ 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 );
+
+ }
+
+ if((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0)
+ {
+ log(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
+ do_exit(1);
+ }
+}
+
+/*--------------------------------------------------------------------------*
+ * 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;
+ cfg_entry_t *cep = NULL;
+
+ j = 0;
+
+ for (i = 0; i < ncontroller; i++)
+ {
+ if((get_controller_state(i)) != CTRL_UP)
+ continue;
+
+ if((ret_channel_state(i, CHAN_B1)) == CHAN_RUN)
+ {
+ if((cep = get_cep_by_cc(i, CHAN_B1)) != 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((ret_channel_state(i, CHAN_B2)) == CHAN_RUN)
+ {
+ if((cep = get_cep_by_cc(i, CHAN_B2)) != 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));
+ do_exit(1);
+ }
+ 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));
+ do_exit(1);
+ }
+ 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;
+
+ if((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0)
+ {
+ log(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
+ do_exit(1);
+ }
+
+ DBGL(DL_DRVR, (log(LL_DBG, "dialresponse: sent [%s]", stattab[dstat])));
+}
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdnd/timer.c b/usr.sbin/i4b/isdnd/timer.c
new file mode 100644
index 0000000..7bbd09fc
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/timer.c
@@ -0,0 +1,411 @@
+/*
+ * 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 - timer/timing support routines
+ * ------------------------------------------
+ *
+ * $Id: timer.c,v 1.17 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:12:32 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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 */
+
+ 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,
+ cep->local_phone_incoming);
+ 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);
+ }
+
+ /*
+ * 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/isdnd/vararray.h b/usr.sbin/i4b/isdnd/vararray.h
new file mode 100644
index 0000000..c71f816
--- /dev/null
+++ b/usr.sbin/i4b/isdnd/vararray.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1997, 1998 Martin Husemann <martin@rumolt.teuto.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. The name of the author may not be used to endorse or promote products
+ * derived from this software withough 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.
+ */
+
+/*
+ * vararray.h: basic collection macros for variable sized (growing) arrays:
+ * macro version of popular C++ template classes,
+ * not as elegant in use as the template version,
+ * but has maximum runtime performance and can give
+ * pointers to array contents (i.e. for ioctl's).
+ * Works in C as well as in C++.
+ * CAVEAT: in C++ only useable for aggregateable objects,
+ * it does use memcpy instead of copy constructors!
+ */
+
+#ifndef VARARRAY_H
+#define VARARRAY_H
+/* declare a variable sized array, element type is t */
+#define VARA_DECL(t) struct { int used, allocated; t *data; }
+
+/* aggregate initializer for a variable array */
+#define VARA_INITIALIZER { 0, 0, NULL }
+
+/* free all allocated storage */
+#define VARA_FREE(a) { if ((a).data != NULL) free((a).data); \
+ (a).allocated = 0; (a).used = 0; }
+
+/* number of elments currently in array*/
+#define VARA_NUM(a) ((a).used)
+
+/* number of elements already allocated in array */
+#define VARA_ALLOCATED(a) ((a).allocated)
+
+/* pointer to array data */
+#define VARA_PTR(a) ((a).data)
+
+/* empty the array */
+#define VARA_EMPTY(a) { (a).used = 0; }
+
+#ifdef __cplusplus
+#define VARA_NEW(t,c) new t[c]
+#define VARA_DELETE(p) delete [] p
+#else
+#define VARA_NEW(t,c) (t*)malloc(sizeof(t)*(c))
+#define VARA_DELETE(p) free(p)
+#endif
+
+/* add an element (not changing any data).
+ * a is the array, i the index,
+ * t the element type and n the initial allocation */
+#define VARA_ADD_AT(a,i,t,n) \
+{ \
+ if ((i) >= (a).allocated) { \
+ int new_alloc = (a).allocated ? (a).allocated*2 : (n); \
+ t *new_data; \
+ if (new_alloc <= (i)) new_alloc = (i)+1; \
+ new_data = VARA_NEW(t, new_alloc); \
+ if ((a).data) { \
+ memcpy(new_data, (a).data, (a).used*sizeof(t)); \
+ VARA_DELETE((a).data); \
+ } \
+ (a).data = new_data; \
+ (a).allocated = new_alloc; \
+ } \
+ if ((i) >= (a).used) { \
+ if (i > (a).used) \
+ memset(&((a).data[(a).used]), 0, \
+ sizeof(t)*((i)-(a).used+1)); \
+ (a).used = (i)+1; \
+ } \
+}
+
+/* return an l-value at index i */
+#define VARA_AT(a,i) ((a).data[(i)])
+
+/* iterate through the array */
+#define VARA_FOREACH(a,i) for ((i) = 0; (i) < (a).used; (i)++)
+
+/* check for a valid index */
+#define VARA_VALID(a,i) ((i) >= 0 && (i) < (a).used)
+
+/* remove one entry */
+#define VARA_REMOVEAT(a,i) \
+{ \
+ if ((i) < ((a).used -1)) \
+ memmove(&((a).data[(i)]), &((a).data[(i)+1]), sizeof((a).data[0])); \
+ (a).used--; \
+}
+
+/* free all storage allocated for the array */
+#define VARA_DESTROY(a) \
+{ \
+ if ((a).data) VARA_DELETE((a).data); \
+ (a).allocated = 0; \
+ (a).used = 0; \
+ (a).data = NULL; \
+}
+
+#endif
+
diff --git a/usr.sbin/i4b/isdndebug/Makefile b/usr.sbin/i4b/isdndebug/Makefile
new file mode 100644
index 0000000..cf52818
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/Makefile
@@ -0,0 +1,5 @@
+PROG = isdndebug
+SRCS = main.c
+MAN8 = isdndebug.8
+
+.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..23f4b56
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/isdndebug.8
@@ -0,0 +1,102 @@
+.\"
+.\" 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: isdndebug.8,v 1.6 1999/02/15 16:48:04 hm Exp $
+.\"
+.\" last edit-date: [Mon Feb 15 17:05:41 1999]
+.\"
+.\" -hm writing manual pages
+.\" -hm getting uptodate
+.\"
+.\"
+.Dd July 9, 1998
+.Dt isdndebug 8
+.Sh NAME
+.Nm isdndebug
+.Nd control debugging handling inside isdn4bsd kernel
+.Sh SYNOPSIS
+.Nm
+.Op Fl e
+.Op Fl g
+.Op Fl h
+.Op Fl l Ar layer
+.Op Fl m
+.Op Fl r
+.Op Fl s Ar value
+.Op Fl u Ar unit
+.Op Fl z
+.Op Fl H
+.Sh DESCRIPTION
+.Nm isdndebug
+is part of the isdn4bsd package and is used to control 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 the HSCX error counters.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.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 h
+Display the HSCX error counters.
+.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 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 -h and -H flags.
+.It Fl z
+Set debugging mask for the selected layer(s) to no output at all (zero).
+.It Fl H
+Reset the HSCX error counters to zero.
+.Pp
+.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 AUTHOR
+The
+.Nm
+utility and this manpage were written by Hellmuth Michaelis. He can be
+contacted at 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..91c1e66
--- /dev/null
+++ b/usr.sbin/i4b/isdndebug/main.c
@@ -0,0 +1,553 @@
+/*
+ * 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 set debug options
+ * ------------------------------
+ *
+ * $Id: main.c,v 1.14 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:13:25 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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_hscx = 0;
+int opt_rhscx = 0;
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ ctl_debug_t cdbg;
+ int ret;
+
+ while ((c = getopt(argc, argv, "eghl:mrs:u:zH?")) != EOF)
+ {
+ switch(c)
+ {
+ case 'e':
+ opt_err = 1;
+ break;
+
+ case 'g':
+ opt_get = 1;
+ break;
+
+ case 'h':
+ opt_hscx = 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 'H':
+ opt_rhscx = 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_hscx == 0 && opt_rhscx == 0)
+ {
+ usage();
+ }
+
+ if((opt_get + opt_set + opt_reset + opt_max + opt_err + opt_zero +
+ opt_hscx + opt_rhscx) > 1)
+ {
+ usage();
+ }
+
+ if((isdnfd = open(I4BCTLDEVICE, O_RDWR)) < 0)
+ {
+ fprintf(stderr, "i4bctl: cannot open %s: %s\n", I4BCTLDEVICE, strerror(errno));
+ exit(1);
+ }
+
+ if(opt_hscx)
+ {
+ hscxstat_t hst;
+
+ hst.unit = opt_unit;
+ hst.chan = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_HSCXSTAT, &hst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_HSCXSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("\nHSCX events: VFR RDO CRC RAB XDU RFO\n");
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ hst.unit, hst.chan,
+ hst.vfr, hst.rdo, hst.crc, hst.rab, hst.xdu, hst.rfo);
+
+ hst.unit = opt_unit;
+ hst.chan = 1;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_GET_HSCXSTAT, &hst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_GET_HSCXSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("unit %d chan %d: %6d %6d %6d %6d %6d %6d\n",
+ hst.unit, hst.chan,
+ hst.vfr, hst.rdo, hst.crc, hst.rab, hst.xdu, hst.rfo);
+
+ exit(0);
+ }
+
+ if(opt_rhscx)
+ {
+ hscxstat_t hst;
+
+ hst.unit = opt_unit;
+ hst.chan = 0;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_CLR_HSCXSTAT, &hst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_CLR_HSCXSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("HSCX event counters unit %d chan %d reset to zero!\n",
+ hst.unit, hst.chan);
+
+ hst.unit = opt_unit;
+ hst.chan = 1;
+
+ if((ret = ioctl(isdnfd, I4B_CTL_CLR_HSCXSTAT, &hst)) < 0)
+ {
+ fprintf(stderr, "ioctl I4B_CTL_CLR_HSCXSTAT failed: %s", strerror(errno));
+ exit(1);
+ }
+
+ printf("HSCX event counters unit %d chan %d reset to zero!\n",
+ hst.unit, hst.chan);
+
+ 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(" ++++-++++-++++-++++-++------------------ 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(" ++++-++++-++++-++++-++++-++++-++-------- unassigned\n");
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdndebug - i4b set debug level, version %02d.%02d, compiled %s %s\n", VERSION, REL, __DATE__, __TIME__);
+ fprintf(stderr, "usage: isdndebug -e -h -g -l <layer> -m -r -s <value> -u <unit> -z -H\n");
+ fprintf(stderr, " -e set error only debugging output\n");
+ fprintf(stderr, " -g get current debugging values\n");
+ fprintf(stderr, " -h get HSCX event counters\n");
+ fprintf(stderr, " -l layer specify layer (1...4)\n");
+ fprintf(stderr, " -m set maximum debugging output\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 -h and -H commands\n");
+ fprintf(stderr, " -z set zero (=no) debugging output\n");
+ fprintf(stderr, " -H reset HSCX event counters to zero\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/Makefile b/usr.sbin/i4b/isdndecode/Makefile
new file mode 100644
index 0000000..cad91437
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/Makefile
@@ -0,0 +1,7 @@
+PROG = isdndecode
+SRCS = main.c layer1.c layer2.c layer3.c \
+ layer3_subr.c facility.c pcause.c
+MAN8 = isdndecode.8
+
+.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..0a88ae7
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/decode.h
@@ -0,0 +1,74 @@
+/*
+ * 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.5 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:15:59 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..183ff50
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.c
@@ -0,0 +1,906 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: facility.c,v 1.3 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:11 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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_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 },
+
+/* 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..2c6475f
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/facility.h
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * facility.h - Q.932 facility header file
+ * ---------------------------------------
+ *
+ * $Id: facility.h,v 1.3 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:17 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+/* #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_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/isdndecode.8 b/usr.sbin/i4b/isdndecode/isdndecode.8
new file mode 100644
index 0000000..0de016d
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/isdndecode.8
@@ -0,0 +1,190 @@
+.\"
+.\" Copyright (c) 1998, 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: isdndecode.8,v 1.4 1999/02/14 09:44:57 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:16:25 1999]
+.\"
+.\" -hm writing manual page
+.\"
+.Dd September 17, 1998
+.Dt isdndecode 8
+.Sh NAME
+.Nm isdndecode
+.Nd isdn4bsd ISDN protocol decode utility
+.Sh SYNOPSIS
+.Nm isdndecode
+.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 B
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+.Nm isdndecode
+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 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.
+
+.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
+.Pp
+
+.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
+/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 AUTHOR
+The
+.Nm
+utility and this manual page was written by Hellmuth Michaelis,
+he can be reached at 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..8df7bc5
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer1.c
@@ -0,0 +1,80 @@
+/*
+ * 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.3 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:40 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..20ce9cb
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer2.c
@@ -0,0 +1,298 @@
+/*
+ * 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.4 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:46 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..1542c10
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3.c
@@ -0,0 +1,508 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3.c - decode and print layer 3 (Q.931) information
+ * -------------------------------------------------------
+ *
+ * $Id: layer3.c,v 1.6 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:52 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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);
+
+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_null },
+ { 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++;
+
+ /* 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));
+ i++;
+ sprintline(3, (pbuf+strlen(pbuf)), off+i, buf[i], 0xff, "Call reference = %d = %02x", (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)),"]");
+
+ 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..78671c5
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/layer3_subr.c
@@ -0,0 +1,1045 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * layer3_subr.c - subroutines for IE decoding
+ * -------------------------------------------
+ *
+ * $Id: layer3_subr.c,v 1.5 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:16:58 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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]);
+
+ 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;
+
+ 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:
+
+ 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);
+}
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdndecode/main.c b/usr.sbin/i4b/isdndecode/main.c
new file mode 100644
index 0000000..fd3774a
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/main.c
@@ -0,0 +1,774 @@
+/*
+ * 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 - isdndecode main program file
+ * -------------------------------------
+ *
+ * $Id: main.c,v 1.7 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:17:04 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "decode.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;
+
+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 );
+
+/*---------------------------------------------------------------------------*
+ * main
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char *argv[])
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ int enable_trace = TRACE_D_RX | TRACE_D_TX;
+ 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:BPR:T:?")) != EOF)
+ {
+ 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 '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(&(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 */
+ layer1(l1buf, buf);
+ break;
+
+ case TRC_CH_D: /* D-channel data */
+ cnt = layer2(l2buf, buf, hdr->dir, print_q921);
+
+ n -= cnt;
+ buf += cnt;
+
+ if(n)
+ 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);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * usage intructions
+ *---------------------------------------------------------------------------*/
+void
+usage(void)
+{
+ fprintf(stderr,"\n");
+ fprintf(stderr,"isdndecode - isdn4bsd package ISDN decoder for passive cards (%02d.%02d)\n", VERSION, REL);
+ fprintf(stderr,"usage: isdntrace -a -b -d -f <file> -h -i -l -n <val> -o -p <file> -r -u <unit>\n");
+ fprintf(stderr," -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," -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);
+}
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdndecode/pcause.c b/usr.sbin/i4b/isdndecode/pcause.c
new file mode 100644
index 0000000..c2b28b1
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.c
@@ -0,0 +1,328 @@
+/*
+ * 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.4 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:17:10 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..765bc3a
--- /dev/null
+++ b/usr.sbin/i4b/isdndecode/pcause.h
@@ -0,0 +1,109 @@
+/*
+ * 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.3 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:17:15 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..9d3811b
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/Makefile
@@ -0,0 +1,8 @@
+PROG = isdnmonitor
+SRCS = main.c
+MAN8 = isdnmonitor.8
+
+# compile debug support
+COPTS += -DDEBUG
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/i4b/isdnmonitor/isdnmonitor.8 b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
new file mode 100644
index 0000000..5da1eba
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/isdnmonitor.8
@@ -0,0 +1,40 @@
+.\" Copyright (c) 1998 Martin Husemann <martin@rumolt.teuto.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. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough 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.
+.\"
+.\" $Id: isdnmonitor.8,v 1.3 1999/02/14 09:44:57 hm Exp $
+.\"
+.\" last edit-date: [Fri Jan 30 22:49:48 1998]
+.\"
+.\" -mh writing manual pages
+.\"
+.\"
+.Dd April 18, 1998
+.Dt isdnmonitor 8
+.Sh NAME
+.Nm isdnmonitor
+.Nd raw sample and test client for isdnd network monitoring
+.Sh DESCRIPTION
+The
+.Nm
+is not intended for real world use. It will be replaced
+by something with a better user interface as soon as the monitoring
+subsystem is functional.
diff --git a/usr.sbin/i4b/isdnmonitor/main.c b/usr.sbin/i4b/isdnmonitor/main.c
new file mode 100644
index 0000000..d1903de
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/main.c
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 1998 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.10 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Tue Oct 27 11:53:12 1998]
+ *
+ * -mh created
+ * -hm checking in
+ * -hm porting to HPUX
+ * -mh all events the fullscreen mode displays now as monitor event
+ *
+ *---------------------------------------------------------------------------*/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <time.h>
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <machine/i4b_ioctl.h>
+
+#ifdef __hpux
+#define AF_LOCAL AF_UNIX
+#endif
+
+#ifdef DEBUG
+#include <ctype.h>
+#endif
+
+#include "monitor.h"
+
+/*
+ * Local function prototypes
+ */
+static int connect_local(const char *sockpath);
+static int connect_remote(const 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, const char * what, const char * msg);
+static void print_charge(time_t tstamp, int channel, int units, int estimated);
+static void print_connect(time_t tstamp, int dir, int channel, const char * cfgname, const char * devname, const char * remphone, const char * locphone);
+static void print_disconnect(time_t tstamp, int channel);
+static void print_updown(time_t tstamp, int channel, int isup);
+static void handle_event(BYTE *msg, int len);
+#ifdef DEBUG
+static void dump_event(BYTE *msg, int len);
+#endif
+
+/*
+ * Global variables
+ */
+static int dumpall = 0;
+static int monsock = -1;
+static int state = 0;
+static int sub_state = 0;
+static int sub_state_count = 0;
+
+static int major = 0;
+static int minor = 0;
+static int nctrl = 0;
+static u_int32_t rights = 0;
+
+/*
+ * Parse command line, startup monitor client
+ */
+int main(int argc, char **argv)
+{
+ char * sockpath = NULL;
+ char * hostname = NULL;
+ int portno = DEF_MONPORT;
+ int i;
+
+ while ((i = getopt(argc, argv, "dh:p:l:")) != EOF)
+ {
+ switch (i)
+ {
+ case 'd':
+ dumpall = 1;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+ case 'l':
+ sockpath = optarg;
+ break;
+ case 'p':
+ if ((sscanf(optarg, "%i", &portno)) != 1)
+ usage();
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ 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)
+ {
+ monsock = connect_remote(hostname, portno);
+ }
+ else
+ {
+ usage();
+ }
+
+ if (monsock == -1)
+ {
+ fprintf(stderr, "Could not connect to i4b isdn daemon.\n");
+ return 1;
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+ mloop();
+
+ close(monsock);
+
+ return 0;
+}
+
+/*
+ * Display usage and exit
+ */
+static void usage()
+{
+ fprintf(stderr, "usage:\n"
+ " isdnmonitor [-d] -h (host) -p (port)\n"
+ "or\n"
+ " isdnmonitor [-d] -l (path)\n"
+ "where (host) is the hostname and (port) the port number of\n"
+ "the isdnd to be monitored and (path) is the pathname of the\n"
+ "local domain socket used to communicate with a daemon on the\n"
+ "local machine.\n"
+ "Options are:\n"
+ " -d dump all incoming packets as hexdump\n"
+ );
+ exit(0);
+}
+
+/*
+ * Connect via tcp/ip.
+ * Return socket if successfull, -1 on error.
+ */
+static int connect_remote(const 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;
+}
+
+/*
+ * Connect local.
+ * Return socket on success, -1 on failure.
+ */
+static int connect_local(const 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);
+
+#ifndef __hpux
+ sa.sun_len = sizeof sa;
+#endif
+
+ 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;
+}
+
+/*
+ * 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))
+ {
+ handle_input();
+ }
+
+ if (FD_ISSET(monsock, &rd))
+ {
+ BYTE buf[1024];
+ u_long u;
+ int bytes, ret;
+
+ /* Network transfer may deliver two or more packets concatenated.
+ * Peek at the header and read only one event at a time... */
+
+ ioctl(monsock, FIONREAD, &u);
+
+ if (u < I4B_MON_EVNT_HDR)
+ continue; /* not enough data there yet */
+
+ bytes = recv(monsock, buf, I4B_MON_EVNT_HDR, MSG_PEEK);
+
+ 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)
+ break;
+
+ /* now we know the size, it fits, so lets read it! */
+
+ ret = read(monsock, buf, bytes);
+
+ if (ret == 0)
+ {
+ printf("remote isdnd has closed our connection\n");
+ break;
+ }
+ else if (ret < 0)
+ {
+ printf("error reading from isdnd: %s", strerror(errno));
+ break;
+ }
+#ifdef DEBUG
+ if (dumpall)
+ dump_event(buf, ret);
+#endif
+ handle_event(buf, ret);
+ }
+ }
+}
+
+#ifdef DEBUG
+/*
+ * Dump a complete event packet.
+ */
+static void dump_event(BYTE *msg, int len)
+{
+ int i;
+
+ printf("event dump:");
+
+ for (i = 0; i < len; i++)
+ {
+ if (i % 8 == 0)
+ printf("\n%02x: ", i);
+ printf("%02x %c ", msg[i], isprint(msg[i]) ? msg[i] : '.');
+ }
+ printf("\n");
+}
+#endif
+
+static void print_logevent(time_t tstamp, int prio, const char * what, const char * msg)
+{
+ char buf[256];
+ strftime(buf, sizeof buf, I4B_TIME_FORMAT, localtime(&tstamp));
+ printf("log: %s prio %d what=%s msg=%s\n",
+ buf, prio, what, msg);
+}
+
+static void print_charge(time_t tstamp, int channel, int units, int estimated)
+{
+ char buf[256];
+ strftime(buf, sizeof buf, I4B_TIME_FORMAT, localtime(&tstamp));
+ printf("%s: channel %d, charge = %d%s\n",
+ buf, channel, units, estimated ? " (estimated)" : "");
+}
+
+/*
+ * 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 channel, /* channel no, used to identify this connection until disconnect */
+ const char * cfgname, /* name of config entry/connection */
+ const char * devname, /* device used (e.g. isp0) */
+ const char * remphone, /* phone no of remote side */
+ const char * locphone) /* local phone no */
+{
+ char buf[256];
+ strftime(buf, sizeof buf, I4B_TIME_FORMAT, localtime(&tstamp));
+
+ if (outgoing)
+ printf("%s: calling out to '%s' [from msn: '%s']",
+ buf, remphone, locphone);
+ else
+ printf("%s: incoming call from '%s' [to msn: '%s']",
+ buf, remphone, locphone);
+ printf(", channel %d, config '%s' on device '%s'\n",
+ channel, cfgname, devname);
+}
+
+/*
+ * 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 channel)
+{
+ char buf[256];
+ strftime(buf, sizeof buf, I4B_TIME_FORMAT, localtime(&tstamp));
+ printf("%s: channel %d disconnected\n",
+ buf, channel);
+}
+
+/*
+ * Print an up- or down event
+ */
+static void print_updown(time_t tstamp, int channel, int isup)
+{
+ char buf[256];
+ strftime(buf, sizeof buf, I4B_TIME_FORMAT, localtime(&tstamp));
+ printf("%s: channel %d is %s\n",
+ buf, channel, isup ? "up" : "down");
+}
+
+/*
+ * Dispatch one message received from the daemon.
+ */
+static void handle_event(BYTE *msg, int len)
+{
+ BYTE cmd[I4B_MON_ICLIENT_SIZE];
+ int local;
+ u_int32_t net;
+ u_int32_t mask;
+ u_int32_t who;
+
+ switch (state)
+ {
+ case 0: /* initial data */
+
+ major = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMAJOR);
+ minor = I4B_GET_2B(msg, I4B_MON_IDATA_VERSMINOR);
+ nctrl = I4B_GET_2B(msg, I4B_MON_IDATA_NUMCTRL);
+ rights = I4B_GET_4B(msg, I4B_MON_IDATA_CLACCESS);
+
+ printf("remote protocol version is %02d.%02d, %d controller(s) found, our rights = %x\n",
+ major, minor, nctrl, rights);
+
+ if (nctrl > 0)
+ {
+ state = 1;
+ sub_state = 0;
+ }
+ else
+ {
+ state = 2;
+
+ /* show menu for the first time */
+ print_menu();
+ }
+
+ /* 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);
+
+ write(monsock, cmd, sizeof cmd);
+
+ break;
+
+ case 1: /* initial controller list */
+ printf("controller %d: %s\n", sub_state++, msg+I4B_MON_ICTRL_NAME);
+
+ if (sub_state >= nctrl)
+ {
+ state = 2; /* end of list reached */
+ sub_state = 0;
+
+ /* show menu for the first time */
+ print_menu();
+ }
+ break;
+
+ case 2: /* any event */
+
+ switch (I4B_GET_2B(msg, I4B_MON_EVNT))
+ {
+ case I4B_MON_DRINI_CODE:
+ state = 3; /* list of rights entries will follow */
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DRINI_COUNT);
+ printf("monitor rights:\n");
+ break;
+
+ case I4B_MON_DCINI_CODE:
+ state = 4;
+ sub_state = 0;
+ sub_state_count = I4B_GET_2B(msg, I4B_MON_DCINI_COUNT);
+ printf("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_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_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_CHANNEL));
+ break;
+
+ case I4B_MON_UPDOWN_CODE:
+ print_updown(
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_TSTAMP),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_CHANNEL),
+ I4B_GET_4B(msg, I4B_MON_UPDOWN_ISUP));
+ break;
+
+ default:
+ printf("unknown event code: %d\n", I4B_GET_2B(msg, I4B_MON_EVNT));
+ }
+ break;
+
+ case 3: /* 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)
+ {
+ printf("\tlocal: rights = %x\n", rights);
+ }
+ else
+ {
+ printf("\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 = 2;
+ print_menu();
+ }
+ break;
+
+ case 4:
+ who = I4B_GET_4B(msg, I4B_MON_DC_WHO);
+ rights = I4B_GET_4B(msg, I4B_MON_DC_RIGHTS);
+
+ printf("\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 = 2;
+ print_menu();
+ }
+ break;
+
+ default:
+ printf("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;
+
+ fgets(buf, sizeof buf, stdin);
+
+ switch (atoi(buf))
+ {
+ case 1:
+ {
+ BYTE cmd[I4B_MON_DUMPRIGHTS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPRIGHTS_CODE);
+ write(monsock, cmd, I4B_MON_DUMPRIGHTS_SIZE);
+ }
+ break;
+
+ case 2:
+ {
+ BYTE cmd[I4B_MON_DUMPMCONS_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_DUMPMCONS_CODE);
+ write(monsock, cmd, I4B_MON_DUMPMCONS_SIZE);
+ }
+ break;
+
+ case 3:
+ {
+ BYTE cmd[I4B_MON_CFGREREAD_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_CFGREREAD_CODE);
+ write(monsock, cmd, I4B_MON_CFGREREAD_SIZE);
+ }
+ break;
+
+ case 4:
+ {
+ BYTE cmd[I4B_MON_HANGUP_SIZE];
+ I4B_PREP_CMD(cmd, I4B_MON_HANGUP_CODE);
+ 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);
+ write(monsock, cmd, I4B_MON_HANGUP_SIZE);
+ }
+ break;
+
+ case 9:
+ close(monsock);
+ exit(0);
+ break;
+
+ default:
+ print_menu();
+ break;
+ }
+}
+
+/*
+ * Display menu
+ */
+static void print_menu()
+{
+ printf("Menu: <1> display rights, <2> display monitor connections,\n");
+ printf(" <3> reread config file, <4> hangup \n");
+ printf(" <9> quit isdnmonitor\n");
+}
diff --git a/usr.sbin/i4b/isdnmonitor/monitor.h b/usr.sbin/i4b/isdnmonitor/monitor.h
new file mode 100644
index 0000000..ff192d0
--- /dev/null
+++ b/usr.sbin/i4b/isdnmonitor/monitor.h
@@ -0,0 +1,263 @@
+/*
+ * Copyright (c) 1998 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 protocl definition
+ * -----------------------------------------------
+ *
+ * $Id: monitor.h,v 1.5 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Mon Aug 3 06:52:06 1998]
+ *
+ * -mh created
+ * -hm checking in
+ * -hm ported to HPUX 10.10
+ *
+ *---------------------------------------------------------------------------*/
+
+#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_in32_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 BYTE */
+#define BYTE 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 1 /* release no */
+/*
+ * We intend to keep different versions of monitor client and isdnd
+ * interoperable as long as possible. We do not, however, even try
+ * to do this during early alpha or beta release phases. If you run
+ * developement versions at this stage, make sure all your clients
+ * and servers run the same version!
+ */
+
+/*
+ * 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+10
+#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_CLACCESS I4B_MON_EVNT_HDR+6 /* 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 */
+
+/* The client sets it's protocol version and event mask (usualy 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 a 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 a 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+4
+#define I4B_MON_HANGUP_CHANNEL I4B_MON_CMD_HDR+0 /* channel to drop */
+
+/* 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+16
+#define I4B_MON_CHRG_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: timestamp */
+#define I4B_MON_CHRG_CHANNEL I4B_MON_EVNT_HDR+4 /* 4 byte: channel charged */
+#define I4B_MON_CHRG_UNITS I4B_MON_EVNT_HDR+8 /* 4 byte: new charge value */
+#define I4B_MON_CHRG_ESTIMATED I4B_MON_EVNT_HDR+12 /* 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+12+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_CHANNEL I4B_MON_EVNT_HDR+8 /* 4 byte: channel connected */
+#define I4B_MON_CONNECT_CFGNAME I4B_MON_EVNT_HDR+12 /* name of config entry */
+#define I4B_MON_CONNECT_DEVNAME I4B_MON_EVNT_HDR+12+I4B_MAX_MON_STRING /* name of device used for connection */
+#define I4B_MON_CONNECT_REMPHONE I4B_MON_EVNT_HDR+12+2*I4B_MAX_MON_STRING /* remote phone no. */
+#define I4B_MON_CONNECT_LOCPHONE I4B_MON_EVNT_HDR+12+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+8
+#define I4B_MON_DISCONNECT_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_DISCONNECT_CHANNEL I4B_MON_EVNT_HDR+4 /* 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+12
+#define I4B_MON_UPDOWN_TSTAMP I4B_MON_EVNT_HDR+0 /* 4 byte: time stamp */
+#define I4B_MON_UPDOWN_CHANNEL I4B_MON_EVNT_HDR+4 /* 4 byte: channel disconnected */
+#define I4B_MON_UPDOWN_ISUP I4B_MON_EVNT_HDR+8 /* 4 byte: interface is up */
+
+/* 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) { ((BYTE*)(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) (((BYTE*)(r))[off])
+#define I4B_GET_2B(r, off) ((((BYTE*)(r))[off]) << 8) | (((BYTE*)(r))[off+1])
+#define I4B_GET_4B(r, off) ((((BYTE*)(r))[off]) << 24) | ((((BYTE*)(r))[off+1]) << 16) | ((((BYTE*)(r))[off+2]) << 8) | (((BYTE*)(r))[off+3])
+
+/* put a string into recor 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] = (BYTE)0; }
+
+#endif /* MONITOR_H */
+
diff --git a/usr.sbin/i4b/isdntel/Makefile b/usr.sbin/i4b/isdntel/Makefile
new file mode 100644
index 0000000..7528571
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG = isdntel
+SRCS = main.c display.c files.c alias.c
+DPADD = ${LIBNCURSES} ${LIBMYTINFO}
+LDADD = -lncurses -lmytinfo
+MAN8 = isdntel.8
+
+.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..95ca985
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.c
@@ -0,0 +1,139 @@
+/*
+ * 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.6 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:13 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;
+ char buffer[MAXBUFSZ + 1];
+ char number[MAXBUFSZ + 1];
+ char name[MAXBUFSZ + 1];
+ char *s, *d;
+ struct alias *newa = NULL;
+ struct alias *lasta = NULL;
+
+ if((fp = fopen(filename, "r")) == NULL)
+ fatal("cannot open aliasfile %s!", filename);
+
+ 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..7540ac0
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/alias.h
@@ -0,0 +1,49 @@
+/*
+ * 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.4 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:20 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..35d1b1e
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/defs.h
@@ -0,0 +1,156 @@
+/*
+ * 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.7 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:26 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>
+
+#define VERSION "1" /* version number */
+#define REL "11" /* release number */
+
+#define GOOD 0
+#define ERROR (-1)
+#define WARNING (-2)
+
+#define SPOOLDIR "/var/isdn"
+#define PLAYCMD "cat %s | alaw2ulaw >/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..8b837dd
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/display.c
@@ -0,0 +1,252 @@
+/*
+ * 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: display.c,v 1.5 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:32 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#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 */
+
+ if((COLS < 80) || (LINES < 24))
+ fatal(0, "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 %s.%s ", VERSION, REL);
+
+ 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_ready = 1;
+}
+
+/*---------------------------------------------------------------------------*
+ * 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..d5d6a76
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/files.c
@@ -0,0 +1,306 @@
+/*
+ * 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.7 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:38 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+1];
+
+ if(this == NULL)
+ return;
+
+ sprintf(buffer, "%s", this->fname);
+
+ 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+1];
+
+ if(this == NULL)
+ return;
+
+ sprintf(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..45d9de1
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/isdntel.8
@@ -0,0 +1,94 @@
+.\"
+.\" 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: [Sun Feb 14 10:19:44 1999]
+.\"
+.\" $Id: isdntel.8,v 1.7 1999/02/14 09:44:57 hm Exp $
+.\"
+.Dd July 11, 1998
+.Dt isdntel 8
+.Sh NAME
+.Nm isdntel
+.Nd isdn4bsd telephone answering management utility
+.Sh SYNOPSIS
+.Nm isdntel
+.Op Fl a Ar aliasfile
+.Op Fl d Ar spooldir
+.Op Fl p Ar playcommand
+.Op Fl t Ar timeout
+.Sh DESCRIPTION
+.Nm
+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
+.Em 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 isdnd 8
+.Xr isdnd.rc 5
+.Xr i4btel 4
+
+.Sh BUGS
+Still two or more left.
+
+.Sh AUTHOR
+The
+.Nm
+utility and this manual page were written by Hellmuth Michaelis (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..5f80440
--- /dev/null
+++ b/usr.sbin/i4b/isdntel/main.c
@@ -0,0 +1,395 @@
+/*
+ * 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.7 1999/02/14 09:44:57 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:19:50 1999]
+ *
+ *----------------------------------------------------------------------------*/
+
+#define MAIN
+#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;
+
+ extern char *optarg;
+
+ while ((i = getopt(argc, argv, "a:d:p:t:?")) != EOF)
+ {
+ 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 %s.%s)\n", VERSION, REL);
+ 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..b38f206
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/Makefile
@@ -0,0 +1,5 @@
+PROG = isdntelctl
+SRCS = main.c
+MAN8 = isdntelctl.8
+
+.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..945bfc2
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/isdntelctl.8
@@ -0,0 +1,87 @@
+.\"
+.\" 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.6 1999/02/16 10:40:18 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:20:38 1999]
+.\"
+.Dd December 14, 1998
+.Dt isdntelctl 8
+.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 R
+.Sh DESCRIPTION
+.Nm isdntelctl
+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
+Set sound format to A-Law.
+.It Fl U
+Set sound format to u-Law.
+.It Fl R
+Set sound format to canonical (regular) A-Law format.
+.El
+.Pp
+By default, isdn4bsd sends and receives audio in A-Law as provided by the
+hardware. However, these samples cannot easily be processed further as the
+sample word is bit-reversed. Both the u-Law and canonical A-Law formats
+store samples in a way suitable for post-processing by e.\ g. sox(1).
+.Pp
+.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 AUTHOR
+The
+.Nm
+utility and this manpage were written by Hellmuth Michaelis. He can be
+contacted at 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..aec72f6
--- /dev/null
+++ b/usr.sbin/i4b/isdntelctl/main.c
@@ -0,0 +1,224 @@
+/*
+ * 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.7 1999/02/16 10:40:18 hm Exp $
+ *
+ * last edit-date: [Tue Feb 16 11:32:09 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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_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_R = 0;
+
+/*---------------------------------------------------------------------------*
+ * program entry
+ *---------------------------------------------------------------------------*/
+int
+main(int argc, char **argv)
+{
+ int c;
+ int ret;
+ int telfd;
+ char namebuffer[128];
+
+ while ((c = getopt(argc, argv, "cgu:AUR?")) != EOF)
+ {
+ 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 'R':
+ opt_R = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(opt_get == 0 && opt_R == 0 && opt_U == 0 && opt_A == 0 && opt_C == 0)
+ {
+ opt_get = 1;
+ }
+
+ if((opt_get + opt_R + 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 uses A-Law sound format\n", namebuffer);
+ }
+ else if(format == CVT_ALAW2ULAW)
+ {
+ printf("device %s uses u-Law sound format\n", namebuffer);
+ }
+ else if(format == CVT_ALAW_CANON)
+ {
+ printf("device %s uses canonical A-Law sound format\n", namebuffer);
+ }
+ else
+ {
+ printf("ERROR, device %s uses unknown format %d!\n", namebuffer, format);
+ }
+ exit(0);
+ }
+
+ if(opt_A)
+ {
+ 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_U)
+ {
+ 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_R)
+ {
+ int format = CVT_ALAW_CANON;
+
+ 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 - i4b telephone i/f control, compiled %s %s\n",__DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntelctl -g -u <unit> -A -U -c\n");
+ fprintf(stderr, " -g get current settings\n");
+ fprintf(stderr, " -u unit specify unit number\n");
+ fprintf(stderr, " -A set interface to A-Law coding\n");
+ fprintf(stderr, " -U set interface to u-Law coding\n");
+ fprintf(stderr, " -R set interface to canonical (regular) A-Law coding\n");
+ fprintf(stderr, " -c clear input queue\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..c1240cc
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/Makefile
@@ -0,0 +1,8 @@
+PROG = isdntest
+SRCS = main.c
+MAN8 = isdntest.8
+
+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..d9c7cf7
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/isdntest.8
@@ -0,0 +1,103 @@
+.\"
+.\" 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.7 1999/02/14 09:44:58 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:21:18 1999]
+.\"
+.Dd July 9, 1998
+.Dt isdntest 8
+.Sh NAME
+.Nm isdntest
+.Nd isdn4bsd debugging and verification tool
+.Sh SYNOPSIS
+.Nm
+.Op Fl c Ar unit
+.Op Fl h
+.Op Fl i Ar number
+.Op Fl o Ar number
+.Op Fl w
+.Sh DESCRIPTION
+.Nm isdntest
+is part of the isdn4bsd package and may be used as a stimulation tool
+for debugging the isdn4bsd kernel functionality.
+.Pp
+NOTE:
+.Nm Isdntest
+must be run
+.Em instead
+of the
+.Xr isdnd 8
+daemon and can not 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 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 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
+.Nm Isdntest
+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.
+.Nm Isdntest
+has to be finished by the user by entering Control-C.
+.Pp
+.Sh FILES
+/dev/i4b
+
+.Sh AUTHOR
+The
+.Nm
+utility and this manpage were written by Hellmuth Michaelis. He can be
+contacted at 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..1ac2d91
--- /dev/null
+++ b/usr.sbin/i4b/isdntest/main.c
@@ -0,0 +1,738 @@
+/*
+ * 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.10 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:21:23 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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;
+
+/*---------------------------------------------------------------------------*
+ * 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?")) != EOF)
+ {
+ 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);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * usage display and exit
+ *---------------------------------------------------------------------------*/
+static void
+usage(void)
+{
+ fprintf(stderr, "\n");
+ fprintf(stderr, "isdntest - i4b selftest, compiled %s %s\n",__DATE__, __TIME__);
+ fprintf(stderr, "usage: isdntest -c <ctrl> -h -i <telno> -o <telno>\n");
+ fprintf(stderr, " -c <ctrl> specify controller to use\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, " -w wait for keyboard entry to disconnect\n");
+ fprintf(stderr, " -t num send test pattern num times\n");
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * 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();
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * 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))
+ {
+ 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:
+ 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);
+ }
+ }
+ 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..363d35d
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/1tr6.c
@@ -0,0 +1,754 @@
+/*
+ * 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.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:22:11 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..150c18d
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/Makefile
@@ -0,0 +1,8 @@
+PROG = isdntrace
+SRCS = q921.c q931.c q931_util.c q932_fac.c 1tr6.c trace.c \
+ pcause_1tr6.c pcause_q850.c
+#CFLAGS += -Wall -g
+MAN8 = isdntrace.8
+
+.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..fcee79b
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/cable.txt
@@ -0,0 +1,60 @@
+ *---------------------------------------------------------------------------
+ *
+ * Custom cable to trace an ISDN S0 bus with two passive (!) ISDN boards
+ * ---------------------------------------------------------------------
+ *
+ * $Id: cable.txt,v 1.3 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Thu Jan 22 19:57:50 1998]
+ *
+ * -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..a2c0ea6
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/isdntrace.8
@@ -0,0 +1,205 @@
+.\"
+.\" 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: isdntrace.8,v 1.10 1999/02/14 09:44:58 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:22:24 1999]
+.\"
+.Dd October 19, 1998
+.Dt isdntrace 8
+.Sh NAME
+.Nm isdntrace
+.Nd isdn4bsd ISDN protocol trace utility
+.Sh SYNOPSIS
+.Nm isdntrace
+.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 B
+.Op Fl F
+.Op Fl P
+.Op Fl R Ar unit
+.Op Fl T Ar unit
+.Sh DESCRIPTION
+.Nm isdntrace
+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
+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 octetts 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 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
+
+.Nm Isdntrace
+automatically detects the layer 3 protocol being used by looking at the
+Protocol Discriminator (see: Q.931/1993 pp. 53).
+.Pp
+
+
+.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
+/tmp/isdn.trace.
+
+.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 AUTHOR
+The
+.Nm
+utility was written by Gary Jennejohn and Hellmuth Michaelis.
+.Pp
+This manual page was written by Hellmuth Michaelis, he can be reached
+at hm@kts.org.
+
diff --git a/usr.sbin/i4b/isdntrace/pcause_1tr6.c b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
new file mode 100644
index 0000000..4c95ba7
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.c
@@ -0,0 +1,164 @@
+/*
+ * 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.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:22:30 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..2702f57
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_1tr6.h
@@ -0,0 +1,68 @@
+/*
+ * 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.4 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:22:36 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..4d5f02d
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.c
@@ -0,0 +1,328 @@
+/*
+ * 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.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:22:42 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..aff6199
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/pcause_q850.h
@@ -0,0 +1,109 @@
+/*
+ * 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.4 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:22:48 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..294e093
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q921.c
@@ -0,0 +1,275 @@
+/*
+ * 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.3 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Thu Apr 16 15:38:34 1998]
+ *
+ * -hm splitting
+ * -hm printing to buffer
+ * -hm slightly reformatted TEI management proc output
+ * -hm minor fixes
+ * -hm fixing response/command
+ * -hm fixing count off by one
+ * -hm dump only Q.921 part of frame
+ * -hm cleanup
+ *
+ *---------------------------------------------------------------------------*/
+
+#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..40f7fc3
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931.c
@@ -0,0 +1,783 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931.c - print Q.931 traces
+ * ---------------------------
+ *
+ * $Id: q931.c,v 1.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:03 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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), ",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);
+
+ /* 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 (Q.932): ");
+ 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 (Q.932): ");
+ 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 ind: ");
+ 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: ");
+ 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: ");
+ break;
+ case 0x76:
+ sprintf((pbuf+strlen(pbuf)), "[redirection number: ");
+ 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: ");
+ 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..827b7fb
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q931_util.c
@@ -0,0 +1,697 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q931_util.c - utility functions to print Q.931 traces
+ * -----------------------------------------------------
+ *
+ * $Id: q931_util.c,v 1.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:10 1999]
+ *
+ *---------------------------------------------------------------------------*/
+
+#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;
+}
+
+/* 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..cedd8be
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.c
@@ -0,0 +1,926 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.c - decode Q.932 facilities
+ * ------------------------------------
+ *
+ * $Id: q932_fac.c,v 1.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:16 1999]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - 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 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_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 },
+
+/* 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/isdntrace/q932_fac.h b/usr.sbin/i4b/isdntrace/q932_fac.h
new file mode 100644
index 0000000..4d13107
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/q932_fac.h
@@ -0,0 +1,174 @@
+/*
+ * 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.
+ *
+ *---------------------------------------------------------------------------
+ *
+ * q932_fac.h - facility header file
+ * ---------------------------------
+ *
+ * $Id: q932_fac.h,v 1.5 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:22 1999]
+ *
+ *---------------------------------------------------------------------------
+ *
+ * - 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_NIX
+};
+
+/* EOF */
+
diff --git a/usr.sbin/i4b/isdntrace/trace.c b/usr.sbin/i4b/isdntrace/trace.c
new file mode 100644
index 0000000..77a9523
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.c
@@ -0,0 +1,809 @@
+/*
+ * Copyright (c) 1996, 1999 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.11 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:28 1999]
+ *
+ * -hm rewriting for isic and new trace format
+ * -hm new option -f, use automatic name for -o
+ * -hm changed default option setting
+ * -hm multi unit support
+ * -hm analyzer functionality
+ * -hm binary record/playback
+ * -hm -p option
+ * -hm cleanup
+ * -hm adding date to timestamp field
+ * -hm reopen files on SIGUSR1 for rotation
+ * -hm Joerg reported a bug with commandline options
+ * -hm I.430 INFO signals from layer 1
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "trace.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;
+
+static char outfilename[1024];
+static char BPfilename[1024];
+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 );
+
+/*---------------------------------------------------------------------------*
+ * 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," -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," -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[])
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+ char devicename[80];
+ char headerbuf[256];
+
+ int n;
+ int c;
+ char *b;
+
+ int enable_trace = TRACE_D_RX | TRACE_D_TX;
+ 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:BFPR:T:?")) != EOF)
+ {
+ 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 '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);
+
+ if((BP = fopen(BPfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(BP);
+ sprintf(buffer, "%s%s", BPfilename, TRACE_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);
+ }
+ 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);
+
+
+ if((Fout = fopen(outfilename, "r")) != NULL)
+ {
+ char buffer[1024];
+ fclose(Fout);
+ sprintf(buffer, "%s%s", outfilename, TRACE_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
+ {
+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]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ usleep(250000);
+ clearerr(BP);
+
+ if(stat(BPfilename, &fstnew) != -1)
+ {
+ if((fst.st_ino != fstnew.st_ino) ||
+ (fstnew.st_nlink == 0))
+ {
+ if((BP = freopen(BPfilename, "r", BP)) == NULL)
+ {
+ char buffer[80];
+ sprintf(buffer, "Error reopening file [%s]", BPfilename);
+ perror(buffer);
+ exit(1);
+ }
+ stat(BPfilename, &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]", 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) && (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(&(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.%6u ",
+ ((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.%6u - 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.%6u - 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 */
+ 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;
+
+ default:
+ decode_q931(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);
+
+ 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);
+ }
+ }
+}
+
+
+/* EOF */
diff --git a/usr.sbin/i4b/isdntrace/trace.h b/usr.sbin/i4b/isdntrace/trace.h
new file mode 100644
index 0000000..3f00bde
--- /dev/null
+++ b/usr.sbin/i4b/isdntrace/trace.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1996, 1999 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.8 1999/02/14 09:44:58 hm Exp $
+ *
+ * last edit-date: [Sun Feb 14 10:23:35 1999]
+ *
+ * -hm splitting
+ * -hm new filenames
+ *
+ *---------------------------------------------------------------------------*/
+
+#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 <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 TRACE_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 */
+
+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_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);
+
+/* EOF */
diff --git a/usr.sbin/i4b/man/Makefile b/usr.sbin/i4b/man/Makefile
new file mode 100644
index 0000000..65248d6
--- /dev/null
+++ b/usr.sbin/i4b/man/Makefile
@@ -0,0 +1,5 @@
+MAN4 = i4b.4 i4bctl.4 i4bipr.4 i4bq921.4 i4bq931.4 i4brbch.4 i4btel.4 \
+ i4btrc.4 isic.4 daic.4 i4bisppp.4
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/i4b/man/daic.4 b/usr.sbin/i4b/man/daic.4
new file mode 100644
index 0000000..fc04797
--- /dev/null
+++ b/usr.sbin/i4b/man/daic.4
@@ -0,0 +1,98 @@
+.\" Copyright (c) 1997 Martin Husemann <martin@rumolt.teuto.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. The name of the author may not be used to endorse or promote products
+.\" derived from this software withough 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.
+.\"
+.\" $Id: daic.4,v 1.1 1998/02/03 12:43:07 hm Exp $
+.\"
+.\" last edit-date: [Fri Jan 30 22:49:48 1998]
+.\"
+.\" -mh writing manual pages
+.\"
+.\"
+.Dd January 30, 1998
+.Dt daic 4
+.Sh NAME
+.Nm daic
+.Nd isdn4bsd driver for EICON.Diehl active isdn cards
+.Pp
+The
+.Nm
+driver supports the old Diehl active cards:
+.Em S, SX, SXn, SCOM
+and
+.Em QUADRO.
+.Sh SYNOPSIS
+.Pp
+Use a config line like this
+.Cd "daic0 at isa? iomem 0xd8000 irq 10"
+.Pp
+For a
+.Em QUADRO
+card use the same, the driver will detect the board type and use
+all four ports, each attached as a controller of its own to the
+ISDN4BSD system, which can be listed using the isdnctl utility.
+.Sh DESCRIPTION
+The
+.Nm
+driver interfaces the isdn card to the ISDN4BSD kernel subsystem.
+All lower layer isdn control is handled by the card. This should
+allow you to run any national isdn protocol delivered by EICON.Diehl
+for your card, but the driver has only been tested with the DSS1
+protocol and some parts of the cards interface are isdn protocol
+dependend.
+.Pp
+The
+.Nm
+driver is written to conform to the software interface documented
+by Diehl in their
+.Nm ISDN-Karten Benutzerhandbuch
+from 1992.
+.Sh MICROCODE DOWNLOAD
+Every active card needs its own operating software before it can
+work. You have to download this to the card before using it with
+ISDN4BSD. Use the isdnctl utility to do this, i.e. call
+.Nm "isdnctl -d te_etsi.sx 1"
+to download the file
+.Nm te_etsi.sx
+to controller number 1. Use
+.Nm "isdnctl -l"
+to list all available controllers (and ports). You have to select the
+correct isdn protocol file for your isdn interface, see the Diehl documentation
+for details.
+.Pp
+The cards bootstrap process involves another file, which is independend
+of the card type you use and the protocol you run. It is called
+.Nm download.bin
+in current versions of the Diehl software distribution and has to be
+copied to the kernel compile directory under
+.Nm dev/ic/microcode/daic
+and converted into a header file used when compiling the kernel by running
+.Nm make
+in that directory. Your kernel compile will fail and remind you of this
+if you forget to do this. Due to copright restrictions we cannot distribute
+the driver with this file integrated. But if you own a card, you do have
+the file (or can get it from the Diehl web server).
+.Sh BUGS
+The driver is not yet finished. It will only compile on NetBSD and
+even there will not work. This should be fixed soon and the driver
+will be ported to FreeBSD.
+.Sh SEE ALSO
+.Xr isdnctl 1
diff --git a/usr.sbin/i4b/man/i4b.4 b/usr.sbin/i4b/man/i4b.4
new file mode 100644
index 0000000..09288bc
--- /dev/null
+++ b/usr.sbin/i4b/man/i4b.4
@@ -0,0 +1,108 @@
+.\"
+.\" 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: i4b.4,v 1.7 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:28 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4b 4
+.Sh NAME
+.Nm i4b
+.Nd isdn4bsd call control ISDN driver
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4b\&"
+.Sh NetBSD SYNOPSIS
+none
+.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.
+.Pp
+The messages and message parameters are documented in the include
+file
+.Em /usr/include/machine/i4b_ioctl.h .
+.Pp
+The 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
+.Pp
+.Sh SEE ALSO
+.Xr isdnd 8
+.Sh AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4bctl.4 b/usr.sbin/i4b/man/i4bctl.4
new file mode 100644
index 0000000..97ecba0
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bctl.4
@@ -0,0 +1,50 @@
+.\"
+.\" 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: i4bctl.4,v 1.5 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:34 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4bctl 4
+.Sh NAME
+.Nm i4bctl
+.Nd control device for the isdn4bsd kernel part
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4bctl\&"
+.Sh NetBSD SYNOPSIS
+none
+.Sh DESCRIPTION
+.Nm
+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 AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4bipr.4 b/usr.sbin/i4b/man/i4bipr.4
new file mode 100644
index 0000000..75a60bb
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bipr.4
@@ -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: i4bipr.4,v 1.9 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:40 1999]
+.\"
+.Dd July 6, 1998
+.Dt i4bipr 4
+.Sh NAME
+.Nm i4bipr
+.Nd isdn4bsd IP over ISDN B-channel network driver
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4bipr\&" Op count
+.Sh NetBSD SYNOPSIS
+none
+.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 isdnd 8
+.Xr isdnd.rc 5
+.Xr bpf 4
+.Xr tcpdump 1
+.Sh AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4bisppp.4 b/usr.sbin/i4b/man/i4bisppp.4
new file mode 100644
index 0000000..5c690fc
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bisppp.4
@@ -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: i4bisppp.4,v 1.10 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:45 1999]
+.\"
+.Dd December 22, 1998
+.Dt i4bisppp 4
+.Sh NAME
+.Nm i4bisppp
+.Nd isdn4bsd synchronous PPP over ISDN B-channel network driver
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4bisppp\&" Op count
+.Sh NetBSD SYNOPSIS
+none
+.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 Serge Vakulenko's sppp
+backend and the ISDN4BSD package.
+.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
+The
+.Xr spppcontrol 8
+utility is used to configure all aspects of PPP required to connect to a
+remote site.
+.Sh LINK0 and LINK1
+The
+.Em link0
+and
+.Em link1
+flags given as parameters to
+.Xr ifconfig 8
+have the following meaning for the
+.Nm isp
+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,
+.Pp
+.Sh SEE ALSO
+.Xr isdnd 8
+.Xr isdnd.rc 5
+.Xr spppcontrol 8
+.Xr sppp 4
+.Xr bpf 4
+.Xr tcpdump 1
+.Sh AUTHOR
+The
+.Nm
+device driver was written by Joerg Wunsch and then added to ISDN4BSD by Gary Jennejohn.
+This manpage was written by Hellmuth Michaelis. He can be contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4bq921.4 b/usr.sbin/i4b/man/i4bq921.4
new file mode 100644
index 0000000..b39c4f1
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq921.4
@@ -0,0 +1,49 @@
+.\"
+.\" 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: i4bq921.4,v 1.6 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:51 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4bq921 4
+.Sh NAME
+.Nm i4bq921
+.Nd isdn4bsd pseudo device driver handling the Q.921 protocol
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4bq921\&"
+.Sh NetBSD SYNOPSIS
+none
+.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 AUTHOR
+The
+.Nm
+driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4bq931.4 b/usr.sbin/i4b/man/i4bq931.4
new file mode 100644
index 0000000..f34f600
--- /dev/null
+++ b/usr.sbin/i4b/man/i4bq931.4
@@ -0,0 +1,49 @@
+.\"
+.\" 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: i4bq931.4,v 1.6 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:36:57 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4bq931 4
+.Sh NAME
+.Nm i4bq931
+.Nd isdn4bsd pseudo device driver handling the Q.931 protocol
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4bq931\&"
+.Sh NetBSD SYNOPSIS
+none
+.Sh DESCRIPTION
+.Nm
+is the ISDN D channel layer 2 handler.
+.Sh STANDARDS
+ITU Recommendation Q.930 and Q.931
+.Sh SEE ALSO
+.Xr i4bq921 4
+.Sh AUTHOR
+The
+.Nm
+driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4brbch.4 b/usr.sbin/i4b/man/i4brbch.4
new file mode 100644
index 0000000..1724727
--- /dev/null
+++ b/usr.sbin/i4b/man/i4brbch.4
@@ -0,0 +1,50 @@
+.\"
+.\" 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: i4brbch.4,v 1.6 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:37:03 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4brbch 4
+.Sh NAME
+.Nm i4brbch
+.Nd isdn4bsd ISDN Raw B-CHannel access driver
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4brbch\&" Op count
+.Sh NetBSD SYNOPSIS
+none
+.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 8
+.Xr isdnd.rc 5
+.Sh AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4btel.4 b/usr.sbin/i4b/man/i4btel.4
new file mode 100644
index 0000000..54518aa
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btel.4
@@ -0,0 +1,52 @@
+.\"
+.\" 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: i4btel.4,v 1.6 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:37:09 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4btel 4
+.Sh NAME
+.Nm i4btel
+.Nd isdn4bsd ISDN B-channel telephony interface driver
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4btel\&" Op count
+.Sh NetBSD SYNOPSIS
+none
+.Sh DESCRIPTION
+The
+.Nm
+driver provides an interface to the 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.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Xr isdnd.rc 5
+.Sh AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/i4btrc.4 b/usr.sbin/i4b/man/i4btrc.4
new file mode 100644
index 0000000..f41a882
--- /dev/null
+++ b/usr.sbin/i4b/man/i4btrc.4
@@ -0,0 +1,52 @@
+.\"
+.\" 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: i4btrc.4,v 1.6 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:37:15 1999]
+.\"
+.Dd February 3, 1998
+.Dt i4btrc 4
+.Sh NAME
+.Nm i4btrc
+.Nd isdn4bsd ISDN interface driver for D and B channel tracing
+.Sh FreeBSD SYNOPSIS
+.Cd pseudo-device \&"i4btrc\&" Op count
+.Sh NetBSD SYNOPSIS
+none
+.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
+utility.
+.Sh SEE ALSO
+.Xr isdnd 8
+.Xr isdntrace 8
+.Sh AUTHOR
+The
+.Nm
+device driver and this manpage were written by Hellmuth Michaelis. He can be
+contacted at hm@kts.org.
diff --git a/usr.sbin/i4b/man/isic.4 b/usr.sbin/i4b/man/isic.4
new file mode 100644
index 0000000..6fa565e
--- /dev/null
+++ b/usr.sbin/i4b/man/isic.4
@@ -0,0 +1,379 @@
+.\"
+.\" 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: isic.4,v 1.16 1999/02/14 09:45:02 hm Exp $
+.\"
+.\" last edit-date: [Sun Feb 14 10:37:21 1999]
+.\"
+.Dd December 22, 1998
+.Dt isic 4
+.Sh NAME
+.Nm isic
+.Nd isdn4bsd Siemens ISDN Chipset device driver
+.Sh FreeBSD SYNOPSIS
+.Pp
+For a Teles S0/8 or Niccy 1008 card:
+.Cd options \&"TEL_S0_8\&"
+.Cd "device isic0 at isa? iomem 0xd0000 net irq 5 flags 1 vector isicintr"
+.Pp
+For a Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 card:
+.Cd options \&"TEL_S0_16\&"
+.Cd "device isic0 at isa? port 0xd80 iomem 0xd0000 net irq 5 flags 2 vector isicintr"
+.Pp
+For a Teles S0/16.3 card:
+.Cd options \&"TEL_S0_16_3\&"
+.Cd "device isic0 at isa? port 0xd80 net irq 5 flags 3 vector isicintr"
+.Pp
+For an AVM A1 or AVM Fritz!Card classic:
+.Cd options \&"AVM_A1\&"
+.Cd "device isic0 at isa? port 0x340 net irq 5 flags 4 vector isicintr"
+.Pp
+For an AVM Fritz!Card PCMCIA:
+.Cd options \&"AVM_A1_PCMCIA\&"
+.Cd "device isic0 at isa? port 0x340 net irq 5 flags 10 vector isicintr"
+.Pp
+For a Teles S0/16.3 PnP card (PnP):
+.Cd options \&"TEL_S0_16_3_P\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For a Creatix ISDN-S0 P&P card (PnP):
+.Cd options \&"CRTX_S0_P\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For an USRobotics Sportster ISDN TA internal or Stollmann Tina-pp card:
+.Cd options \&"USR_STI\&"
+.Cd "device isic0 at isa? port 0x268 net irq 5 flags 7 vector isicintr"
+.Pp
+For an ITK micro ix1 card:
+.Cd options \&"ITKIX1\&"
+.Cd "device isic0 at isa? port 0x398 net irq 10 flags 18 vector isicintr"
+.Pp
+For a Dr. Neuhaus Niccy Go@ (PnP):
+.Cd options \&"DRN_NGO\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For a Sedlbauer Win Speed card (PnP):
+.Cd options \&"SEDLBAUER\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For a Dynalink IS64PH (PnP):
+.Cd options \&"DYNALINK\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For an ELSA QuickStep 1000pro ISA (PnP):
+.Cd options \&"ELSA_QS1ISA\&"
+.Cd "device isic0 at isa? port ? net irq ? vector isicintr"
+.Pp
+For an ELSA QuickStep 1000pro PCI:
+.Cd options \&"ELSA_QS1PCI\&"
+.Cd "device isic0"
+.Pp
+.Ar FreeBSD PnP configuration:
+.Pp
+To be able to use PnP cards under FreeBSD, you have to add
+.Pp
+.Cd controller pnp0
+.Pp
+to you kernel config file. More, it is recommended to add
+.Pp
+.Cd options \&"USERCONFIG\&"
+.Pp
+to your kernel config file to be able to adjust your PnP configuration
+in case of trouble.
+.Pp
+See also:
+.Xr pnp 4
+and
+.Xr boot 8
+.Pp
+.Sh NetBSD SYNOPSIS
+On the ISA bus:
+.Pp
+For a Teles S0/8 or Niccy 1008 card:
+.Cd options \&"TEL_S0_8\&"
+.Cd "isic0 at isa? iomem 0xd0000 irq 5"
+.Pp
+For a Teles S0/16 or Creatix ISDN-S0 or Niccy 1016 card:
+.Cd options \&"TEL_S0_16\&"
+.Cd "isic0 at isa? port 0xd80 iomem 0xd0000 irq 5"
+.Pp
+For a Teles S0/16.3 card:
+.Cd options \&"TEL_S0_16_3\&"
+.Cd "isic0 at isa? port 0xd80 irq 5"
+.Pp
+For an AVM A1 or AVM Fritz card:
+.Cd options \&"AVM_A1\&"
+.Cd "isic0 at isa? port 0x340 irq 5"
+.Pp
+For an USRobotics Sportster ISDN TA internal or Stollmann Tina-pp card:
+.Cd options \&"USR_STI\&"
+.Cd "isic0 at isa? port 0x268 irq 5"
+.Pp
+For an ITK ix1 micro card:
+.Cd options \&"ITKIX1\&"
+.Cd "isic0 at isa? port 0x398 irq 10"
+.Pp
+On the ISAPNP bus:
+.Pp
+For a Teles S0/16.3 PnP card
+.Cd options \&"TEL_S0_16_3_P\&"
+.Cd "isic* at isapnp?"
+.Pp
+For a Creatix ISDN-S0 P&P card
+.Cd options \&"CRTX_S0_P\&"
+.Cd "isic* at isapnp?"
+.Pp
+For a Dr. Neuhaus Niccy GO@
+.Cd options \&"DRN_NGO\&"
+.Cd "isic* at isapnp?"
+.Pp
+For an ELSA QuickStep 1000pro (ISA version):
+.Cd options \&"ELSA_QS1ISA\&"
+.Cd "isic* at isapnp?"
+.Pp
+For a Sedlbauer WinSpeed:
+.Cd options \&"SEDLBAUER\&"
+.Cd "isic* at isapnp?"
+.Pp
+For a Dynalink IS64PH:
+.Cd options \&"DYNALINK\&"
+.Cd "isic* at isapnp?"
+.Pp
+Cards on the PCI bus:
+.Pp
+For an ELSA QuickStep 1000pro (PCI version)
+.Cd options \&"ELSA_QS1PCI\&"
+.Cd "isic* at pci?"
+.Pp
+Cards on the PCMCIA or PCCARD bus:
+.Pp
+For an AVM Fritz!Card PCMCIA
+.Cd options \&"AVM_PCMCIA\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+For an ELSA MicroLink ISDN/MC
+.Cd options \&"ELSA_ISDNMC\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+For an ELSA MicroLink MC/all
+.Cd options \&"ELSA_MCALL\&"
+.Cd "isic* at pcmcia? function ?"
+.Pp
+.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 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.
+.Pp
+.Sh SUPPORTED CARDS
+.Pp
+.Bl -tag -width Ds -compact -offset
+.It Ar Teles S0/8, Dr. Neuhaus Niccy 1008, Creatix ISDN-S0/8
+.Pp
+The required (optional for NetBSD)
+.Em flag
+value is 1.
+.Pp
+Notice that this cards must not have a
+.Em port
+value in the config line.
+.Pp
+Valid interrupts are 2, 3, 4, 5, 6 and 7.
+.Pp
+The i/o ports are memory mapped and the memory start address may
+be in the range 0xA0000 to 0xDF000 and uses 4kB of memory.
+.Pp
+.It Ar Teles S0/16, Creatix ISDN-S0, Dr. Neuhaus Niccy 1016
+.Pp
+The required (optional under NetBSD)
+.Em flag
+value is 2.
+.Pp
+These boards have a jumper which specifies an i/o base address 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 are 2, 3, 4, 5, 10, 11, 12 or 15.
+.Pp
+Valid memory start
+addresses 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 (optional under NetBSD)
+.Em flag
+value is 3.
+.Pp
+This card is completely i/o mapped and must not have an
+.Em iomem
+statement in the config line.
+.Pp
+Valid interrupts are 2, 5, 9, 10, 12 or 15.
+.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, AVM Fritz!Card
+.Pp
+The required (optional under NetBSD)
+.Em flag
+value is 4.
+.Pp
+These boards have a jumper which specifies an i/o base address of either
+0x200, 0x240, 0x300 or 0x340.
+.Pp
+Valid interrupt configurations 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
+This card is completely i/o mapped and must not have an
+.Em iomem
+statement in the config line.
+.Pp
+.It Ar Teles S0/16.3 PnP
+.Pp
+Possible i/o port values are 0x580, 0x500 and 0x680.
+Possible interrupt configurations are 3, 5, 7, 10, 11 and 12.
+.Pp
+The the card is auto-configured by the PnP kernel subsystem.
+.Pp
+.It Ar Creatix ISDN-S0 P&P
+.Pp
+Valid i/o port values are 0x120, 0x180 and 0x100.
+.Pp
+Valid interrupt configurations are 3, 5, 7, 10, 11 and 12.
+.Pp
+The card is auto-configured by the PnP kernel subsystem.
+.Pp
+.It Ar "3Com USRobotics Sportster ISDN TA intern and Stollmann Tina pp"
+.Pp
+The required (optional for NetBSD)
+.Em flag
+value is 7.
+.Pp
+Valid i/o port values are 0x200, 0x208, 0x210, 0x218, 0x220, 0x228, 0x230,
+0x238, 0x240, 0x248, 0x250, 0x258, 0x260, 0x268, 0x270 and 0x278.
+.Pp
+Valid interrupt configurations are 5, 7, 10, 11, 12, 14, 15.
+.Pp
+Notice: this card has a strange address decoding scheme resulting in 64
+windows of some bytes length. Anyway, support for this card is good because
+the manufacturer gave out technical docs for this card!
+.Pp
+.Pp
+.It Ar "Dr. Neuhaus Niccy Go@"
+.Pp
+Valid i/o port values must be in the range 0x200 ... 0x3e0.
+.Pp
+Valid interrupt configurations are 3, 4, 5, 9, 10, 11, 12, 15.
+.Pp
+The card is auto-configured by the PnP kernel subsystem.
+.Pp
+.It Ar "Sedlbauer Win Speed"
+.Pp
+Valid i/o port values must be in the range 0x100 ... 0x3f0. (alignment 0x8,
+len 0x8)
+.Pp
+Valid interrupt configurations are 3, 4, 5, 7, 10, 11, 12, 13, 15.
+.Pp
+The card is auto-configured by the PnP kernel subsystem.
+.Em FreeBSD:
+This card is PnP only, and so it can be configured using USERCONFIG
+('man 4 pnp'). This can be done via
+.Em /kernel.config
+\&. For example:
+.Pp
+.Cd USERCONFIG
+.Cd pnp 1 0 os enable port0 0x270 irq0 10
+.Cd quit
+.Pp
+.Pp
+.It Ar "ELSA QuickStep 1000pro (ISA)"
+.Pp
+I/O port in the range 0x160 ... 0x360 (occupies 8 bytes).
+.Pp
+Valid interrupt configurations are 3, 4, 5, 7, 10, 11, 12, 15.
+.Pp
+The card is auto-configured by the PnP kernel subsystem.
+.Pp
+.Pp
+.It Ar "ELSA QuickStep 1000pro-PCI"
+.Pp
+The card is auto-configured by the PCI kernel subsystem.
+.Pp
+.Pp
+.It Ar "ITK ix1 micro"
+.Pp
+The required (optional under NetBSD)
+.Em flag
+value is 18.
+.Pp
+Valid i/o port values must be in the range (<unknown>).
+.Pp
+Valid interrupt configurations are (<unknown>).
+.Pp
+.Sh CAVEATS
+Note that all of the 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.
+.Pp
+.Sh BUGS
+Since there is no hardware documentation available from several manufacturers
+for their boards, it is likely that there are many, many bugs left.
+
+.Sh STANDARDS
+CCITT Recommendation I.430
+
+.Sh SEE ALSO
+.Xr i4bq921 4
+.Xr i4bq931 4
+
+.Sh AUTHOR
+The
+.Nm
+driver and this manpage were written by Hellmuth Michaelis. It is based
+on earlier work of Arne Helme, Andrew Gordon and Gary Jennejohn. The author
+can be contacted at hm@kts.org.
+.Pp
+The complete porting to and maintenance of NetBSD was done by Martin Husemann.
+He can be contacted at martin@rumolt.teuto.de
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile
new file mode 100644
index 0000000..1c671fc
--- /dev/null
+++ b/usr.sbin/inetd/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= inetd
+MAN8= inetd.8
+MLINKS= inetd.8 inetd.conf.5
+
+COPTS+= -Wall -DLOGIN_CAP -DLIBWRAP
+#COPTS+= -DSANITY_CHECK
+
+DPADD+= ${LIBUTIL} ${LIBWRAP}
+LDADD+= -lutil -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8
new file mode 100644
index 0000000..55c2461
--- /dev/null
+++ b/usr.sbin/inetd/inetd.8
@@ -0,0 +1,542 @@
+.\" 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
+.\" $Id: inetd.8,v 1.22 1998/06/10 12:34:25 phk Exp $
+.\"
+.Dd February 7, 1996
+.Dt INETD 8
+.Os BSD 4.4
+.Sh NAME
+.Nm inetd
+.Nd internet
+.Dq super-server
+.Sh SYNOPSIS
+.Nm inetd
+.Op Fl d
+.Op Fl l
+.Op Fl c Ar maximum
+.Op Fl C Ar rate
+.Op Fl a Ar address
+.Op Fl p Ar filename
+.Op Fl R Ar rate
+.Op Ar configuration file
+.Sh DESCRIPTION
+The
+.Nm
+program
+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.
+.It Fl c Ar maximum
+Specify the default maximum number of services that can be invoked.
+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.
+.It Fl a
+Specify a specific IP address to bind to.
+.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 ``#'' 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]]
+user[:group][/login-class]
+server program
+server program arguments
+.Ed
+.Pp
+To specify an
+.No 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
+BSD-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 .
+For
+.Dq internal
+services (discussed below), the service
+name
+.Em must
+be the official name of the service (that is, the first entry in
+.Pa /etc/services ) .
+When used to specify an
+.No 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 as given in
+.Pa /etc/protocols .
+Examples might be
+.Dq tcp
+or
+.Dq udp .
+If it is desired that the service is reachable via T/TCP, one should
+specify
+.Dq tcp/ttcp .
+Rpc based services are specified with the
+.Dq rpc/tcp
+or
+.Dq rpc/udp
+service type.
+TCPMUX services must use
+.Dq tcp .
+.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 received 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 .
+.Xr Comsat 8 ,
+.Pq Xr biff 1
+and
+.Xr talkd 8
+are both examples of the latter type of
+datagram server.
+.Xr Tftpd 8
+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 inetd ,
+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 ``threads'')
+for a ``nowait'' service may be explicitly specified by appending a
+``/'' followed by the number to the ``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 ``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 ``/'' 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.
+.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 ``:'' allows to specify group name different
+than default group for this user.
+Optional
+.Em login-class
+part separated by ``/'' allows to specify login class different
+than default ``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
+word
+.Dq internal
+should take the place of this entry.
+.Pp
+The
+.Nm
+program
+provides several
+.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 an
+.Xr accept 2
+is made, which notes the
+service selected and the IP-number of the remote requestor.
+.Pp
+The
+.Nm
+program
+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.
+.Pp
+Support is provided for tcp_wrappers; see the relevant documentation. The
+.Pa tcpd
+daemon is not required.
+.Sh TCPMUX
+.Pp
+.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 ``+'',
+.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 .
+.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 inetd .
+.El
+.Sh "EXAMPLES"
+.Pp
+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
+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
+.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
+program attempted to renounce the privileged state associated with a
+socket but was unable to.
+.El
+.Sh SEE ALSO
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr rpc 5 ,
+.Xr services 5 ,
+.Xr comsat 8 ,
+.Xr fingerd 8 ,
+.Xr ftpd 8 ,
+.Xr portmap 8 ,
+.Xr rexecd 8 ,
+.Xr rlogind 8 ,
+.Xr rshd 8 ,
+.Xr telnetd 8 ,
+.Xr tftpd 8 ,
+.Xr hosts_access 5 ,
+.Xr hosts_options 5
+.Sh HISTORY
+The
+.Nm
+command 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.
diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c
new file mode 100644
index 0000000..f7cd1d5
--- /dev/null
+++ b/usr.sbin/inetd/inetd.c
@@ -0,0 +1,2106 @@
+/*
+ * 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
+static const char rcsid[] =
+ "$Id: inetd.c,v 1.46 1999/01/05 11:56:35 danny Exp $";
+#endif /* not lint */
+
+/*
+ * 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 being with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services or must
+ * name a tcpmux service
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * 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 must be in /etc/protocols
+ * 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.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <sysexits.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
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+
+/* see init.c */
+#define RESOURCE_RC "daemon"
+
+#endif
+
+#include "pathnames.h"
+
+#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
+
+#define TOOMANY 256 /* don't start more than TOOMANY */
+#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))
+
+int debug = 0;
+int log = 0;
+int nsock, maxsock;
+fd_set allsock;
+int options;
+int timingout;
+int toomany = TOOMANY;
+int maxchild = MAXCPM;
+int maxcpm = MAXCHILD;
+struct servent *sp;
+struct rpcent *rpc;
+struct in_addr bind_address;
+int signalpipe[2];
+
+struct servtab {
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ 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 */
+#define MAXARGV 20
+ char *se_argv[MAXARGV+1]; /* program arguments */
+ int se_fd; /* open descriptor */
+ struct sockaddr_in se_ctrladdr;/* bound address */
+ 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;
+} *servtab;
+
+#define NORM_TYPE 0
+#define MUX_TYPE 1
+#define MUXPLUS_TYPE 2
+#define TTCP_TYPE 3
+#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)
+
+
+void chargen_dg __P((int, struct servtab *));
+void chargen_stream __P((int, struct servtab *));
+void close_sep __P((struct servtab *));
+void flag_signal __P((char));
+void flag_config __P((int));
+void config __P((void));
+void daytime_dg __P((int, struct servtab *));
+void daytime_stream __P((int, struct servtab *));
+void discard_dg __P((int, struct servtab *));
+void discard_stream __P((int, struct servtab *));
+void echo_dg __P((int, struct servtab *));
+void echo_stream __P((int, struct servtab *));
+void endconfig __P((void));
+struct servtab *enter __P((struct servtab *));
+void freeconfig __P((struct servtab *));
+struct servtab *getconfigent __P((void));
+void ident_stream __P((int, struct servtab *));
+void machtime_dg __P((int, struct servtab *));
+void machtime_stream __P((int, struct servtab *));
+char *newstr __P((char *));
+char *nextline __P((FILE *));
+void print_service __P((char *, struct servtab *));
+void addchild __P((struct servtab *, int));
+void flag_reapchild __P((int));
+void reapchild __P((void));
+void enable __P((struct servtab *));
+void disable __P((struct servtab *));
+void flag_retry __P((int));
+void retry __P((void));
+int setconfig __P((void));
+void setup __P((struct servtab *));
+char *sskip __P((char **));
+char *skip __P((char **));
+struct servtab *tcpmux __P((int));
+int cpmip __P((struct servtab *, int));
+
+void unregisterrpc __P((register struct servtab *sep));
+
+struct biltin {
+ 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 (default) */
+ void (*bi_fn)(); /* function which performs it */
+} biltins[] = {
+ /* Echo received data */
+ { "echo", SOCK_STREAM, 1, 0, echo_stream },
+ { "echo", SOCK_DGRAM, 0, 0, echo_dg },
+
+ /* Internet /dev/null */
+ { "discard", SOCK_STREAM, 1, 0, discard_stream },
+ { "discard", SOCK_DGRAM, 0, 0, discard_dg },
+
+ /* Return 32 bit time since 1970 */
+ { "time", SOCK_STREAM, 0, 0, machtime_stream },
+ { "time", SOCK_DGRAM, 0, 0, machtime_dg },
+
+ /* Return human-readable time */
+ { "daytime", SOCK_STREAM, 0, 0, daytime_stream },
+ { "daytime", SOCK_DGRAM, 0, 0, daytime_dg },
+
+ /* Familiar character generator */
+ { "chargen", SOCK_STREAM, 1, 0, chargen_stream },
+ { "chargen", SOCK_DGRAM, 0, 0, chargen_dg },
+
+ { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux },
+
+ { "ident", SOCK_STREAM, 1, 0, ident_stream },
+
+ { NULL }
+};
+
+#define NUMINT (sizeof(intab) / sizeof(struct inent))
+char *CONFIG = _PATH_INETDCONF;
+char *pid_file = _PATH_INETDPID;
+
+#ifdef OLD_SETPROCTITLE
+char **Argv;
+char *LastArg;
+#endif
+
+int
+getvalue(arg, value, whine)
+ char *arg, *whine;
+ int *value;
+{
+ int tmp;
+ char *p;
+
+ tmp = strtol(arg, &p, 0);
+ if (tmp < 1 || *p) {
+ syslog(LOG_ERR, whine, arg);
+ return 1; /* failure */
+ }
+ *value = tmp;
+ return 0; /* success */
+}
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ struct servtab *sep;
+ struct passwd *pwd;
+ struct group *grp;
+ struct sigaction sa, sapipe;
+ int tmpint, ch, dofork;
+ pid_t pid;
+ char buf[50];
+ struct sockaddr_in peer;
+ int i;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+#ifdef LIBWRAP
+ struct request_info req;
+ int denied;
+ char *service = NULL;
+#endif
+
+
+#ifdef OLD_SETPROCTITLE
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+ openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+
+ bind_address.s_addr = htonl(INADDR_ANY);
+ while ((ch = getopt(argc, argv, "dlR:a:c:C:p:")) != -1)
+ switch(ch) {
+ case 'd':
+ debug = 1;
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ log = 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':
+ if (!inet_aton(optarg, &bind_address)) {
+ syslog(LOG_ERR,
+ "-a %s: invalid IP address", optarg);
+ exit(EX_USAGE);
+ }
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case '?':
+ default:
+ syslog(LOG_ERR,
+ "usage: inetd [-dl] [-a address] [-R rate]"
+ " [-c maximum] [-C rate]"
+ " [-p pidfile] [conf-file]");
+ exit(EX_USAGE);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (debug == 0) {
+ FILE *fp;
+ if (daemon(0, 0) < 0) {
+ syslog(LOG_WARNING, "daemon(0,0) failed: %m");
+ }
+ /*
+ * 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);
+ }
+ }
+ 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, (struct sigaction *)0);
+ config();
+ sa.sa_handler = flag_config;
+ sigaction(SIGHUP, &sa, (struct sigaction *)0);
+ sa.sa_handler = flag_reapchild;
+ sigaction(SIGCHLD, &sa, (struct sigaction *)0);
+ 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);
+ }
+ FD_SET(signalpipe[0], &allsock);
+ if (signalpipe[0]>maxsock) maxsock = signalpipe[0];
+
+ for (;;) {
+ int n, ctrl;
+ fd_set readable;
+
+ if (nsock == 0) {
+ (void) sigblock(SIGBLOCK);
+ while (nsock == 0)
+ sigpause(0L);
+ (void) sigsetmask(0L);
+ }
+ 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 n;
+ if (ioctl(signalpipe[0], FIONREAD, &n) != 0) {
+ syslog(LOG_ERR, "ioctl: %m");
+ exit(EX_OSERR);
+ }
+ while (--n >= 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);
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
+ ctrl = accept(sep->se_fd, (struct sockaddr *)0,
+ (int *)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;
+ }
+ if (cpmip(sep, ctrl) < 0) {
+ close(ctrl);
+ continue;
+ }
+ if (log) {
+ i = sizeof peer;
+ if (getpeername(ctrl, (struct sockaddr *)
+ &peer, &i)) {
+ syslog(LOG_WARNING,
+ "getpeername(for %s): %m",
+ sep->se_service);
+ close(ctrl);
+ continue;
+ }
+ syslog(LOG_INFO,"%s from %s",
+ sep->se_service,
+ inet_ntoa(peer.sin_addr));
+ }
+ } else
+ ctrl = sep->se_fd;
+ (void) sigblock(SIGBLOCK);
+ pid = 0;
+#ifdef LIBWRAP_INTERNAL
+ dofork = 1;
+#else
+ dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
+#endif
+ if (dofork) {
+ if (sep->se_count++ == 0)
+ (void)gettimeofday(&sep->se_time, (struct timezone *)NULL);
+ else if (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);
+ close_sep(sep);
+ 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);
+ sigsetmask(0L);
+ sleep(1);
+ continue;
+ }
+ if (pid)
+ addchild(sep, pid);
+ sigsetmask(0L);
+ if (pid == 0) {
+ if (dofork) {
+ if (debug)
+ warnx("+ closing from %d", maxsock);
+ for (tmpint = maxsock; tmpint > 2; tmpint--)
+ if (tmpint != ctrl)
+ (void) close(tmpint);
+ }
+ /*
+ * Call tcpmux to find the real service to exec.
+ */
+ if (sep->se_bi &&
+ sep->se_bi->bi_fn == (void (*)()) tcpmux) {
+ sep = tcpmux(ctrl);
+ if (sep == NULL) {
+ close(ctrl);
+ _exit(0);
+ }
+ }
+#ifdef LIBWRAP
+#ifndef LIBWRAP_INTERNAL
+ if (sep->se_bi == 0)
+#endif
+ if (sep->se_accept
+ && sep->se_socktype == SOCK_STREAM) {
+ request_init(&req,
+ RQ_DAEMON, sep->se_argv[0] ?
+ sep->se_argv[0] : sep->se_service,
+ RQ_FILE, ctrl, NULL);
+ fromhost(&req);
+ denied = !hosts_access(&req);
+ if (denied || log) {
+ sp = getservbyport(sep->se_ctrladdr.sin_port, sep->se_proto);
+ if (sp == NULL) {
+ (void)snprintf(buf, sizeof buf, "%d",
+ ntohs(sep->se_ctrladdr.sin_port));
+ service = buf;
+ } else
+ service = sp->s_name;
+ }
+ if (denied) {
+ syslog(deny_severity,
+ "refused connection from %.500s, service %s (%s)",
+ eval_client(&req), service, sep->se_proto);
+ goto reject;
+ }
+ if (log) {
+ syslog(allow_severity,
+ "connection from %.500s, service %s (%s)",
+ eval_client(&req), service, sep->se_proto);
+ }
+ }
+#endif /* LIBWRAP */
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ /* NOTREACHED */
+ } else {
+ if (debug)
+ warnx("%d execl %s",
+ getpid(), sep->se_server);
+ 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) != 0) {
+ syslog(LOG_ERR,
+ "%s: can't setusercontext(..%s..): %m",
+ sep->se_service, sep->se_user);
+ _exit(EX_OSERR);
+ }
+#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);
+#ifdef LIBWRAP
+ reject:
+#endif
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_OSERR);
+ }
+ }
+ 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(c)
+ char c;
+{
+ if (write(signalpipe[1], &c, 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)
+{
+#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
+ if (sep->se_maxchild == 0)
+ return;
+ 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(signo)
+ int signo;
+{
+ flag_signal('C');
+}
+
+void
+reapchild()
+{
+ 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, status %#x", pid, 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 (status)
+ syslog(LOG_WARNING,
+ "%s[%d]: exit status 0x%x",
+ sep->se_server, pid, status);
+ break;
+ }
+ }
+}
+
+void
+flag_config(signo)
+ int signo;
+{
+ flag_signal('H');
+}
+
+void config()
+{
+ struct servtab *sep, *new, **sepp;
+ long omask;
+
+ 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 (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;
+ }
+#endif
+ 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)
+ break;
+ if (sep != 0) {
+ int i;
+
+#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
+ omask = sigblock(SIGBLOCK);
+ /* copy over outstanding child pids */
+ if (sep->se_maxchild && new->se_maxchild) {
+ 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(sep->se_pids, new->se_pids);
+ sep->se_maxchild = new->se_maxchild;
+ sep->se_numchild = new->se_numchild;
+ sep->se_maxcpm = new->se_maxcpm;
+ /* might need to turn on or off service now */
+ if (sep->se_fd >= 0) {
+ if (sep->se_maxchild
+ && 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(sep->se_user, new->se_user);
+ SWAP(sep->se_group, new->se_group);
+#ifdef LOGIN_CAP
+ SWAP(sep->se_class, new->se_class);
+#endif
+ SWAP(sep->se_server, new->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(sep->se_argv[i], new->se_argv[i]);
+ 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;
+ }
+ if (!sep->se_rpc) {
+ 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;
+ }
+ if (sp->s_port != sep->se_ctrladdr.sin_port) {
+ sep->se_ctrladdr.sin_family = AF_INET;
+ sep->se_ctrladdr.sin_addr = bind_address;
+ sep->se_ctrladdr.sin_port = sp->s_port;
+ if (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 (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;
+ }
+ }
+ 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((char *)sep);
+ }
+ (void) sigsetmask(omask);
+}
+
+void
+unregisterrpc(sep)
+ struct servtab *sep;
+{
+ int i;
+ struct servtab *sepp;
+ long omask;
+
+ omask = sigblock(SIGBLOCK);
+ for (sepp = servtab; sepp; sepp = sepp->se_next) {
+ if (sepp == sep)
+ continue;
+ if (sep->se_checked == 0 ||
+ !sepp->se_rpc ||
+ sep->se_rpc_prog != sepp->se_rpc_prog)
+ continue;
+ return;
+ }
+ if (debug)
+ print_service("UNREG", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
+ pmap_unset(sep->se_rpc_prog, i);
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ (void) sigsetmask(omask);
+}
+
+void
+flag_retry(signo)
+ int signo;
+{
+ flag_signal('A');
+}
+
+void
+retry()
+{
+ struct servtab *sep;
+
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_fd == -1 && !ISMUX(sep))
+ setup(sep);
+}
+
+void
+setup(sep)
+ struct servtab *sep;
+{
+ int on = 1;
+
+ if ((sep->se_fd = socket(AF_INET, 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;
+ }
+#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
+#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");
+ if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
+ sizeof (sep->se_ctrladdr)) < 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);
+ }
+ return;
+ }
+ if (sep->se_rpc) {
+ int i, len = sizeof(struct sockaddr);
+
+ 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;
+ }
+ if (debug)
+ print_service("REG ", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
+ pmap_unset(sep->se_rpc_prog, i);
+ pmap_set(sep->se_rpc_prog, i,
+ (sep->se_socktype == SOCK_DGRAM)
+ ? IPPROTO_UDP : IPPROTO_TCP,
+ ntohs(sep->se_ctrladdr.sin_port));
+ }
+
+ }
+ 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);
+ }
+}
+
+/*
+ * Finish with a service and its socket.
+ */
+void
+close_sep(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 */
+}
+
+struct servtab *
+enter(cp)
+ struct servtab *cp;
+{
+ struct servtab *sep;
+ long omask;
+
+ sep = (struct servtab *)malloc(sizeof (*sep));
+ if (sep == (struct servtab *)0) {
+ syslog(LOG_ERR, "Out of memory.");
+ 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);
+ }
+#endif
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ 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);
+ }
+#endif
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ if (sep->se_fd == maxsock)
+ maxsock--;
+}
+
+FILE *fconfig = NULL;
+struct servtab serv;
+char line[LINE_MAX];
+
+int
+setconfig()
+{
+
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return (1);
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+void
+endconfig()
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+}
+
+struct servtab *
+getconfigent()
+{
+ struct servtab *sep = &serv;
+ int argc;
+ char *cp, *arg, *s;
+ char *versp;
+ static char TCPMUX_TOKEN[] = "tcpmux/";
+#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
+
+more:
+ while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
+ ;
+ if (cp == NULL)
+ return ((struct servtab *)0);
+ /*
+ * clear the static buffer, since some fields (se_ctrladdr,
+ * for example) don't get initialized here.
+ */
+ memset((caddr_t)sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (cp == NULL) {
+ /* got an empty line containing just blanks/tabs. */
+ goto more;
+ }
+ 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 (strcmp(arg, "tcp/ttcp") == 0) {
+ sep->se_type = TTCP_TYPE;
+ sep->se_proto = newstr("tcp");
+ } else {
+ 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;
+ sep->se_ctrladdr.sin_family = AF_INET;
+ sep->se_ctrladdr.sin_port = 0;
+ sep->se_ctrladdr.sin_addr = bind_address;
+ if ((versp = rindex(sep->se_service, '/'))) {
+ *versp++ = '\0';
+ switch (sscanf(versp, "%d-%d",
+ &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\n",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ }
+ else {
+ sep->se_rpc_lowvers =
+ sep->se_rpc_highvers = 1;
+ }
+ }
+ 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 = maxchild;
+ sep->se_maxcpm = maxcpm;
+ 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;
+ }
+ sep->se_maxchild = val;
+ if (*eptr == '/')
+ sep->se_maxcpm = 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 (strcmp(sep->se_server, "internal") == 0) {
+ struct biltin *bi;
+
+ for (bi = biltins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ strcmp(bi->bi_service, sep->se_service) == 0)
+ 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_maxchild < 0) /* apply default max-children */
+ if (sep->se_bi)
+ sep->se_maxchild = sep->se_bi->bi_maxchild;
+ else
+ sep->se_maxchild = sep->se_accept ? 0 : 1;
+ if (sep->se_maxchild) {
+ sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
+ if (sep->se_pids == NULL) {
+ syslog(LOG_ERR, "Out of memory.");
+ 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;
+ return (sep);
+}
+
+void
+freeconfig(cp)
+ 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]);
+}
+
+
+/*
+ * Safe skip - if skip returns null, log a syntax error in the
+ * configuration file and exit.
+ */
+char *
+sskip(cpp)
+ char **cpp;
+{
+ char *cp;
+
+ cp = skip(cpp);
+ if (cp == NULL) {
+ syslog(LOG_ERR, "%s: syntax error", CONFIG);
+ exit(EX_DATAERR);
+ }
+ return (cp);
+}
+
+char *
+skip(cpp)
+ 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(fd)
+ 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(cp)
+ char *cp;
+{
+ if ((cp = strdup(cp ? cp : "")))
+ return (cp);
+ syslog(LOG_ERR, "strdup: %m");
+ exit(EX_OSERR);
+}
+
+#ifdef OLD_SETPROCTITLE
+void
+inetd_setproctitle(a, s)
+ char *a;
+ int s;
+{
+ int size;
+ char *cp;
+ struct sockaddr_in sin;
+ char buf[80];
+
+ cp = Argv[0];
+ size = sizeof(sin);
+ if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
+ (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
+ else
+ (void) sprintf(buf, "-%s", a);
+ strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
+#else
+void
+inetd_setproctitle(a, s)
+ char *a;
+ int s;
+{
+ int size;
+ struct sockaddr_in sin;
+ char buf[80];
+
+ size = sizeof(sin);
+ if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
+ (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
+ else
+ (void) sprintf(buf, "%s", a);
+ setproctitle("%s", buf);
+}
+#endif
+
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 8192
+
+#define IDENT_RESPONSE ":ERROR:HIDDEN-USER\r\n"
+
+/* ARGSUSED */
+void
+ident_stream(s, sep) /* Ident service */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+ int i, j;
+
+ inetd_setproctitle(sep->se_service, s);
+ j = 0;
+ while ((i = read(s, buffer + j, sizeof(buffer) - j)) > 0) {
+ j += i;
+ buffer[j] = '\0';
+ if (strchr(buffer, '\n'))
+ break;
+ if (strchr(buffer, '\r'))
+ break;
+ }
+ while (j > 0 && (buffer[j-1] == '\n' || buffer[j-1] == '\r'))
+ j--;
+ write(s, buffer, j);
+ write(s, IDENT_RESPONSE, strlen(IDENT_RESPONSE));
+ exit(0);
+}
+/* ARGSUSED */
+void
+echo_stream(s, sep) /* Echo service -- echo data back */
+ 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);
+}
+
+int check_loop(sin, sep)
+ struct sockaddr_in *sin;
+ struct servtab *sep;
+{
+ struct servtab *se2;
+
+ for (se2 = servtab; se2; se2 = se2->se_next) {
+ if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
+ continue;
+
+ if (sin->sin_port == se2->se_ctrladdr.sin_port) {
+ syslog(LOG_WARNING,
+ "%s/%s:%s/%s loop request REFUSED from %s",
+ sep->se_service, sep->se_proto,
+ se2->se_service, se2->se_proto,
+ inet_ntoa(sin->sin_addr));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* ARGSUSED */
+void
+echo_dg(s, sep) /* Echo service -- echo data back */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+ int i, size;
+ struct sockaddr_in sin;
+
+ size = sizeof(sin);
+ if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&sin, &size)) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
+ sizeof(sin));
+}
+
+/* ARGSUSED */
+void
+discard_stream(s, sep) /* Discard service -- ignore data */
+ 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);
+}
+
+/* ARGSUSED */
+void
+discard_dg(s, sep) /* Discard service -- ignore data */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+
+#include <ctype.h>
+#define LINESIZ 72
+char ring[128];
+char *endring;
+
+void
+initring()
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* ARGSUSED */
+void
+chargen_stream(s, sep) /* Character generator */
+ 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);
+}
+
+/* ARGSUSED */
+void
+chargen_dg(s, sep) /* Character generator */
+ int s;
+ struct servtab *sep;
+{
+ struct sockaddr_in sin;
+ static char *rs;
+ int len, size;
+ char text[LINESIZ+2];
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(sin);
+ if (recvfrom(s, text, sizeof(text), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, 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 *)&sin, sizeof(sin));
+}
+
+/*
+ * 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()
+{
+ 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_stream(s, sep)
+ int s;
+ struct servtab *sep;
+{
+ unsigned long result;
+
+ result = machtime();
+ (void) write(s, (char *) &result, sizeof(result));
+}
+
+/* ARGSUSED */
+void
+machtime_dg(s, sep)
+ int s;
+ struct servtab *sep;
+{
+ unsigned long result;
+ struct sockaddr_in sin;
+ int size;
+
+ size = sizeof(sin);
+ if (recvfrom(s, (char *)&result, sizeof(result), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+/* ARGSUSED */
+void
+daytime_stream(s, sep) /* Return human-readable time of day */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[256];
+ time_t clock;
+
+ clock = time((time_t *) 0);
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+ (void) write(s, buffer, strlen(buffer));
+}
+
+/* ARGSUSED */
+void
+daytime_dg(s, sep) /* Return human-readable time of day */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[256];
+ time_t clock;
+ struct sockaddr_in sin;
+ int size;
+
+ clock = time((time_t *) 0);
+
+ size = sizeof(sin);
+ if (recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+ (void) sendto(s, buffer, strlen(buffer), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+/*
+ * print_service:
+ * Dump relevant information to stderr
+ */
+void
+print_service(action, sep)
+ char *action;
+ struct servtab *sep;
+{
+ fprintf(stderr,
+#ifdef LOGIN_CAP
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%p server=%s\n",
+#else
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%p server=%s\n",
+#endif
+ 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);
+}
+
+/*
+ * Based on TCPMUX.C by Mark K. Lottor November 1988
+ * sri-nic::ps:<mkl>tcpmux.c
+ */
+
+
+static int /* # of characters upto \r,\n or \0 */
+getline(fd, buf, len)
+ 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);
+}
+
+#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
+
+#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
+
+struct servtab *
+tcpmux(s)
+ 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);
+}
+
+#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 {
+ struct in_addr ch_Addr;
+ time_t ch_LTime;
+ char *ch_Service;
+ CTime ch_Times[CHTSIZE];
+} CHash;
+
+CHash CHashAry[CPMHSIZE];
+
+int
+cpmip(sep, ctrl)
+ struct servtab *sep;
+ int ctrl;
+{
+ struct sockaddr_in rsin;
+ int rsinLen = sizeof(rsin);
+ 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 *)&rsin, &rsinLen) == 0 ) {
+ time_t t = time(NULL);
+ int hv = 0xABC3D20F;
+ int i;
+ int cnt = 0;
+ CHash *chBest = NULL;
+ unsigned int ticks = t / CHTGRAN;
+
+ {
+ char *p;
+ int i;
+
+ for (i = 0, p = (char *)&rsin.sin_addr;
+ i < sizeof(rsin.sin_addr);
+ ++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 (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+ if (chBest == NULL || ch->ch_LTime == 0 ||
+ ch->ch_LTime < chBest->ch_LTime) {
+ chBest = ch;
+ }
+ }
+ if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Addr = rsin.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));
+ }
+ 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 * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
+ r = -1;
+ syslog(LOG_ERR,
+ "%s from %s exceeded counts/min (limit %d/min)",
+ sep->se_service, inet_ntoa(rsin.sin_addr),
+ sep->se_maxcpm);
+ }
+ }
+ return(r);
+}
diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h
new file mode 100644
index 0000000..503cd21
--- /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
+ * $Id$
+ */
+
+#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..1caefc5
--- /dev/null
+++ b/usr.sbin/iostat/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= iostat
+CFLAGS+=-I${.CURDIR}/../../sys
+SRCS= iostat.c
+MAN8= iostat.8
+DPADD= ${LIBKVM} ${LIBDEVSTAT}
+LDADD= -lkvm -ldevstat
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8
new file mode 100644
index 0000000..194faf7
--- /dev/null
+++ b/usr.sbin/iostat/iostat.8
@@ -0,0 +1,400 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: iostat.8,v 1.6 1998/09/16 18:03:44 dillon Exp $
+.\"
+.\" 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 FreeBSD 3.0
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm iostat
+.Op Fl CdhKIoT?
+.Op Fl c Ar count
+.Op Fl M Ar core
+.Op Fl n Ar devs
+.Op Fl N Ar system
+.Op Fl t Ar type,if,pass
+.Op Fl w Ar wait
+.Op Ar drives
+.Sh DESCRIPTION
+.Nm Iostat
+displays kernel
+.Tn I/O
+statistics on terminal, device and cpu
+operations.
+.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 specfied to enable the display of CPU or TTY statistics.
+.It Fl h
+Put iostat in
+.Sq top
+mode. In this mode, iostat will show devices in order from highest to
+lowest bytes per measurement cycle.
+.It Fl I
+Display total statstics for a given time period, rather than average
+statistics for each second during that time period.
+.It Fl K
+In the blocks transfered 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.
+.Nm iostat
+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 /kernel .
+.It Fl o
+Display old-style
+.Nm iostat
+device statistics. Sectors per second, transfers per second, and miliseconds
+per seek are displayed. If
+.Fl I
+is specified, total blocks/sectors, total transfers, and
+miliseconds per seek are displayed.
+.It Fl t
+Specify which types of devices to display. There are three different
+categories of devices:
+
+.Bl -tag -width indent -compact
+.It device type:
+.Bl -tag -width 123456789 -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 123456789 -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 123456789 -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 iostat
+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
+.Nm Iostat
+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.
+.Nm iostat
+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, iostat will display the smaller of the
+requested number of devices, and the maximum number of devices in the system.
+To force
+.Nm iostat
+to display specific drives, their names may be supplied on the command
+line.
+.Nm iostat
+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, iostat will show only the specified
+devices.
+.Pp
+The standard
+.Nm iostat
+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 iostat
+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 iostat
+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 iostat
+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 /dev/kmem -compact
+.It Pa /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 inifitum.
+.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 iostat
+first appeared in
+.Fx 3.0 .
+.Sh BUGS
+.Pp
+You cannot display device statistics for a non-running system, due to the
+fact that the new device statistics interface is accessible only via
+.Xr sysctl 3 ,
+which does not provide a way to access non-running systems.
+.Sh AUTHOR
+.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..e961725
--- /dev/null
+++ b/usr.sbin/iostat/iostat.c
@@ -0,0 +1,676 @@
+/*
+ * Copyright (c) 1997, 1998 Kenneth D. Merry.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: iostat.c,v 1.11 1998/09/16 23:14:47 ken Exp $
+ */
+/*
+ * 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/types.h>
+#include <sys/errno.h>
+#include <sys/dkstat.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <devstat.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_HZ 3
+ { "_hz" },
+#define X_STATHZ 4
+ { "_stathz" },
+#define X_END 4
+ { NULL },
+};
+
+struct statinfo cur, last;
+int num_devices;
+struct device_selection *dev_select;
+int maxshowdevs;
+int dflag = 0, Iflag = 0, Cflag = 0, Tflag = 0, oflag = 0, Kflag = 0;
+
+#define nlread(x, v) \
+ kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
+
+/* local function declarations */
+static void usage(void);
+static void phdr(int signo);
+static void devstats(int perf_select);
+static void cpustats(void);
+
+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];
+ char *err_str;
+ kvm_t *kd;
+ int hz, stathz;
+ int headercount;
+ long generation;
+ int num_devices_specified;
+ int num_selected, num_selections;
+ long select_generation;
+ char **specified_devices;
+ devstat_select_mode select_mode;
+
+ 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 devcies %d is < 0",
+ maxshowdevs);
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'o':
+ oflag++;
+ break;
+ case 't':
+ tflag++;
+ if (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;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ /*
+ * 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 (checkversion() < 0)
+ errx(1, "%s", devstat_errbuf);
+
+ /*
+ * 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 = getnumdevs()) < 0)
+ err(1, "can't get number of devices");
+
+ cur.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ last.dinfo = (struct devinfo *)malloc(sizeof(struct devinfo));
+ 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 (getdevs(&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 *));
+ 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);
+ 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 (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;
+
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+
+ if (kd == 0)
+ errx(1, "kvm_openfiles: %s", errbuf);
+
+ if (kvm_nlist(kd, namelist) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+
+ (void)nlread(X_HZ, hz);
+ (void)nlread(X_STATHZ, stathz);
+ if (stathz)
+ hz = stathz;
+
+ /*
+ * If the user stops the program (control-Z) and then resumes it,
+ * print out the header again.
+ */
+ (void)signal(SIGCONT, phdr);
+
+ for (headercount = 1;;) {
+ struct devinfo *tmp_dinfo;
+ long tmp;
+ double etime;
+
+ if (!--headercount) {
+ phdr(0);
+ headercount = 20;
+ }
+ (void)kvm_read(kd, namelist[X_TK_NIN].n_value,
+ &cur.tk_nin, sizeof(cur.tk_nin));
+ (void)kvm_read(kd, namelist[X_TK_NOUT].n_value,
+ &cur.tk_nout, sizeof(cur.tk_nout));
+ (void)kvm_read(kd, namelist[X_CP_TIME].n_value,
+ cur.cp_time, sizeof(cur.cp_time));
+
+ tmp_dinfo = last.dinfo;
+ last.dinfo = cur.dinfo;
+ cur.dinfo = tmp_dinfo;
+
+ last.busy_time = cur.busy_time;
+
+ /*
+ * Here what we want to do is refresh our device stats.
+ * 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 (getdevs(&cur)) {
+ case -1:
+ errx(1, "%s", devstat_errbuf);
+ break;
+ case 1: {
+ int retval;
+
+ num_devices = cur.dinfo->numdevs;
+ generation = cur.dinfo->generation;
+ retval = 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(0);
+ 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 = 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(0);
+ headercount = 20;
+ break;
+ default:
+ break;
+ }
+ }
+
+ 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 = 0.0;
+
+#define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
+
+ for (i = 0; i < CPUSTATES; i++) {
+ X(cp_time);
+ etime += cur.cp_time[i];
+ }
+ if (etime == 0.0)
+ etime = 1.0;
+ etime /= (float)hz;
+ if ((dflag == 0) || (Tflag > 0))
+ printf("%4.0f%5.0f", cur.tk_nin / etime,
+ cur.tk_nout/etime);
+ devstats(hflag);
+ if ((dflag == 0) || (Cflag > 0))
+ cpustats();
+ printf("\n");
+ fflush(stdout);
+
+ if (count >= 0 && --count <= 0)
+ break;
+
+ sleep(waittime);
+ }
+
+ exit(0);
+}
+
+static void
+phdr(int signo)
+{
+ register int i;
+ int printed;
+
+ if ((dflag == 0) || (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 ((dflag == 0) || (Cflag > 0))
+ (void)printf(" cpu\n");
+ else
+ (void)printf("\n");
+
+ if ((dflag == 0) || (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 ((dflag == 0) || (Cflag > 0))
+ (void)printf(" us ni sy in id\n");
+ else
+ printf("\n");
+
+}
+
+static void
+devstats(int perf_select)
+{
+ 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 busy_seconds;
+ long double total_mb;
+ long double blocks_per_second, ms_per_transaction;
+
+ /*
+ * Calculate elapsed time up front, since it's the same for all
+ * devices.
+ */
+ busy_seconds = compute_etime(cur.busy_time, last.busy_time);
+
+ 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 (compute_stats(&cur.dinfo->devices[di],
+ &last.dinfo->devices[di], busy_seconds,
+ &total_bytes, &total_transfers,
+ &total_blocks, &kb_per_transfer,
+ &transfers_per_second, &mb_per_second,
+ &blocks_per_second, &ms_per_transaction)!= 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("%3.0f",
+ 100. * cur.cp_time[state] / (time ? time : 1));
+}
diff --git a/usr.sbin/ipfstat/Makefile b/usr.sbin/ipfstat/Makefile
new file mode 100644
index 0000000..200b0b8
--- /dev/null
+++ b/usr.sbin/ipfstat/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipfstat
+MAN8= ipfstat.8
+SRCS= fils.c parse.c opt.c kmem.c
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I- -I${.CURDIR}/../../sys/netinet -I${.CURDIR}/../../sys -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipftest/Makefile b/usr.sbin/ipftest/Makefile
new file mode 100644
index 0000000..60ee314
--- /dev/null
+++ b/usr.sbin/ipftest/Makefile
@@ -0,0 +1,20 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../sys/netinet \
+ ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipftest
+MAN1= ipftest.1
+SRCS= opt_ipfilter.h \
+ ipt.c fil.c ipft_hx.c ipft_sn.c ipft_ef.c ipft_td.c ipft_pc.c \
+ ipft_tx.c misc.c parse.c opt.c ip_frag.c ip_nat.c ip_state.c \
+ ip_auth.c ip_fil.c ip_proxy.c
+
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I- -I${.OBJDIR} -I${.CURDIR}/../../sys/netinet -I${.CURDIR}/../../sys -I${.CURDIR}/../../contrib/ipfilter
+
+CLEANFILES+= opt_ipfilter.h
+
+opt_ipfilter.h: Makefile
+ echo "#define IPFILTER 1" > opt_ipfilter.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipmon/Makefile b/usr.sbin/ipmon/Makefile
new file mode 100644
index 0000000..96cffcf
--- /dev/null
+++ b/usr.sbin/ipmon/Makefile
@@ -0,0 +1,11 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipmon
+MAN8= ipmon.8
+SRCS= ipmon.c
+
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I- -I${.CURDIR}/../../sys -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ipnat/Makefile b/usr.sbin/ipnat/Makefile
new file mode 100644
index 0000000..7daa52d
--- /dev/null
+++ b/usr.sbin/ipnat/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter ${.CURDIR}/../../contrib/ipfilter/man
+
+PROG= ipnat
+MAN1= ipnat.1
+MAN4= ipnat.4
+MAN5= ipnat.5
+SRCS= ipnat.c kmem.c
+CFLAGS+=-DIPL_NAME=\"/dev/ipl\" -I- -I${.CURDIR}/../../sys/netinet -I${.CURDIR}/../../sys -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..8a9d8b2
--- /dev/null
+++ b/usr.sbin/ipresend/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipresend
+MAN1= ipresend.1
+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\" \
+ -I- -I${.CURDIR}/../../sys/netinet \
+ -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..75a45b3
--- /dev/null
+++ b/usr.sbin/ipsend/Makefile
@@ -0,0 +1,22 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter/iplang \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= ipsend
+MAN1= ipsend.1
+MAN5= 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\" \
+ -I- -I. -I${.CURDIR}/../../sys/netinet \
+ -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..f296945
--- /dev/null
+++ b/usr.sbin/iptest/Makefile
@@ -0,0 +1,15 @@
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../contrib/ipfilter/ipsend \
+ ${.CURDIR}/../../contrib/ipfilter
+
+PROG= iptest
+MAN1= iptest.1
+SRCS= iptest.c iptests.c ip.c sbpf.c 44arp.c sock.c
+
+CFLAGS+=-DDOSOCKET -DIPL_NAME=\"/dev/ipl\" \
+ -I- -I${.CURDIR}/../../sys/netinet \
+ -I${.CURDIR}/../../contrib/ipfilter/ipsend \
+ -I${.CURDIR}/../../contrib/ipfilter
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdcontrol/Makefile b/usr.sbin/kbdcontrol/Makefile
new file mode 100644
index 0000000..ad91cb0
--- /dev/null
+++ b/usr.sbin/kbdcontrol/Makefile
@@ -0,0 +1,6 @@
+PROG= kbdcontrol
+SRCS= kbdcontrol.c lex.l
+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..ebac0d9
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.1
@@ -0,0 +1,106 @@
+.\"
+.\" 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
+.\"
+.Dd May 22, 1994
+.Dt kbdcontrol 1
+.Os FreeBSD
+.Sh NAME
+.Nm kbdcontrol
+.Nd a utility for manipulating the syscons keyboard driver section
+.Sh SYNOPSIS
+.Nm
+.Op Fl dFx
+.Op Fl b Ar duration.pitch | Ar [quiet.]belltype
+.Op Fl r Ar delay.repeat | Ar speed
+.Op Fl l Ar mapfile
+.Op Fl f Ar # Ar string
+.Op Fl h Ar size
+.Op Fl L Ar mapfile
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various keyboard related options for the syscons
+console driver, such as keymap, keyboard repeat & delay rates, bell
+characteristics etc.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl b Ar duration.pitch | Ar [quiet.]belltype
+Set the bell duration in miliseconds and pitch in hertz.
+If a
+.Ar belltype
+argument is specified, it may be one of
+.Ar normal
+which set sound parameters back to normal values, or
+.Ar visual
+which set the bell to visual mode, i.e. flashes the screen instead.
+If
+.Ar belltype
+is preceded by the word
+.Ar quiet. ,
+the bell will not be rung when the ringing process is in the background vty.
+.It Fl r Ar delay.repeat | Ar speed
+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
+.Ar slow
+(1000.504),
+.Ar fast
+(250.34)
+or
+.Ar normal
+(500.126).
+.It Fl l Ar mapfile
+Install keyboard map file from
+.Ar mapfile .
+.It Fl d
+Dump the current keyboard map onto stdout.
+.It Fl f Ar # Ar string
+.BI "\-f\ " #\ string
+Set function key number
+.Ar #
+to send
+.Ar string .
+.It Fl F
+Set function keys back to the standard definitions.
+.It Fl x
+Use hexadecimal numbers in keyboard map dump.
+.It Fl h Ar size
+Set history buffer size to
+.Ar size
+lines.
+.It Fl L Ar mapfile
+Load keyboard map file from
+.Ar mapfile
+and write the
+.Ft "struct keymap"
+compiled from it to stdout.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps -compact
+.Pa /usr/share/syscons/keymaps
+.Sh "BUGS"
+Report when found.
+.Sh "SEE ALSO"
+.Xr vidcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4
+.Sh AUTHORS
+.An Søren 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..4a88b48
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.c
@@ -0,0 +1,1015 @@
+/*-
+ * 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 withough 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[] =
+ "$Id: kbdcontrol.c,v 1.23 1999/03/10 10:36:51 yokota Exp $";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <machine/console.h>
+#include "path.h"
+#include "lex.h"
+
+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 */ "" , "" , "" , "" ,
+ };
+
+int hex = 0;
+int number;
+char letter;
+int token;
+
+static void usage __P((void));
+
+char *
+nextarg(int ac, char **av, int *indp, int oc)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ warnx("option requires two arguments -- %c", oc);
+ usage();
+ return("");
+}
+
+
+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()
+{
+ 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 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 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;
+ }
+}
+
+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 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;
+ 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 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;
+ 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;
+ char *name, *cp;
+ char *prefix[] = {"", "", KEYMAP_PATH, KEYMAP_PATH, NULL};
+ char *postfix[] = {"", ".kbd", "", ".kbd"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], opt, postfix[i]);
+ if ((fd = fopen(name, "r")))
+ break;
+ }
+ if (fd == NULL) {
+ warn("keymap file not found");
+ 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()
+{
+ 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()
+{
+ 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 {
+ 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");
+ 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;
+
+ if (!strcmp(opt, "slow"))
+ delay = 1000, repeat = 500;
+ else if (!strcmp(opt, "normal"))
+ delay = 500, repeat = 125;
+ else if (!strcmp(opt, "fast"))
+ delay = repeat = 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");
+ return;
+ }
+ }
+
+ arg[0] = delay;
+ arg[1] = repeat;
+ if (ioctl(0, KDSETREPEAT, arg))
+ warn("setting keyboard rate");
+}
+
+
+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");
+}
+
+static char
+*get_kbd_type_name(int type)
+{
+ static struct {
+ int type;
+ char *name;
+ } name_table[] = {
+ { KB_84, "AT 84" },
+ { KB_101, "AT 101/102" },
+ { KB_OTHER, "generic" },
+ };
+ 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",
+ 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",
+ 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",
+ 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");
+}
+
+
+static 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]",
+" [-h size] [-k device] [-L mapfile]");
+ exit(1);
+}
+
+
+void
+main(int argc, char **argv)
+{
+ int opt;
+
+ while((opt = getopt(argc, argv, "b:df:h: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 'h':
+ set_history(optarg);
+ 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/lex.h b/usr.sbin/kbdcontrol/lex.h
new file mode 100644
index 0000000..f188d51
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.h
@@ -0,0 +1,59 @@
+/*-
+ * 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 withough 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.
+ *
+ * $Id: lex.h,v 1.5 1998/01/07 08:43:27 yokota Exp $
+ */
+
+#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
+
+extern int number;
+extern char letter;
+extern FILE *yyin;
diff --git a/usr.sbin/kbdcontrol/lex.l b/usr.sbin/kbdcontrol/lex.l
new file mode 100644
index 0000000..01ca536
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.l
@@ -0,0 +1,137 @@
+/*-
+ * 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 withough 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.
+ *
+ * $Id: lex.l,v 1.7 1998/08/03 11:33:22 yokota Exp $
+ */
+
+%{
+
+#include "lex.h"
+
+%}
+
+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; }
+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; }
+
+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..e344524
--- /dev/null
+++ b/usr.sbin/kbdmap/Makefile
@@ -0,0 +1,19 @@
+# Makefile for kbdmap / vidfont
+# $Id: Makefile,v 1.7 1997/02/22 16:06:00 peter Exp $
+
+MAINTAINER= wosch
+
+
+SCRIPT= ${.CURDIR}/kbdmap.pl
+LINKS= ${BINDIR}/kbdmap ${BINDIR}/vidfont
+MAN1= kbdmap.1
+MLINKS= kbdmap.1 vidfont.1
+
+all:
+ @echo -n
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${SCRIPT} ${DESTDIR}${BINDIR}/kbdmap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdmap/TODO b/usr.sbin/kbdmap/TODO
new file mode 100644
index 0000000..0735569
--- /dev/null
+++ b/usr.sbin/kbdmap/TODO
@@ -0,0 +1,5 @@
+o kbdmap.5 man page
+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..1b62c1f
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.1
@@ -0,0 +1,131 @@
+.\" 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.
+.\"
+.\" $Id: kbdmap.1,v 1.13 1997/09/19 06:29:52 charnier Exp $
+
+.Dd Mar 25, 1995
+.Dt KBDMAP 1
+.Os FreeBSD
+.Sh NAME
+.Nm kbdmap ,
+.Nm vidfont
+.Nd front end for syscons
+.Sh SYNOPSIS
+.Nm kbdmap
+.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
+.Nm kbdmap
+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.
+.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.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl K
+Run as command
+.Xr kbdmap 1 .
+.It Fl V
+Run as command
+.Xr vidfont 1 .
+.It Fl d , Fl default
+Use default language. Ignore $LANG.
+.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.
+.Sh ENVIRONMENT
+.Bl -tag -width /etc/master.passwdxx -compact
+.Ev LANG
+Preferred language.
+.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 LANG values.
+.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.
+.\"
+.Nm vidcontrol
+and
+.Nm kbdcontrol
+work only on a (virtual) console and not with X11.
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr kbdcontrol 1 ,
+.Xr vidcontrol 1 ,
+.Xr rc.conf 5
+.Sh HISTORY
+The
+.Nm kbdmap
+and
+.Nm vidfont
+commands appeared in
+.Fx 2.1 .
+.Sh AUTHORS
+.An Wolfram Schneider
+.Aq wosch@FreeBSD.org ,
+Berlin.
diff --git a/usr.sbin/kbdmap/kbdmap.pl b/usr.sbin/kbdmap/kbdmap.pl
new file mode 100644
index 0000000..9d46c1f
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.pl
@@ -0,0 +1,318 @@
+#!/usr/bin/perl
+#
+# 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.
+#
+# kbdmap/vidfont - front end for syscons
+#
+# $Id: kbdmap.pl,v 1.7 1997/05/19 07:30:45 jkh Exp $
+
+
+# simple test if syscons works
+$x11 = system("kbdcontrol -d >/dev/null");
+if ($x11) {
+ warn "You are not on a virtual console - " .
+ "expect certain strange side-effects\n";
+ sleep 2;
+}
+
+sub variables_static {
+ $lang_default = "en"; # set default language
+ $lang = $ENV{'LC_CTYPE'} || $ENV{'LANG'} || $lang_default;
+ $lang = &lang($lang);
+ $program = $0; $program =~ s|.*/||; $program =~ s/\.(pl|perl)$//;
+ $keymapdir = "/usr/share/syscons/keymaps";
+ $fontdir = "/usr/share/syscons/fonts";
+ $sysconfig = "/etc/rc.conf";
+
+ # for test only
+ #$keymapdir = "/tmp/kbdmap/syscons/keymaps";
+ #$fontdir = "/tmp/kbdmap/syscons/fonts";
+
+ # read current font from rc.conf
+ $font_default = "cp437-8x16.fnt";
+ $font_current = &font_current($font_default);
+
+ if ($program eq "kbdmap") {
+ $dir = $keymapdir;
+ } else {
+ $dir = $fontdir;
+ }
+
+ @langsupport = ('MENU', 'FONT'); # lang depended variables
+ $show = 0; # show which languages currently supported
+ $index = "INDEX"; # Keyboard language database
+ $verbose = 0;
+ %keymap = '';
+}
+
+sub lang {
+ local($lang) = @_;
+
+ #$lang =~ s/_.*//; # strip country and font
+ $lang =~ s/^(C)$/en/; # aliases
+ #$lang =~ s/^(..).*/$1/; # use only first to characters
+
+ return $lang;
+}
+
+sub font_current {
+ local($font) = @_;
+ local($font_current);
+
+ open(F, "$sysconfig") || warn "$sysconfig: $!\n";
+
+ while(<F>) {
+ /^#/ && next;
+ if (/^\s*font[0-9]+x[0-9]+\s*=\s*(\S+)/) {
+ $font_current = $1 if $1 ne "NO";
+ }
+ }
+ close F;
+
+ return $font_current if $font_current;
+ return $font;
+}
+
+sub vidcontrol {
+ local($font) = @_;
+
+ return $x11 if $x11; # syscons test failed
+
+ if ($font =~ /.*([0-9]+x[0-9]+)(\.fnt)?$/) {
+ warn "vidcontrol -f $1 $font\n" if $verbose;
+ return system("vidcontrol -f $1 $font");
+ } else {
+ warn "Which font size? ``$font''\n";
+ return 1;
+ }
+}
+
+sub menu_read {
+ local($e,@a,$mark,$ext);
+ local($keym, $lg, $dialect, $desc);
+ local(@langlist) = $lang_default;
+
+ $ext = $dir; $ext =~ s|.*/||;
+ # en_US.ISO8859-1 -> en_..\.ISO8859-1
+ ($dialect = $lang) =~ s/^(..)_..(.+)$/$1_..$2/;
+ # en_US.ISO8859-1 -> en
+ ($lang_abk = $lang) =~ s/^(..)_.*$/$1/;
+
+ # read index database
+ open(I, "$dir/$index.$ext") || warn "$dir/$index.$ext: $!\n";
+ while(<I>) {
+ # skip blank lines and comments
+ /^#/ && next;
+ s/^\s+//;
+ /^\w/ || next;
+ s/\s+$//;
+
+ ($keym, $lg, $desc) = split(/:/);
+ if (! -r "$keym" && ! -r "$dir/$keym" &&
+ !grep(/$keym/, @langsupport)) {
+ warn "$keym not found!\n" if $verbose;
+ next;
+ }
+
+ # set empty language to default language
+ $lg = $lang_default if $lg eq "";
+
+ # save language
+ if ($show) {
+ foreach $e (split(/,/, $lg)) {
+ push(@langlist, $e) if !grep($_ eq $e, @langlist);
+ }
+ }
+
+ # 4) your choise if exist
+ # 3) long match e.g. 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 kommalist
+ # higher match overwrite lower
+ # last entry overwrite previous if exist twice in database
+
+ # found your favorite language :-)
+ if ($lg =~ /^(.+,)?$lang(,.+)?$/) {
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 4;
+ } elsif ($mark{$keym} <= 3 && $lg =~ /^(.+,)?$dialect(,.+)?$/) {
+ # dialect
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 3;
+ } elsif ($mark{$keym} <= 2 && $lg =~ /^(.+,)?$lang_abk(,.+)?$/) {
+ # abrevation
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 2;
+ } elsif ($mark{$keym} <= 1 && $lg =~ /^(.+,)?$lang_default(,.+)?$/) {
+ # default
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 1;
+ } elsif ($mark{$keym} <= 0) {
+ # any
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 0;
+ }
+ }
+ close I;
+
+ if ($show) {
+ @langlist = sort(@langlist);
+ print "Currently supported languages: @langlist\n";
+ exit(0);
+ }
+
+ # remove variables from list
+ local($ee);
+ foreach $e (@langsupport) {
+ ($ee = $e) =~ y/A-Z/a-z/;
+ eval "\$$ee = \"$keymap{$e}\"";
+ #warn "$e \$$ee = \"$keymap{$e}\"";
+ delete $keymap{$e};
+ }
+ #warn "$font $font_default $font_current\n";
+
+
+ # look for keymaps which are not in database
+ opendir(D, "$dir") || warn "$dir: $!\n";
+ foreach $e (readdir(D)) {
+ if ($e =~ /^[a-z].*(kbd|fnt)$/ && !$keymap{$e}) {
+ warn "$e not in database\n" if $verbose;
+ $keymap{$e} = $e;
+ $keymap{$e} =~ s/\.(kbd|fnt)$//;
+ }
+ }
+ closedir D;
+
+ # sort menu, font 8x8 is less than 8x14 and 8x16
+ foreach $e (sort(keys %keymap)) {
+ push(@a, "\"$keymap{$e}\" \"\"");
+ }
+ # side effects to @a
+ grep(s/8x8/8x08/, @a);
+ @a = sort @a;
+ grep(s/8x08/8x8/, @a);
+
+ if ($print) {
+ foreach (@a) {
+ s/"//g; #"
+ print "$_\n";
+ }
+ exit;
+ }
+
+ return @a;
+}
+
+sub dialog {
+ srand;
+ local(@argv) = @_;
+ local($tmp) = "/tmp/_kbd_lang" . rand(9999);
+
+ $dialog = "/usr/bin/dialog \\
+--clear \\
+--title \"Keyboard Menu\" \\
+--menu \"$menu\" \\
+-1 -1 10";
+
+ ## *always* start right font, don't believe that your current font
+ ## is equal with default font in /etc/rc.conf
+ ## see also at end of this function
+ ## if ($font) {
+
+ # 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 && $font ne $font_current) {
+ &vidcontrol($font);
+ }
+
+ # start dialog
+ system("$dialog @argv 2> $tmp");
+
+ if (!$?) {
+ $choise = `cat $tmp`;
+ foreach $e (keys %keymap) {
+ if ($keymap{$e} eq $choise) {
+ if ($program eq "kbdmap") {
+ system("kbdcontrol -l $dir/$e\n") unless $x11;
+ print "keymap=$e", "\n";
+ } else {
+ &vidcontrol("$dir/$e");
+ $_ = $e;
+ if (/^.*\-(.*)\.fnt/) {
+ $font=$1
+ } else { $font="unknown" }
+ print "font$font=$e", "\n";
+ }
+ last;
+ }
+ }
+ # } else {
+ } elsif ($font && $font ne $font_current) {
+ # cancel, restore old font
+ &vidcontrol($font_current);
+ }
+ unlink $tmp;
+ exit($?);
+}
+
+sub usage {
+ warn <<EOF;
+usage: $program\t[-K] [-V] [-d|-default] [-h|-help] [-l|-lang language]
+\t\t[-p|-print] [-r|-restore] [-s|-show] [-v|-verbose]
+EOF
+ exit 1;
+}
+
+# Argumente lesen
+sub parse {
+ local(@argv) = @_;
+
+ while($_ = $argv[0], /^-/) {
+ shift @argv;
+ last if /^--$/;
+ if (/^--?(h|help|\?)$/) { &usage; }
+ elsif (/^-(v|verbose)$/) { $verbose = 1; }
+ elsif (/^-(l|lang)$/) { $lang = &lang($argv[0]); shift @argv; }
+ elsif (/^-(d|default)$/) { $lang = $lang_default }
+ elsif (/^-(s|show)$/) { $show = 1 }
+ elsif (/^-(p|print)$/) { $print = 1 }
+ elsif (/^-(r|restore)$/) { &vidcontrol($font_current); exit(0) }
+ elsif (/^-K$/) { $dir = $keymapdir; }
+ elsif (/^-V$/) { $dir = $fontdir; }
+ else { &usage }
+ }
+}
+
+# main
+&variables_static; # read variables
+&parse(@ARGV); # parse arguments
+&dialog(&menu_read); # start dialog and kbdcontrol/vidcontrol
diff --git a/usr.sbin/kernbb/Makefile b/usr.sbin/kernbb/Makefile
new file mode 100644
index 0000000..92a416f
--- /dev/null
+++ b/usr.sbin/kernbb/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= kernbb
+MAN8= kernbb.8
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/kernbb/kernbb.8 b/usr.sbin/kernbb/kernbb.8
new file mode 100644
index 0000000..b62c0a4
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: kernbb.8,v 1.6 1997/09/22 06:30:04 charnier Exp $
+.\"
+.Dd May 22, 1995
+.Dt KERNBB 8
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm kernbb
+.Nd generate a dump of the kernels basic-block profile buffers
+.Sh SYNOPSIS
+.Nm kernbb
+.Sh DESCRIPTION
+.Nm Kernbb
+is a tool used to dump the basic-block profiling buffers of the running
+kernel.
+.Pp
+At least one source file in the running kernel must have been compiled
+with the
+.Fl a
+option.
+.Pp
+The output format is ascii, consisting of one line per record with the
+following fields: filename, linenumber, procedure, address, count
+of executions, length of the basic-block in bytes and the product of
+the previous two fields.
+.Sh FILES
+.Bl -tag -width /dev/kmemx -compact
+.It Pa /kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr cc 1
+.Sh AUTHORS
+The
+.Nm
+command was written by
+.An Poul-Henning Kamp ,
+along with the kernel-support.
diff --git a/usr.sbin/kernbb/kernbb.c b/usr.sbin/kernbb/kernbb.c
new file mode 100644
index 0000000..03bfc8a
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.c
@@ -0,0 +1,139 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "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[] =
+ "$Id: kernbb.c,v 1.9 1997/09/22 06:30:04 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAXBB 32768
+
+struct bb {
+ u_long zero_one;
+ u_long filename;
+ u_long counts;
+ u_long ncounts;
+ u_long next;
+ u_long addr;
+ u_long nwords;
+ u_long func;
+ u_long lineno;
+ u_long file;
+};
+
+struct nlist namelist[] = {
+ { "bbhead" },
+ { NULL }
+};
+
+u_long lineno[MAXBB];
+u_long counts[MAXBB];
+u_long addr[MAXBB];
+u_long func[MAXBB];
+u_long file[MAXBB];
+char *fn[MAXBB];
+char *pn[MAXBB];
+
+kvm_t *kv;
+
+int
+main()
+{
+ int i,j;
+ u_long l1,l2,l3,l4;
+ struct bb bb;
+ char buf[128];
+
+ 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);
+ l2 = bb.next;
+ if (!bb.ncounts)
+ continue;
+ if (bb.ncounts > MAXBB)
+ errx(1, "found %lu counts above limit of %u",
+ bb.ncounts, MAXBB);
+ kvm_read(kv,bb.lineno,lineno, bb.ncounts * sizeof lineno[0]);
+ kvm_read(kv,bb.counts,counts, bb.ncounts * sizeof counts[0]);
+ kvm_read(kv,bb.addr, addr, bb.ncounts * sizeof addr[0]);
+ kvm_read(kv,bb.file, file, bb.ncounts * sizeof file[0]);
+ kvm_read(kv,bb.func, func, bb.ncounts * sizeof func[0]);
+ l4 = 0;
+ for (i=0; i < bb.ncounts; i++) {
+ if (counts[i])
+ l4++;
+ if (!func[i] && i+1 < bb.ncounts)
+ func[i] = func[i+1];
+ }
+ if (!l4)
+ continue;
+ for (i=0; i < bb.ncounts; i++) {
+
+ if (0 && !counts[i])
+ continue;
+
+ if (!pn[i] && func[i]) {
+ kvm_read(kv,func[i], buf, sizeof buf);
+ buf[sizeof buf -1] = 0;
+ pn[i] = strdup(buf);
+ for(j=i+1;j<bb.ncounts;j++)
+ if (func[j] == func[i]) {
+ pn[j] = pn[i];
+ func[j] = 0;
+ }
+ }
+ if (!pn[i])
+ pn[i] = "-";
+ if (!fn[i] && file[i]) {
+ kvm_read(kv,file[i], buf, sizeof buf);
+ buf[sizeof buf -1] = 0;
+ fn[i] = strdup(buf);
+ for(j=i+1;j<bb.ncounts;j++)
+ if (file[j] == file[i]) {
+ fn[j] = fn[i];
+ file[j] = 0;
+ }
+ }
+ if (!fn[i])
+ fn[i] = "-";
+ l4 = 0;
+ if (i+1 < bb.ncounts)
+ l4 = addr[i+1] - addr[i];
+ printf("%s %5lu %s %lu %lu %lu %lu\n",
+ fn[i], lineno[i], pn[i], addr[i], counts[i], l4, counts[i] * l4);
+ }
+ for(i=0;i<bb.ncounts;i++) {
+ if (func[i] && pn[i])
+ free(pn[i]);
+ if (file[i] && fn[i])
+ free(fn[i]);
+ pn[i] = 0;
+ fn[i] = 0;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/keyadmin/Makefile b/usr.sbin/keyadmin/Makefile
new file mode 100644
index 0000000..d0a422e
--- /dev/null
+++ b/usr.sbin/keyadmin/Makefile
@@ -0,0 +1,6 @@
+# $ANA: Makefile,v 1.2 1996/06/13 20:11:05 wollman Exp $
+
+PROG= keyadmin
+MAN8= keyadmin.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/keyadmin/keyadmin.8 b/usr.sbin/keyadmin/keyadmin.8
new file mode 100644
index 0000000..eedb8cb
--- /dev/null
+++ b/usr.sbin/keyadmin/keyadmin.8
@@ -0,0 +1,239 @@
+.\"# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995
+.\"
+.\"COPYRIGHT NOTICE
+.\"
+.\"All of the documentation and software included in this software
+.\"distribution from the US Naval Research Laboratory (NRL) are
+.\"copyrighted by their respective developers.
+.\"
+.\"This software and documentation were developed at NRL by various
+.\"people. Those developers have each copyrighted the portions that they
+.\"developed at NRL and have assigned All Rights for those portions to
+.\"NRL. Outside the USA, NRL also has copyright on the software
+.\"developed at NRL. The affected files all contain specific copyright
+.\"notices and those notices must be retained in any derived work.
+.\"
+.\"NRL LICENSE
+.\"
+.\"NRL grants permission for redistribution and use in source and binary
+.\"forms, with or without modification, of the software and documentation
+.\"created at NRL provided that the following conditions are met:
+.\"
+.\"1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\"
+.\" This product includes software developed at the Information
+.\" Technology Division, US Naval Research Laboratory.
+.\"
+.\"4. Neither the name of the NRL nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\"THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
+.\"IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\"TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+.\"PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
+.\"CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+.\"EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\"PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+.\"PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\"LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 US Naval
+.\"Research Laboratory (NRL).
+.\"
+.\"----------------------------------------------------------------------*/
+.\"
+.\" $ANA: keyadmin.8,v 1.3 1996/06/13 20:15:57 wollman Exp $
+.\"
+.Dd June 13, 1996
+.Dt KEY 8
+.Os
+.Sh NAME
+.Nm keyadmin
+.Nd manually manipulate the kernel key management database
+.Sh SYNOPSIS
+.Nm keyadmin
+.Op Ar command Op Ar args
+.Sh DESCRIPTION
+The
+.Nm
+command is used to manually enter security associations into the kernel
+key/security association database. (See
+.Xr key 4 ).
+.Pp
+Almost any operation offered in the
+.Xr key 4
+API is available to privileged users running
+.Nm keyadmin .
+Until there is an implementation of an automated key management protocol,
+which will manipulate the key database in a manner similar to how
+.Xr routed 8
+or
+.Xr gated 8
+manipulates the routing tables,
+.Nm
+is the only way of establishing security associations.
+.Pp
+If
+.Nm
+is invoked without any arguments, it will enter an interactive mode, where
+the user can type in
+.Dq Ar command Op Ar args
+interactively, or use
+.Nm
+to enter a single
+.Dq Ar command Op Ar args .
+.Ar Command
+can be one of the following:
+.Bl -inset
+.It Nm del Ar type spi source destination
+.Pp
+Delete a security association between
+.Ar source
+and
+.Ar destination
+of the given
+.Ar type
+and
+.Ar spi .
+Example:
+.Bd -literal
+ delete esp 90125 anderson.yes.org rabin.yes.org
+.Ed
+.It Nm get Ar type spi source destination
+.Pp
+Retrieve (and print) a security association between
+.Ar source
+and
+.Ar destination
+of the given
+.Ar type
+and
+.Ar spi .
+Example:
+.Bd -literal
+ get ah 5150 eddie.vanhalen.com alex.vanhalen.com
+.Ed
+.It Nm dump
+.Pp
+Display the entire security association table. WARNING: This prints a lot
+of data.
+.It Nm load Ar filename
+.Pp
+Load security association information from a file formatted as documented in
+.Xr keys 5 . If
+.Dq -
+is specified for the
+.Ar filename ,
+load keys from the standard input.
+.It Nm save Ar filename
+.Pp
+Save security association information to a file formatted as documented in
+.Xr keys 5 . If
+.Dq -
+is specified for the
+.Ar filename ,
+place the key file out on the standard output. (This can be used as a sort
+of lightweight
+.Nm dump
+command.)
+NOTE: The save command must create a new file; it will not write into an
+existing file. This is to prevent writing into a world-readable file, or a
+named pipe or UNIX socket (see
+.Xr socket 2
+and
+.Xr mkfifo 1 ).
+.It Nm help Op command
+.Pp
+Offer brief help without an argument, or slightly more specific help on a
+particular command.
+.It Nm flush
+.Pp
+Erase all entries in the kernel security association table.
+.El
+
+.Pp
+The following values for
+.Ar command
+are only available by using
+.Nm
+in its interactive mode of operation:
+.Bl -inset
+.It Nm add Ar type spi source destination transform key
+.Op Ar iv
+.Pp
+Add a security association of a particular
+.Ar type
+and
+.Ar spi
+from a
+.Ar source
+to a
+.Ar destination ,
+using a particular
+.Ar transform
+and
+.Ar key .
+If a transform requires an initialization vector, the
+.Ar iv
+argument contains it. This command is available only in interactive mode
+because
+.Nm
+makes no attempt to destroy its argument vector after use. A malicous user
+of the
+.Xr ps 1
+command could determine security keys if
+.Nm add
+were allowed to be used straight from the command line. Example:
+.Bd -literal
+ add esp 2112 temples.syrinx.org priests.syrinx.org des-cbc \\
+ a652a476a652a476 87ac9876deac9876
+.Ed
+.It Nm exit
+.It Nm quit
+.Pp
+Exit interaction with
+.Nm keyadmin .
+An EOF will also end interaction with
+.Nm keyadmin .
+.El
+.Sh SEE ALSO
+.Xr ipsec 4 ,
+.Xr key 4 ,
+.Xr route 4 ,
+.Xr gated 8 ,
+.Xr routed 8
+
+.Sh HISTORY
+The
+.Nm
+command first appeared in NRL's
+.Bx 4.4
+IPv6 networking distribution.
+.Nm Keyadmin
+started its life as a pipe dream thought up by Dan McDonald, and came to
+life through the excruciating efforts of Ran Atkinson, Dan McDonald,
+Craig Metz, and Bao Phan.
+The NRL version of the program was originally called
+.Nm key ,
+but was renamed to
+.Nm keyadmin
+because of the conflict with
+.Xr key 1 .
+.Sh BUGS
+.Nm Keyadmin
+needs a -n flag like
+.Xr route 8
+to avoid name lookups.
+.Pp
+The dump and save commands currently display the first 30 or so entries.
diff --git a/usr.sbin/keyadmin/keyadmin.c b/usr.sbin/keyadmin/keyadmin.c
new file mode 100644
index 0000000..1bbd3b8
--- /dev/null
+++ b/usr.sbin/keyadmin/keyadmin.c
@@ -0,0 +1,1251 @@
+/*----------------------------------------------------------------------
+ key.c: Main routines for the key(8) tool for manually managing
+ cryptographic keys and security associations inside the
+ Key Engine of the operating system.
+
+ Future Enhancements should support:
+ multiple sensitivity levels
+ OSPFv2 keys
+ RIPv2 keys
+ Triple DES for ESP
+ DES+MD5 for ESP
+
+ Copyright 1995 by Bao Phan, Randall Atkinson, & Dan McDonald,
+ All Rights Reserved. All Rights have been assigned to the
+ US Naval Research Laboratory. The NRL Copyright Notice and
+ License govern distribution and use of this software.
+----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------
+# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995
+
+COPYRIGHT NOTICE
+
+All of the documentation and software included in this software
+distribution from the US Naval Research Laboratory (NRL) are
+copyrighted by their respective developers.
+
+This software and documentation were developed at NRL by various
+people. Those developers have each copyrighted the portions that they
+developed at NRL and have assigned All Rights for those portions to
+NRL. Outside the USA, NRL also has copyright on the software
+developed at NRL. The affected files all contain specific copyright
+notices and those notices must be retained in any derived work.
+
+NRL LICENSE
+
+NRL grants permission for redistribution and use in source and binary
+forms, with or without modification, of the software and documentation
+created at NRL provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed at the Information
+ Technology Division, US Naval Research Laboratory.
+
+4. Neither the name of the NRL nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
+IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (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 US Naval
+Research Laboratory (NRL).
+
+----------------------------------------------------------------------*/
+
+/*
+ * $ANA: keyadmin.c,v 1.2 1996/06/13 19:42:40 wollman Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#ifdef INET6
+#include <netinet6/in6.h>
+#else /* INET6 */
+#if 0
+#include <netinet6/in6_types.h>
+#endif
+#endif /* INET6 */
+
+#ifdef IPSEC
+#include <netsec/ipsec.h>
+#endif
+#include <netkey/key.h>
+
+#ifdef INET6
+#include <netinet6/support.h>
+#endif
+
+#ifndef INET6 /* XXX */
+#define hostname2addr(a, b, c) gethostbyname(a)
+#define addr2hostname(a, b, c, d) gethostbyaddr((a), (b), (c))
+#endif
+
+#include <arpa/inet.h>
+
+int parse7 __P((int, char **));
+int parse4 __P((int, char **));
+int docmd __P((int, char **));
+
+#define KEYCMD_ARG_MAX 10
+
+#define KEYCMD_LOAD 1
+#define KEYCMD_SAVE 2
+#define KEYCMD_ADD 3
+#define KEYCMD_DEL 4
+#define KEYCMD_FLUSH 5
+#define KEYCMD_GET 6
+#define KEYCMD_DUMP 7
+#define KEYCMD_HELP 8
+#define KEYCMD_EXIT 9
+#define KEYCMD_SHELL 10
+
+struct nametonum {
+ char *name;
+ int num;
+ int flags;
+};
+
+char parse7usage[] = "<type> <spi> <src> <dst> <transform> <key> [iv]";
+char parse4usage[] = "<type> <spi> <src> <dst>";
+
+struct keycmd {
+ char *name;
+ int num;
+ int (*parse) __P((int, char **));
+ char *usage;
+ char *help;
+} keycmds[] = {
+ { "add", KEYCMD_ADD, parse7, parse7usage,
+ "Adds a specific key entry to the kernel key table." },
+ { "del", KEYCMD_DEL, parse4, parse4usage,
+ "Removes a specific key entry from the kernel key table." },
+ { "get", KEYCMD_GET, parse4, parse4usage,
+ "Retrieves a key entry from the kernel key table." },
+ { "dump", KEYCMD_DUMP, NULL, " ",
+ "Retrieves all key entries from the kernel key table." },
+ { "load", KEYCMD_LOAD, NULL, "{ <filename> | - }",
+ "Loads keys from a file or stdin into the kernel key table." },
+ { "save", KEYCMD_SAVE, NULL, "{ <filename> | - }",
+ "Saves keys from the kernel key table to a file or stdout." },
+ { "help", KEYCMD_HELP, NULL, "[command]",
+ "Provides limited on-line help. Read the man page for more." },
+ { "flush", KEYCMD_FLUSH, NULL, NULL,
+ "Clears the kernel key table." },
+ { "!", KEYCMD_SHELL, NULL, "[command]",
+ "Executes a subshell." },
+ { "exit", KEYCMD_EXIT, NULL, NULL,
+ "Exits the program." },
+ { "quit", KEYCMD_EXIT, NULL, NULL,
+ "Exits the program." },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+/* flags: index into algorithmtabs */
+
+struct nametonum keytypes[] = {
+#ifdef IPSEC
+ { "ah", KEY_TYPE_AH, 0 },
+ { "esp", KEY_TYPE_ESP, 1 },
+#endif
+ { "rsvp", KEY_TYPE_RSVP, 2 },
+ { "ospf", KEY_TYPE_OSPF, 3 },
+ { "rip", KEY_TYPE_RIPV2, 4 },
+ { NULL, 0, 0 }
+};
+
+#ifndef IPSEC_ALGTYPE_AUTH_MD5 /* XXX */
+#define IPSEC_ALGTYPE_AUTH_MD5 1
+#endif
+
+/* flags: true = iv req. */
+struct nametonum authalgorithms[] = {
+ { "md5", IPSEC_ALGTYPE_AUTH_MD5, 0 },
+#ifdef DEBUG
+ /* These provide no security but are useful for debugging the
+ kernel's ESP and Key Engine and PF_KEY routines */
+ { "dummy", IPSEC_ALGTYPE_AUTH_DUMMY, 0 },
+ { "cksum", IPSEC_ALGTYPE_AUTH_CKSUM, 0 },
+#endif
+ { NULL, 0, 0 }
+};
+
+#ifndef IPSEC_ALGTYPE_ESP_DES_CBC /* XXX */
+#define IPSEC_ALGTYPE_ESP_DES_CBC 1
+#endif
+
+/* flags: true = iv req. */
+struct nametonum encralgorithms[] = {
+ { "des-cbc", IPSEC_ALGTYPE_ESP_DES_CBC, 1 },
+#ifdef DEBUG
+ /* These provide no security but are useful for debugging the
+ kernel's ESP and Key Engine and PF_KEY routines */
+ { "dummy", IPSEC_ALGTYPE_ESP_DUMMY, 0 },
+#endif
+ { NULL, 0, 0 }
+};
+
+/*
+ * These numbers should be defined in a header file somewhere
+ * and shared with the consuming programs, once someone has
+ * actually written the support in those programs (rspvd,
+ * gated, and routed). Probably <protocols/*>...?
+ */
+#define RSVP_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum rsvpalgorithms[] = {
+ { "md5", RSVP_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+#define OSPF_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum ospfalgorithms[] = {
+ { "md5", OSPF_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+#define RIPV2_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum ripalgorithms[] = {
+ { "md5", RIPV2_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+/* NB: It is the ordering here that determines the values for the
+ flags specified above that are used to index into algorithmtabs[] */
+struct nametonum *algorithmtabs[] = {
+ authalgorithms,
+ encralgorithms,
+ rsvpalgorithms,
+ ospfalgorithms,
+ ripalgorithms,
+ NULL
+};
+
+char buffer[1024] = "\0";
+
+#define MAXRCVBUFSIZE 8 * 1024
+
+char key_message[sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ + MAX_KEY_SZ + MAX_IV_SZ];
+int key_messageptr;
+
+int keysock = -1;
+
+int keygetseqno = 1;
+int keydumpseqno = 1;
+pid_t mypid;
+
+
+/*----------------------------------------------------------------------
+ help: Print appropriate help message on stdout.
+
+----------------------------------------------------------------------*/
+int help(cmdname)
+ char *cmdname;
+{
+ int i;
+
+ if (cmdname) {
+ for (i = 0; keycmds[i].name; i++)
+ if (!strcasecmp(keycmds[i].name, cmdname))
+ break;
+
+ if (!keycmds[i].name) {
+ warnx("unknown command: %s", cmdname);
+ return 0;
+ }
+
+ printf("%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
+ keycmds[i].usage ? keycmds[i].usage : "");
+
+ if (keycmds[i].help)
+ puts(keycmds[i].help);
+ } else {
+ for (i = 0; keycmds[i].name; i++)
+ printf("\t%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
+ keycmds[i].usage ? keycmds[i].usage : "");
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ usage: print suitable usage message on stdout.
+
+----------------------------------------------------------------------*/
+static void
+usage()
+{
+
+ fprintf(stderr, "usage: keyadmin <command> <args>\n");
+}
+
+/*----------------------------------------------------------------------
+ parsekey: parse argument into a binary key and also record
+ the length of the resulting key.
+----------------------------------------------------------------------*/
+int parsekey(key, keylen, arg)
+ u_int8_t *key;
+ u_int8_t *keylen;
+ char *arg;
+{
+ int i, j, k, l;
+ u_int8_t thisbyte;
+
+ i = strlen(arg);
+
+ if ((i == 1) && (arg[0] == '0')) {
+ *keylen = 0;
+ return 0;
+ }
+
+ if ((i % 2)) {
+ printf("Invalid number \"%s\"\n", arg);
+ return -1;
+ }
+
+ j = 1;
+ k = l = thisbyte = 0;
+
+ while(arg[k]) {
+ if ((arg[k] >= '0') && (arg[k] <= '9'))
+ thisbyte |= arg[k] - '0';
+ else
+ if ((arg[k] >= 'a') && (arg[k] <= 'f'))
+ thisbyte |= arg[k] - 'a' + 10;
+ else
+ if ((arg[k] >= 'A') && (arg[k] <= 'F'))
+ thisbyte |= arg[k] - 'A' + 10;
+ else {
+ printf("Invalid hex number \"%s\"\n", arg);
+ return 1;
+ }
+
+ if (!(j % 2))
+ key[l++] = thisbyte;
+
+ thisbyte = (thisbyte << 4);
+ j++;
+ k++;
+ }
+
+ *keylen = l;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsenametonum: converts command-line name into index number.
+
+----------------------------------------------------------------------*/
+int parsenametonum(tab, arg)
+ struct nametonum *tab;
+ char *arg;
+{
+ int i;
+
+ for (i = 0; tab[i].name; i++)
+ if (!strcasecmp(tab[i].name, arg))
+ return i;
+
+ if (!tab[i].name)
+ return -1;
+}
+
+/*----------------------------------------------------------------------
+ parsesockaddr: Convert hostname arg into an appropriate sockaddr.
+
+----------------------------------------------------------------------*/
+int parsesockaddr(sockaddr, arg)
+ struct sockaddr *sockaddr;
+ char *arg;
+{
+ struct hostent *hostent;
+ struct in_addr in_addr, *in_addrp;
+#ifdef INET6
+ struct in_addr6 in_addr6, *in_addr6p;
+#endif /* INET6 */
+
+ if ((hostent = hostname2addr(arg, AF_INET, 0)))
+ if ((hostent->h_addrtype == AF_INET) &&
+ (hostent->h_length == sizeof(struct in_addr))) {
+ in_addrp = ((struct in_addr *)hostent->h_addr_list[0]);
+ goto fillin4;
+ }
+
+ if (ascii2addr(AF_INET, arg, (char *)&in_addr) ==
+ sizeof(struct in_addr)) {
+ in_addrp = &in_addr;
+ goto fillin4;
+ }
+
+#ifdef INET6
+ if (hostent = hostname2addr(arg, AF_INET6))
+ if ((hostent->h_addrtype == AF_INET6) &&
+ (hostent->h_length == sizeof(struct in_addr6))) {
+ in_addr6p = ((struct in_addr6 *)hostent->h_addr_list[0]);
+ goto fillin6;
+ }
+
+ if (ascii2addr(AF_INET6, arg, (char *)&in_addr6)
+ == sizeof(struct in_addr6)) {
+ in_addr6p = &in_addr6;
+ goto fillin6;
+ }
+#endif /* INET6 */
+
+ warnx("unknown host \"%s\"", arg);
+ return 1;
+
+ fillin4:
+ bzero(sockaddr, sizeof(struct sockaddr_in));
+ sockaddr->sa_len = sizeof(struct sockaddr_in);
+ sockaddr->sa_family = AF_INET;
+ ((struct sockaddr_in *)sockaddr)->sin_addr = *in_addrp;
+ return 0;
+
+#ifdef INET6
+ fillin6:
+ bzero(sockaddr, sizeof(struct sockaddr_in6));
+ sockaddr->sa_len = sizeof(struct sockaddr_in6);
+ sockaddr->sa_family = AF_INET6;
+ ((struct sockaddr_in6 *)sockaddr)->sin6_addr = *in_addr6p;
+ return 0;
+#endif /* INET6 */
+}
+
+/*----------------------------------------------------------------------
+ dummyfromaddr: Creates a zeroed sockaddr of family af.
+
+----------------------------------------------------------------------*/
+void dummyfromaddr(sa, af)
+ struct sockaddr *sa;
+ int af;
+{
+ int size;
+#ifdef INET6
+ size = (af == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+#else /* INET6 */
+ size = sizeof(struct sockaddr_in);
+#endif /* INET6 */
+ bzero((char *)sa, size);
+ sa->sa_family = af;
+ sa->sa_len = size;
+}
+
+/*
+ * Macros to ensure word alignment.
+ */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) \
+ { x += ROUNDUP(n); }
+
+
+/*----------------------------------------------------------------------
+ parse4: parse keytype, spi, src addr, and dest addr from argv (command line)
+ and stick in structure pointed to by key_messageptr.
+----------------------------------------------------------------------*/
+int parse4(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (argc < 4)
+ return 1;
+
+ if ((i = parsenametonum(keytypes, argv[0])) < 0)
+ return i;
+
+ ((struct key_msghdr *)key_message)->type = keytypes[i].num;
+
+ /* Should we zero check? */
+ ((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ /*
+ * We need to put a dummy from address since the kernel expects
+ * this to be in the message.
+ */
+#ifdef INET6
+ dummyfromaddr(key_message + key_messageptr, AF_INET6);
+#else /* INET6 */
+ dummyfromaddr(key_message + key_messageptr, AF_INET);
+#endif /* INET6 */
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parse7: parse keytype, spi, src addr, dest addr, alg type, key, and iv
+ from argv (command line)
+ and stick in structure pointed to by key_messageptr.
+----------------------------------------------------------------------*/
+int parse7(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j;
+
+ if (argc < 6)
+ return 1;
+
+ if ((i = parsenametonum(keytypes, argv[0])) < 0)
+ return i;
+
+ ((struct key_msghdr *)key_message)->type = keytypes[i].num;
+
+/* Should we zero check? */
+ ((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ /*
+ * We need to put a dummy from address since the kernel expects
+ * this to be in the message.
+ */
+#ifdef INET6
+ dummyfromaddr(key_message + key_messageptr, AF_INET6);
+#else /* INET6 */
+ dummyfromaddr(key_message + key_messageptr, AF_INET);
+#endif /* INET6 */
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if ((j = parsenametonum(algorithmtabs[keytypes[i].flags], argv[4])) < 0)
+ return j;
+
+ ((struct key_msghdr *)key_message)->algorithm =
+ algorithmtabs[keytypes[i].flags][j].num;
+
+ if ((argc < 7) && algorithmtabs[keytypes[i].flags][j].flags)
+ return 1;
+
+ if (parsekey(key_message + key_messageptr,
+ &(((struct key_msghdr *)key_message)->keylen), argv[5]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->keylen);
+
+ if (argc >= 7) {
+ if (parsekey(key_message + key_messageptr,
+ &(((struct key_msghdr *)key_message)->ivlen), argv[6]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->ivlen);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsecmdline:
+
+----------------------------------------------------------------------*/
+int parsecmdline(buffer, argv, argc)
+ char *buffer;
+ char **argv;
+ int *argc;
+{
+ int i = 0, iargc = 0;
+ char *head;
+
+ head = buffer;
+
+ while(buffer[i] && (iargc < KEYCMD_ARG_MAX)) {
+ if ((buffer[i] == '\n') || (buffer[i] == '#')) {
+ buffer[i] = 0;
+ if (*head)
+ argv[iargc++] = head;
+ break;
+ }
+ if ((buffer[i] == ' ') || (buffer[i] == '\t')) {
+ buffer[i] = 0;
+ if (*head)
+ argv[iargc++] = head;
+ head = &(buffer[++i]);
+ } else
+ i++;
+ };
+ argv[iargc] = NULL;
+ *argc = iargc;
+
+ return iargc ? 0 : 1;
+}
+
+
+/*----------------------------------------------------------------------
+ load: load keys from file filename into Key Engine.
+
+----------------------------------------------------------------------*/
+int load(filename)
+ char *filename;
+{
+ FILE *fh;
+ char buffer[1024], *buf, *largv[KEYCMD_ARG_MAX], *c;
+ int i, largc, left, line = 0;
+
+ if (strcmp(filename, "-")) {
+ if (!(fh = fopen(filename, "r")))
+ return -1;
+ } else
+ fh = stdin;
+
+ largv[0] = "add";
+
+ buf = buffer;
+ left = sizeof(buffer);
+
+ while(fgets(buf, left, fh)) {
+ line++;
+ if ((c = strchr(buffer, '\\'))) {
+ left = (sizeof(buffer) - 1) - (--c - buffer);
+ buf = c;
+ } else {
+ buffer[sizeof(buffer)-1] = 0;
+
+ if ((i = parsecmdline(buffer, &(largv[1]), &largc)) < 0)
+ return i;
+
+ if (!i) {
+ if ((i = docmd(++largc, largv))) {
+ if (i > 0) {
+ warnx("parse error on line %d of %s", line, filename);
+ return 0;
+ }
+ return i;
+ }
+ }
+
+ buf = buffer;
+ left = sizeof(buffer);
+ }
+ };
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsedata:
+
+----------------------------------------------------------------------*/
+int
+parsedata(km, kip)
+ struct key_msghdr *km;
+ struct key_msgdata *kip;
+{
+ char *cp, *cpmax;
+
+ if (!km)
+ return (-1);
+ if (!(km->key_msglen))
+ return (-1);
+
+ cp = (caddr_t)(km + 1);
+ cpmax = (caddr_t)km + km->key_msglen;
+
+#define NEXTDATA(x, n) \
+ { x += ROUNDUP(n); if (cp >= cpmax) { \
+ warnx("kernel returned a truncated message!"); return(-1); } }
+
+ /* Grab src addr */
+ kip->src = (struct sockaddr *)cp;
+ if (!kip->src->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->src->sa_len);
+
+ /* Grab dest addr */
+ kip->dst = (struct sockaddr *)cp;
+ if (!kip->dst->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->dst->sa_len);
+
+ /* Grab from addr */
+ kip->from = (struct sockaddr *)cp;
+ if (!kip->from->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->from->sa_len);
+
+ /* Grab key */
+ if (kip->keylen = km->keylen) {
+ kip->key = cp;
+ NEXTDATA(cp, km->keylen);
+ }
+
+ /* Grab iv */
+ if (kip->ivlen = km->ivlen)
+ kip->iv = cp;
+
+ cp += kip->ivlen;
+
+ printf("key: parsedata: difference=%d\n", cp - cpmax);
+ return (0);
+}
+
+
+/*----------------------------------------------------------------------
+ printkeyiv:
+
+----------------------------------------------------------------------*/
+void printkeyiv(fp, cp, len)
+ FILE *fp;
+ caddr_t cp;
+ int len;
+{
+ int i;
+ for (i=0; i<len; i++)
+ fprintf(fp, "%02x", (u_int8_t)*(cp+i));
+}
+
+/*----------------------------------------------------------------------
+ printsockaddr:
+
+----------------------------------------------------------------------*/
+void printsockaddr(fp, sa)
+ FILE *fp;
+ struct sockaddr *sa;
+{
+ struct hostent *hp;
+ char *addrp;
+ int len;
+
+#ifdef INET6
+ if (sa->sa_family == AF_INET6) {
+ len = sizeof(struct in_addr6);
+ addrp = (char *)&(((struct sockaddr_in6 *)sa)->sin6_addr);
+ } else {
+#endif /* INET6 */
+ len = sizeof(struct in_addr);
+ addrp = (char *)&(((struct sockaddr_in *)sa)->sin_addr);
+#ifdef INET6
+ }
+#endif /* INET6 */
+
+ if((hp = addr2hostname(addrp, len, sa->sa_family, 0)) != NULL)
+ fprintf(fp, "%s", hp->h_name);
+ else
+ fprintf(fp, "%s", addr2ascii(sa->sa_family, addrp, len, NULL));
+}
+
+/*----------------------------------------------------------------------
+ parsenumtoname:
+
+----------------------------------------------------------------------*/
+char *
+parsenumtoname(tab, num)
+ struct nametonum *tab;
+ int num;
+{
+ int i;
+ for (i = 0; tab[i].name; i++) {
+ if (num == tab[i].num)
+ return(tab[i].name);
+ }
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ parsenumtoflag:
+
+----------------------------------------------------------------------*/
+int
+parsenumtoflag(tab, num)
+ struct nametonum *tab;
+ int num;
+{
+ int i;
+ for (i = 0; tab[i].name; i++) {
+ if (num == tab[i].num)
+ return(tab[i].flags);
+ }
+ return(-1);
+}
+
+
+/*----------------------------------------------------------------------
+ printkeymsg:
+
+----------------------------------------------------------------------*/
+void printkeymsg(kmp, kdp)
+ struct key_msghdr *kmp;
+ struct key_msgdata *kdp;
+{
+
+ printf("type=%d(%s) ",kmp->type, parsenumtoname(keytypes, kmp->type));
+ printf("spi=%u ", kmp->spi);
+ printf("alogrithm=%u(%s) ", kmp->algorithm,
+ parsenumtoname(algorithmtabs[parsenumtoflag(keytypes, kmp->type)],
+ kmp->algorithm));
+ printf("state=0x%x ",kmp->state);
+
+ if (kmp->state != 0) {
+ if (kmp->state & K_USED)
+ printf("USED ");
+ if (kmp->state & K_UNIQUE)
+ printf("UNIQUE ");
+ if (kmp->state & K_LARVAL)
+ printf("LARVAL ");
+ if (kmp->state & K_ZOMBIE)
+ printf("ZOMBIE ");
+ if (kmp->state & K_DEAD)
+ printf("DEAD ");
+ if (kmp->state & K_INBOUND)
+ printf("INBOUND ");
+ if (kmp->state & K_OUTBOUND)
+ printf("OUTBOUND");
+ }
+ printf("\n");
+ printf("sensitivity_label=%d ",kmp->label);
+ printf("lifetype=%d ",kmp->lifetype);
+ printf("lifetime1=%d ",kmp->lifetime1);
+ printf("lifetime2=%d\n",kmp->lifetime2);
+ printf("key (len=%d):\t",kdp->keylen);
+ printkeyiv(stdout, kdp->key, kdp->keylen);
+ printf("\n");
+ printf("iv (len=%d):\t", kdp->ivlen);
+ printkeyiv(stdout, kdp->iv, kdp->ivlen);
+ printf("\n");
+ printf("src:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->src);
+ printf("\n");
+ printf("dst:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->dst);
+ printf("\n");
+/* printf("from:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->from); */
+ printf("\n");
+}
+
+/*----------------------------------------------------------------------
+ docmd:
+
+----------------------------------------------------------------------*/
+int docmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, j, seqno;
+ int fd;
+ FILE *fp;
+
+ if (!argv[0] || !argc)
+ return -1;
+
+ if (!argv[0][0])
+ return -1;
+
+ bzero(&key_message, sizeof(key_message));
+ key_messageptr = sizeof(struct key_msghdr);
+
+ for (i = 0; keycmds[i].name; i++)
+ if (!strcasecmp(keycmds[i].name, argv[0]))
+ break;
+
+ if (!keycmds[i].name)
+ return -1;
+
+ if (keycmds[i].parse)
+ if ((j = keycmds[i].parse(argc - 1, &(argv[1]))))
+ return j;
+
+ ((struct key_msghdr *)key_message)->key_msglen = key_messageptr;
+ ((struct key_msghdr *)key_message)->key_msgvers = 1;
+ ((struct key_msghdr *)key_message)->key_seq = 1;
+
+ switch(keycmds[i].num) {
+
+ case KEYCMD_ADD:
+#ifndef NOSANITYCHK
+ /*
+ * For now, we do sanity check of security association
+ * information here until we find a better place.
+ */
+ {
+ struct key_msghdr *kmp = (struct key_msghdr *)key_message;
+
+ if ((kmp->type == KEY_TYPE_AH ||
+ kmp->type == KEY_TYPE_ESP) && (kmp->spi < 256)) {
+ warnx("add: spi must be greater than 255");
+ return(0);
+ }
+
+ if (kmp->type == KEY_TYPE_ESP &&
+ (kmp->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC
+#ifdef IPSEC_ALGTYPE_ESP_3DES
+ || kmp->algorithm == IPSEC_ALGTYPE_ESP_3DES
+#endif
+ )) {
+ if (kmp->keylen != 8) {
+ warnx("add: key must be 8 bytes");
+ return (0);
+ }
+ if (kmp->ivlen != 4 && kmp->ivlen != 8) {
+ warnx("add: iv must be 4 or 8 bytes");
+ return (0);
+ }
+ }
+
+ if (kmp->type == KEY_TYPE_AH &&
+ kmp->algorithm == IPSEC_ALGTYPE_AUTH_MD5 && kmp->keylen == 0) {
+ warnx("add: no key specified");
+ return (0);
+ }
+ }
+#endif
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_ADD;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == EEXIST)
+ warnx("add: security association already exists");
+ else
+ warn("add");
+ return -1;
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_DEL:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_DELETE;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == ESRCH) {
+ warnx("delete: security association not found");
+ return 0;
+ } else {
+ warn("delete");
+ return -1;
+ }
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_GET:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_GET;
+ ((struct key_msghdr *)key_message)->key_seq = seqno = keygetseqno++;
+
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == ESRCH) {
+ warnx("get: security association not found");
+ return 0;
+ } else {
+ warn("get");
+ return (-1);
+ } /* endif ESRCH */
+ } /* endif write() */
+
+ {
+ int len;
+ struct key_msgdata keymsgdata;
+
+ len = sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ + MAX_KEY_SZ + MAX_IV_SZ;
+
+readmesg:
+ if (read(keysock, key_message, len) < 0) {
+ warn("read");
+ return -1;
+ }
+
+ if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
+ && (((struct key_msghdr *)&key_message)->key_msgtype==KEY_GET)
+ && (((struct key_msghdr *)&key_message)->key_seq==seqno))) {
+ fprintf(stderr, ".");
+ goto readmesg;
+ }
+
+ if (((struct key_msghdr *)&key_message)->key_errno != 0) {
+ printf("Error: kernel reporting errno=%d\n",
+ ((struct key_msghdr *)&key_message)->key_errno);
+ return 0;
+ }
+
+ if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
+ printf("get: can't parse reply\n");
+ return -1;
+ }
+ printf("\n");
+ printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
+ }
+ return (0);
+
+ case KEYCMD_FLUSH:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_FLUSH;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ warn("write");
+ return -1;
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_HELP:
+ return help((argc > 1) ? argv[1] : NULL);
+
+ case KEYCMD_SHELL:
+ if (argc > 1) {
+ char buffer[1024], *ap, *ep, *c;
+ int i;
+
+ ep = buffer + sizeof(buffer) - 1;
+ for (i = 1, ap = buffer; (i < argc) && (ap < ep); i++) {
+ c = argv[i];
+ while ((*(ap++) = *(c++)) && (ap < ep));
+ *(ap - 1) = ' ';
+ }
+ *(ap - 1) = 0;
+ system(buffer);
+ } else {
+ char *c = getenv("SHELL");
+ if (!c)
+ c = "/bin/sh";
+ system(c);
+ }
+ return 0;
+
+ case KEYCMD_EXIT:
+ exit(0);
+
+ case KEYCMD_LOAD:
+ if (argc != 2)
+ return 1;
+ return load(argv[1]);
+
+ case KEYCMD_SAVE:
+ if (argc != 2)
+ return 1;
+ if (!strcmp(argv[1], "-"))
+ fp = stdout;
+ else if ((fd = open(argv[1], O_CREAT | O_RDWR | O_EXCL,
+ S_IRUSR | S_IWUSR)) < 0) {
+ warn("open");
+ return 1;
+ } else if (!(fp = fdopen(fd, "w"))) {
+ warn("fdopen");
+ return 1;
+ }
+
+ case KEYCMD_DUMP:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_DUMP;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ warn("write");
+ return -1;
+ }
+
+ {
+ struct key_msgdata keymsgdata;
+
+readmesg2:
+ if (read(keysock, key_message, sizeof(key_message)) < 0) {
+ warn("read");
+ return -1;
+ }
+
+ if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
+ && (((struct key_msghdr *)&key_message)->key_msgtype==KEY_DUMP)))
+ goto readmesg2;
+
+ /*
+ * Kernel is done sending secassoc if key_seq == 0
+ */
+ if (((struct key_msghdr *)&key_message)->key_seq == 0) {
+ if ((keycmds[i].num == KEYCMD_SAVE) && (fp != stdout))
+ fclose(fp);
+ return 0;
+ }
+
+ if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
+ printf("get: can't parse reply\n");
+ goto readmesg2;
+ }
+ if (keycmds[i].num == KEYCMD_SAVE) {
+ char *keytype, *algorithm;
+
+ keytype = parsenumtoname(keytypes,
+ ((struct key_msghdr *)&key_message)->type);
+
+ algorithm = parsenumtoname(algorithmtabs[parsenumtoflag(keytypes,
+ ((struct key_msghdr *)&key_message)->type)],
+ ((struct key_msghdr *)&key_message)->algorithm);
+
+ fprintf(fp, "%s %u ", keytype,
+ ((struct key_msghdr *)&key_message)->spi);
+ printsockaddr(fp, (struct sockaddr *)(keymsgdata.src));
+ fprintf(fp, " ");
+ printsockaddr(fp, (struct sockaddr *)(keymsgdata.dst));
+ fprintf(fp, " ");
+ fprintf(fp, "%s ", algorithm);
+ printkeyiv(fp, keymsgdata.key, keymsgdata.keylen);
+ fprintf(fp, " ");
+ printkeyiv(fp, keymsgdata.iv, keymsgdata.ivlen);
+ fprintf(fp, "\n");
+ } else
+ printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
+ goto readmesg2;
+ }
+ return (0);
+ }
+ return (-1);
+}
+
+/*----------------------------------------------------------------------
+ main:
+
+----------------------------------------------------------------------*/
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j;
+ u_long rcvsize;
+
+ if (getuid())
+ errx(1, "this program is intended for the superuser only");
+
+ if (!(keysock = socket(PF_KEY, SOCK_RAW, 0))) {
+ warn("socket");
+ return -1;
+ }
+
+ for (rcvsize = MAXRCVBUFSIZE; rcvsize; rcvsize -= 1024) {
+ if (setsockopt(keysock, SOL_SOCKET, SO_RCVBUF, &rcvsize,
+ sizeof(rcvsize)) <= 0)
+ break;
+ }
+
+ mypid = getpid();
+ if (mypid < 0) {
+ warn("getpid");
+ return -1;
+ }
+
+ if (argc > 1) {
+ /*
+ * Attempt to do a single command, based on command line arguments.
+ */
+ if (strcasecmp(argv[1], "add") == 0)
+ errx(1, "cannot add keys from the command line. RTFM for why");
+ if ((i = docmd(argc - 1, &(argv[1])))) {
+ if (i > 0) {
+ for (j = 0; keycmds[j].name; j++)
+ if (!strcasecmp(keycmds[j].name, argv[1]))
+ break;
+
+ if (keycmds[j].name) {
+ fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
+ keycmds[j].usage ? " " : "",
+ keycmds[j].usage ? keycmds[j].usage : "");
+ exit(1);
+ }
+ }
+ usage();
+ }
+ return 0;
+ }
+
+ {
+ char buffer[1024];
+ char *iargv[KEYCMD_ARG_MAX];
+ int iargc;
+
+ while(1) {
+ printf("key> ");
+ if (!(fgets(buffer, sizeof(buffer), stdin)))
+ return -1;
+ buffer[sizeof(buffer)-1] = 0;
+ /*
+ * get command line, and parse into an argc/argv form.
+ */
+ if ((i = parsecmdline(buffer, iargv, &iargc)) < 0)
+ exit(1);
+ if (i > 0)
+ continue;
+ errno = 0;
+ /*
+ * given argc/argv, process argument as if it came from the command
+ * line.
+ */
+ if ((i = docmd(iargc, iargv))) {
+ if (i > 0) {
+ for (j = 0; keycmds[j].name; j++)
+ if (!strcasecmp(keycmds[j].name, iargv[0]))
+ break;
+
+ if (keycmds[j].name) {
+ fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
+ keycmds[j].usage ? " " : "",
+ keycmds[j].usage ? keycmds[j].usage : "");
+ } else
+ i = -1;
+ }
+ if (i < 0) {
+ if (errno)
+ warn("system error");
+ else
+ warnx("unrecognized command");
+ warnx("type 'help' if you need help");
+ };
+ };
+ };
+ };
+}
+
+/* EOF */
diff --git a/usr.sbin/keyadmin/keys b/usr.sbin/keyadmin/keys
new file mode 100644
index 0000000..b1657bf
--- /dev/null
+++ b/usr.sbin/keyadmin/keys
@@ -0,0 +1,18 @@
+# This is an example key file.
+
+# The format of entries in this file is as follows:
+# <type> <spi> <src> <dst> <transform> <key> [iv]
+#
+# where:
+#
+# <type> is currently one of { ah | esp }
+# <spi> is a decimal number
+# <src> is an IP address for the source this association applies to
+# <dst> is an IP address for the destination this assoc. applies to
+# <transform> is currently one of { md5 } for ah, { des-cbc } for esp
+# <key> is a hexadecimal key value (key length is derived from hex len)
+# [iv] is a hexadecimal initial value (length is derived from hex len)
+# [this field is required for des-cbc, ignored for others]
+
+ah 1142 ::0 ::0 md5 0123456789abcdef0123456789abcdef
+esp 1984 ::0 ::0 des-cbc 0123456789abcdef 11223344
diff --git a/usr.sbin/keyserv/Makefile b/usr.sbin/keyserv/Makefile
new file mode 100644
index 0000000..f08135f
--- /dev/null
+++ b/usr.sbin/keyserv/Makefile
@@ -0,0 +1,28 @@
+# $Id: Makefile,v 1.3 1998/05/09 13:32:37 bde Exp $
+
+PROG= keyserv
+SRCS= keyserv.c setkey.c keyserv_uid.c crypt_svc.c crypt_server.c crypt.h
+
+MAN8= keyserv.8
+
+CFLAGS+= -DKEYSERV_RANDOM -DBROKEN_DES -I.
+.if $(OBJFORMAT) == elf
+CFLAGS+= -DOBJFORMAT_ELF
+.endif
+
+DPADD= ${LIBMP} ${LIBRPCSVC}
+LDADD= -lmp -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..5b944ae
--- /dev/null
+++ b/usr.sbin/keyserv/crypt_server.c
@@ -0,0 +1,309 @@
+/*
+ * 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[] =
+ "$Id: crypt_server.c,v 1.3 1997/09/23 06:36:26 charnier Exp $";
+#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)__P((char *, int, struct desparams *)) = NULL;
+
+static void *dlhandle;
+
+#ifndef _PATH_USRLIB
+#define _PATH_USRLIB "/usr/lib"
+#endif
+
+#ifndef LIBDES
+#ifdef OBJFORMAT_ELF
+#define LIBDES "libdes.so.3"
+#else
+#define LIBDES "libdes.so.3."
+#endif /* OBJFORMAT_ELF */
+#endif
+
+void load_des(warn, libpath)
+ int warn;
+ char *libpath;
+{
+ DIR *dird;
+ struct dirent *dirp;
+ char dlpath[MAXPATHLEN];
+ int minor = -1;
+ int len;
+
+ if (libpath == NULL) {
+#ifdef OBJFORMAT_ELF
+ snprintf(dlpath, sizeof(dlpath), "%s/%s", _PATH_USRLIB, LIBDES);
+#else
+ len = strlen(LIBDES);
+ if ((dird = opendir(_PATH_USRLIB)) == NULL)
+ err(1, "opendir(/usr/lib) failed");
+
+ while ((dirp = readdir(dird)) != NULL) {
+ /* must have a minor number */
+ if (strlen(dirp->d_name) <= len)
+ continue;
+ if (!strncmp(dirp->d_name, LIBDES, len)) {
+ if (atoi((dirp->d_name + len + 1)) > minor) {
+ minor = atoi((dirp->d_name + len + 1));
+ snprintf(dlpath,sizeof(dlpath),"%s/%s",
+ _PATH_USRLIB, dirp->d_name);
+ }
+ }
+ }
+
+ closedir(dird);
+#endif /* OBJFORMAT_ELF */
+ } else
+ snprintf(dlpath, sizeof(dlpath), "%s", libpath);
+
+ if (dlpath != NULL && (dlhandle = dlopen(dlpath, 0444)) != NULL)
+#ifdef OBJFORMAT_ELF
+ _my_crypt = (int (*)())dlsym(dlhandle, "_des_crypt");
+#else
+ _my_crypt = (int (*)())dlsym(dlhandle, "__des_crypt");
+#endif /* OBJFORMAT_ELF */
+
+ 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..b44e787
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.8
@@ -0,0 +1,77 @@
+.\" @(#)keyserv.1m 1.21 93/07/14 SMI; from SVr4
+.\"macro stdmacro
+.\" Copyright 1989 AT&T
+.\" @(#)keyserv.8c 1.8 89/03/29 SMI;
+.\".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 keyserv
+.Op Fl d
+.Op Fl D
+.Op Fl n
+.Op Fl p Ar path
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Keyserv
+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 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 keyserv ,
+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 keyserv .
+.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..e49dded
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.c
@@ -0,0 +1,803 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 <rpc/pmap_clnt.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;
+ register SVCXPRT *transp;
+ int sock = RPC_ANYSOCK;
+ int warn = 0;
+ char *path = 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(066); /* paranoia */
+ if (geteuid() != 0)
+ errx(1, "keyserv must be run as root");
+ setmodulus(HEXMODULUS);
+ getrootkey(&masterkey, nflag);
+
+
+ /* Create services. */
+
+ (void) pmap_unset(KEY_PROG, KEY_VERS);
+ (void) pmap_unset(KEY_PROG, KEY_VERS2);
+ unlink(KEYSERVSOCK);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_UDP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, udp)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_UDP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_TCP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, tcp)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_TCP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, tcp)");
+
+ transp = svcunix_create(sock, 0, 0, KEYSERVSOCK);
+ chmod(KEYSERVSOCK, 0666);
+ if (transp == NULL)
+ errx(1, "cannot create AF_UNIX service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, 0))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, 0))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)");
+ if (!svc_register(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, 0))
+ errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)");
+
+ 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;
+{
+ 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));
+ }
+#ifdef KEYSERV_RANDOM
+ srandom(seed);
+ master->key.low = random();
+ master->key.high = random();
+ srandom(seed);
+#else
+ /* use stupid dangerous bad rand() */
+ srand(seed);
+ master->key.low = rand();
+ master->key.high = rand();
+ srand(seed);
+#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;
+ bool_t(*xdr_argument)(), (*xdr_result)();
+ char *(*local) ();
+ uid_t uid = -1;
+ int check_auth;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case KEY_SET:
+ xdr_argument = xdr_keybuf;
+ xdr_result = xdr_int;
+ local = (char *(*)()) key_set_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_ENCRYPT:
+ xdr_argument = xdr_cryptkeyarg;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT:
+ xdr_argument = xdr_cryptkeyarg;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GEN:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_des_block;
+ local = (char *(*)()) key_gen_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_GETCRED:
+ xdr_argument = xdr_netnamestr;
+ xdr_result = xdr_getcredres;
+ local = (char *(*)()) key_getcred_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_ENCRYPT_PK:
+ xdr_argument = xdr_cryptkeyarg2;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT_PK:
+ xdr_argument = xdr_cryptkeyarg2;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+
+ case KEY_NET_PUT:
+ xdr_argument = xdr_key_netstarg;
+ xdr_result = 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 = 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 = 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((char *) &argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (uid, &argument);
+ if (!svc_sendreply(transp, xdr_result, (char *) result)) {
+ if (debugging)
+ (void) fprintf(stderr, "unable to reply\n");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&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_in *remote;
+
+ remote = svc_getcaller(trans);
+ if (remote->sin_family == AF_INET) {
+ if (debugging)
+ fprintf(stderr, "client didn't use AF_UNIX\n");
+ return (0);
+ }
+
+ if (__rpc_get_local_uid(&uid, trans) < 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..6e2402d
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.h
@@ -0,0 +1,19 @@
+
+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 int __rpc_get_local_uid __P(( uid_t * , SVCXPRT * ));
+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/keyserv_uid.c b/usr.sbin/keyserv/keyserv_uid.c
new file mode 100644
index 0000000..add3d33
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv_uid.c
@@ -0,0 +1,76 @@
+/*
+ * 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 <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/key_prot.h>
+#include <rpc/des.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#include "keyserv.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+/*
+ * XXX should be declared somewhere
+ */
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+int
+__rpc_get_local_uid(uid, transp)
+ uid_t *uid;
+ SVCXPRT *transp;
+{
+ struct cmessage *cm;
+
+ if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
+ transp->xp_verf.oa_base == NULL ||
+ transp->xp_verf.oa_flavor != AUTH_UNIX)
+ return(-1);
+
+ cm = (struct cmessage *)transp->xp_verf.oa_base;
+ if (cm->cmsg.cmsg_type != SCM_CREDS)
+ return(-1);
+
+ *uid = cm->cmcred.cmcred_euid;
+ return(0);
+}
diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c
new file mode 100644
index 0000000..8403913
--- /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[] =
+ "$Id$";
+#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..20fcf68
--- /dev/null
+++ b/usr.sbin/kgmon/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= kgmon
+MAN8= kgmon.8
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+#
+# This program may safely be run setuid-root to allow non-root
+# users to start, stop, and reset profiling buffers.
+#
+#BINOWN=root
+#BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8
new file mode 100644
index 0000000..0ec962d
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.8
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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
+.\"
+.Dd June 6, 1993
+.Dt KGMON 8
+.Os BSD 4.2
+.Sh NAME
+.Nm kgmon
+.Nd generate a dump of the operating system's profile buffers
+.Sh SYNOPSIS
+.Nm kgmon
+.Op Fl Bbhpr
+.Op Fl M core
+.Op Fl N system
+.Sh DESCRIPTION
+.Nm Kgmon
+is a tool 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 ``/dev/kmem''.
+.It Fl N
+Extract the name list from the specified system instead of the
+default ``/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 /dev/kmemx -compact
+.It Pa /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
+command 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..4d2bd4f
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.c
@@ -0,0 +1,505 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 <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 __P((struct kvmvars *));
+int getprofhz __P((struct kvmvars *));
+void kern_readonly __P((int));
+int openfiles __P((char *, char *, struct kvmvars *));
+void setprof __P((struct kvmvars *kvp, int state));
+void dumpstate __P((struct kvmvars *kvp));
+void reset __P((struct kvmvars *kvp));
+static void usage __P((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;
+{
+ int mib[3], state, size, 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;
+{
+ int mib[3], size;
+
+ 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;
+ }
+ if (size != sizeof kvp->gpm)
+ errx(4, "cannot get gmonparam: %s",
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ 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;
+ int mib[3], sz, 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;
+ int mib[3], i;
+ 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;
+ if (h.profrate == 0)
+ h.profrate = getprofhz(kvp); /* ancient kernel */
+ 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;
+{
+ int mib[2], size, 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/kvm_mkdb/Makefile b/usr.sbin/kvm_mkdb/Makefile
new file mode 100644
index 0000000..715c903
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= kvm_mkdb
+SRCS= kvm_mkdb.c nlist.c testdb.c
+MAN8= kvm_mkdb.8
+CFLAGS+=-DDO_AOUT
+CFLAGS+=-DDO_ELF
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kvm_mkdb/extern.h b/usr.sbin/kvm_mkdb/extern.h
new file mode 100644
index 0000000..6d72268
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/extern.h
@@ -0,0 +1,38 @@
+/*-
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+void create_knlist __P((char *, DB *));
+void error __P((char *));
+int testdb __P(());
diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.8 b/usr.sbin/kvm_mkdb/kvm_mkdb.8
new file mode 100644
index 0000000..0bfcbea
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/kvm_mkdb.8
@@ -0,0 +1,70 @@
+.\" 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.
+.\"
+.\" @(#)kvm_mkdb.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt KVM_MKDB 8
+.Os
+.Sh NAME
+.Nm kvm_mkdb
+.Nd create kernel database
+.Sh SYNOPSIS
+.Nm kvm_mkdb
+.Op file
+.Sh DESCRIPTION
+.Nm Kvm_mkdb
+creates a database in
+.Pa /var/db
+containing information about the specified file.
+If no file is specified,
+.Pa /kernel
+is used by default.
+The file is named ``kvm_filename.db'', where ``filename'' is the
+name of the file read.
+Various library routines consult this database.
+The only information currently stored is the kernel namelist, which is
+used by the
+.Xr kvm_nlist 3
+function, however, in the future the database may contain other static
+information about the current system.
+.Sh FILES
+.Bl -tag -width /var/db/kvm_kernel.db -compact
+.It Pa /kernel
+.It Pa /var/db/kvm_kernel.db
+.El
+.Sh SEE ALSO
+.Xr kvm_nlist 3
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.c b/usr.sbin/kvm_mkdb/kvm_mkdb.c
new file mode 100644
index 0000000..0284c55
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/kvm_mkdb.c
@@ -0,0 +1,126 @@
+/*-
+ * 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 const 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[] = "@(#)kvm_mkdb.c 8.3 (Berkeley) 5/4/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void usage __P((void));
+
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 128, /* ffactor */
+ 1024, /* nelem */
+ 2048 * 1024, /* cachesize */
+ NULL, /* hash() */
+ 0 /* lorder */
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB *db;
+ int ch;
+ char *p, *nlistpath, *nlistname, dbtemp[MAXPATHLEN], dbname[MAXPATHLEN];
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+
+ /* If the existing db file matches the currently running kernel, exit */
+ if (testdb())
+ exit(0);
+
+#define basename(cp) ((p = rindex((cp), '/')) != NULL ? p + 1 : (cp))
+ nlistpath = argc > 0 ? argv[0] : (char *)getbootfile();
+ nlistname = basename(nlistpath);
+
+ (void)snprintf(dbtemp, sizeof(dbtemp), "%skvm_%s.tmp",
+ _PATH_VARDB, nlistname);
+ (void)snprintf(dbname, sizeof(dbname), "%skvm_%s.db",
+ _PATH_VARDB, nlistname);
+ (void)umask(0);
+
+ db = dbopen(dbtemp, O_CREAT | O_EXLOCK | O_TRUNC | O_RDWR,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, DB_HASH, &openinfo);
+ if (db == NULL)
+ err(1, "%s", dbtemp);
+ create_knlist(nlistpath, db);
+ if (db->close(db))
+ err(1, "%s", dbtemp);
+ if (rename(dbtemp, dbname))
+ err(1, "rename %s to %s", dbtemp, dbname);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: kvm_mkdb [file]\n");
+ exit(1);
+}
diff --git a/usr.sbin/kvm_mkdb/nlist.c b/usr.sbin/kvm_mkdb/nlist.c
new file mode 100644
index 0000000..e3e0666
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/nlist.c
@@ -0,0 +1,368 @@
+/*-
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: nlist.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: nlist.c,v 1.9 1998/08/17 08:46:46 dfr Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+
+#include <a.out.h>
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#ifdef DO_ELF
+#include <elf.h>
+#endif
+
+typedef struct nlist NLIST;
+#define _strx n_un.n_strx
+#define _name n_un.n_name
+
+#define badfmt(str) errx(1, "%s: %s: %s", kfile, str, strerror(EFTYPE))
+static char *kfile;
+
+#if defined(DO_AOUT)
+
+int
+__aout_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ register int nsyms;
+ struct exec *ebuf;
+ NLIST *nbuf;
+ DBT data, key;
+ int fd;
+ char *strtab;
+ u_char *filep;
+ char *vp;
+ struct stat sst;
+ long cur_off, voff;
+
+ kfile = name;
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ err(1, "%s", name);
+
+ fstat(fd,&sst);
+
+ filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (filep == (u_char*)MAP_FAILED)
+ err(1, "mmap failed");
+
+ /* Read in exec structure. */
+ ebuf = (struct exec *) filep;
+
+ /* Check magic number and symbol count. */
+ if (N_BADMAG(*ebuf))
+ badfmt("bad magic number");
+ if (!ebuf->a_syms)
+ badfmt("stripped");
+
+ strtab = filep + N_STROFF(*ebuf) + sizeof (int);
+
+ /* Seek to symbol table. */
+ cur_off = N_SYMOFF(*ebuf);
+
+ /* Read each symbol and enter it into the database. */
+ nsyms = ebuf->a_syms / sizeof(struct nlist);
+ while (nsyms--) {
+
+ nbuf = (NLIST *)(filep + cur_off);
+ cur_off += sizeof(NLIST);
+
+ if (!nbuf->_strx || nbuf->n_type&N_STAB)
+ continue;
+
+ key.data = (u_char *)strtab + nbuf->_strx - sizeof(long);
+ key.size = strlen((char *)key.data);
+ data.data = (u_char *)nbuf;
+ data.size = sizeof(NLIST);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ if (1 && strcmp((char *)key.data, VRS_SYM) == 0) {
+#ifndef KERNTEXTOFF
+/*
+ * XXX
+ * The FreeBSD bootloader loads the kernel at the a_entry address, meaning
+ * that this is where the kernel starts. (not at KERNBASE)
+ *
+ * This may be introducing an i386 dependency.
+ */
+#if defined(__FreeBSD__)
+#define KERNTEXTOFF ebuf->a_entry
+#else
+#define KERNTEXTOFF KERNBASE
+#endif
+#endif
+ /*
+ * Calculate offset relative to a normal (non-kernel)
+ * a.out. KERNTEXTOFF is where the kernel is really
+ * loaded; N_TXTADDR is where a normal file is loaded.
+ * From there, locate file offset in text or data.
+ */
+ voff = nbuf->n_value - KERNTEXTOFF + N_TXTADDR(*ebuf);
+ if ((nbuf->n_type & N_TYPE) == N_TEXT)
+ voff += N_TXTOFF(*ebuf) - N_TXTADDR(*ebuf);
+ else
+ voff += N_DATOFF(*ebuf) - N_DATADDR(*ebuf);
+
+ vp = filep + voff;
+
+ key.data = (u_char *)VRS_KEY;
+ key.size = sizeof(VRS_KEY) - 1;
+ data.data = vp;
+ data.size = strchr(vp, '\n') - vp + 1;
+
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Restore to original values. */
+ data.size = sizeof(NLIST);
+ }
+ }
+ return(0);
+}
+
+
+#endif /* DO_AOUT */
+
+#ifdef DO_ELF
+
+static void elf_sym_to_nlist __P((struct nlist *, Elf_Sym *, Elf_Shdr *, int));
+
+int
+__elf_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ register caddr_t strtab;
+ register off_t symstroff = 0, symoff = 0;
+ register u_long symsize = 0;
+ register u_long kernvma, kernoffs;
+ register int i;
+ Elf_Sym *sbuf;
+ size_t symstrsize;
+ char *shstr, buf[1024];
+ Elf_Ehdr *eh;
+ Elf_Shdr *sh = NULL;
+ DBT data, key;
+ NLIST nbuf;
+ int fd;
+ u_char *filep;
+ struct stat sst;
+
+ kfile = name;
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ err(1, "%s", name);
+
+ fstat(fd, &sst);
+
+ /* Check for files too large to mmap. */
+ /* XXX is this really possible? */
+ if (sst.st_size > SIZE_T_MAX) {
+ badfmt("corrupt file");
+ }
+ filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (filep == (u_char*)MAP_FAILED)
+ err(1, "mmap failed");
+
+ /* Read in exec structure. */
+ eh = (Elf_Ehdr *) filep;
+
+ if (!IS_ELF(*eh))
+ return(-1);
+
+ sh = (Elf_Shdr *)&filep[eh->e_shoff];
+
+ shstr = (char *)&filep[sh[eh->e_shstrndx].sh_offset];
+
+ for (i = 0; i < eh->e_shnum; i++) {
+ if (strcmp (shstr + sh[i].sh_name, ".strtab") == 0) {
+ symstroff = sh[i].sh_offset;
+ symstrsize = sh[i].sh_size;
+ }
+ else if (strcmp (shstr + sh[i].sh_name, ".symtab") == 0) {
+ symoff = sh[i].sh_offset;
+ symsize = sh[i].sh_size;
+ }
+ else if (strcmp (shstr + sh[i].sh_name, ".text") == 0) {
+ kernvma = sh[i].sh_addr;
+ kernoffs = sh[i].sh_offset;
+ }
+ }
+ if (symsize == 0)
+ badfmt("stripped");
+
+ strtab = (char *)&filep[symstroff];
+
+ data.data = (u_char *)&nbuf;
+ data.size = sizeof(NLIST);
+
+ /* Read each symbol and enter it into the database. */
+ for (i = 0; symsize > 0; i++, symsize -= sizeof(Elf_Sym)) {
+
+ sbuf = (Elf_Sym *)&filep[symoff + i * sizeof(*sbuf)];
+ if (!sbuf->st_name)
+ continue;
+ elf_sym_to_nlist(&nbuf, sbuf, sh, eh->e_shnum);
+ key.data = (u_char *)(strtab + sbuf->st_name);
+ key.size = strlen((char *)key.data);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* also put in name prefixed with _ */
+ *buf = '_';
+ strcpy(buf + 1, strtab + sbuf->st_name);
+ key.data = (u_char *)buf;
+ key.size = strlen((char *)key.data);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Special processing for "_version" (depends on above) */
+ if (strcmp((char *)key.data, VRS_SYM) == 0) {
+ char *vp;
+
+ key.data = (u_char *)VRS_KEY;
+ key.size = sizeof(VRS_KEY) - 1;
+ /* Find the version string, relative to its section */
+ data.data = strdup(&filep[nbuf.n_value -
+ sh[sbuf->st_shndx].sh_addr +
+ sh[sbuf->st_shndx].sh_offset]);
+ /* assumes newline terminates version. */
+ if ((vp = strchr(data.data, '\n')) != NULL)
+ *vp = '\0';
+ data.size = strlen((char *)data.data);
+
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Restore to original values. */
+ data.data = (u_char *)&nbuf;
+ data.size = sizeof(NLIST);
+ }
+ }
+ munmap(filep, sst.st_size);
+ (void)close(fd);
+ return(0);
+}
+
+/*
+ * Convert an Elf_Sym into an nlist structure. This fills in only the
+ * n_value and n_type members.
+ */
+static void
+elf_sym_to_nlist(nl, s, shdr, shnum)
+ struct nlist *nl;
+ Elf_Sym *s;
+ Elf_Shdr *shdr;
+ int shnum;
+{
+ nl->n_value = s->st_value;
+
+ switch (s->st_shndx) {
+ case SHN_UNDEF:
+ case SHN_COMMON:
+ nl->n_type = N_UNDF;
+ break;
+ case SHN_ABS:
+ nl->n_type = ELF_ST_TYPE(s->st_info) == STT_FILE ?
+ N_FN : N_ABS;
+ break;
+ default:
+ if (s->st_shndx >= shnum)
+ nl->n_type = N_UNDF;
+ else {
+ Elf_Shdr *sh = shdr + s->st_shndx;
+
+ nl->n_type = sh->sh_type == SHT_PROGBITS ?
+ (sh->sh_flags & SHF_WRITE ? N_DATA : N_TEXT) :
+ (sh->sh_type == SHT_NOBITS ? N_BSS : N_UNDF);
+ }
+ break;
+ }
+
+ if (ELF_ST_BIND(s->st_info) == STB_GLOBAL ||
+ ELF_ST_BIND(s->st_info) == STB_WEAK)
+ nl->n_type |= N_EXT;
+}
+#endif /* DO_ELF */
+
+static struct knlist_handlers {
+ int (*fn) __P((char *name, DB *db));
+} nlist_fn[] = {
+#ifdef DO_ELF
+ { __elf_knlist },
+#endif
+#ifdef DO_AOUT
+ { __aout_knlist },
+#endif
+};
+
+void
+create_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ int n, i;
+
+ for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
+ n = (nlist_fn[i].fn)(name, db);
+ if (n != -1)
+ break;
+ }
+}
diff --git a/usr.sbin/kvm_mkdb/testdb.c b/usr.sbin/kvm_mkdb/testdb.c
new file mode 100644
index 0000000..8d769bd
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/testdb.c
@@ -0,0 +1,117 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)testdb.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <limits.h>
+#include <kvm.h>
+#include <db.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <paths.h>
+
+#include "extern.h"
+
+/* Return true if the db file is valid, else false */
+int
+testdb()
+{
+ register DB *db;
+ register int cc, kd, ret, dbversionlen;
+ register char *cp, *uf;
+ DBT rec;
+ struct nlist nitem;
+ char dbname[MAXPATHLEN], dbversion[_POSIX2_LINE_MAX];
+ char kversion[_POSIX2_LINE_MAX];
+
+ ret = 0;
+ db = NULL;
+
+ if ((kd = open(_PATH_KMEM, O_RDONLY, 0)) < 0)
+ goto close;
+
+ uf = (char *)getbootfile();
+ if ((cp = rindex(uf, '/')) != 0)
+ uf = cp + 1;
+ (void) snprintf(dbname, sizeof(dbname), "%skvm_%s.db", _PATH_VARDB, uf);
+ if ((db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
+ goto close;
+
+ /* Read the version out of the database */
+ rec.data = VRS_KEY;
+ rec.size = sizeof(VRS_KEY) - 1;
+ if ((db->get)(db, &rec, &rec, 0))
+ goto close;
+ if (rec.data == 0 || rec.size > sizeof(dbversion))
+ goto close;
+ bcopy(rec.data, dbversion, rec.size);
+ dbversionlen = rec.size;
+
+ /* Read version string from kernel memory */
+ rec.data = VRS_SYM;
+ rec.size = sizeof(VRS_SYM) - 1;
+ if ((db->get)(db, &rec, &rec, 0))
+ goto close;
+ if (rec.data == 0 || rec.size != sizeof(struct nlist))
+ goto close;
+ bcopy(rec.data, &nitem, sizeof(nitem));
+ /*
+ * Theoretically possible for lseek to be seeking to -1. Not
+ * that it's something to lie awake nights about, however.
+ */
+ errno = 0;
+ if (lseek(kd, (off_t)nitem.n_value, SEEK_SET) == -1 && errno != 0)
+ goto close;
+ cc = read(kd, kversion, sizeof(kversion));
+ if (cc < 0 || cc != sizeof(kversion))
+ goto close;
+
+ /* If they match, we win */
+ ret = bcmp(dbversion, kversion, dbversionlen) == 0;
+
+close: if (kd >= 0)
+ (void)close(kd);
+ if (db)
+ (void)(db->close)(db);
+ return (ret);
+}
diff --git a/usr.sbin/lpr/Makefile b/usr.sbin/lpr/Makefile
new file mode 100644
index 0000000..e4a44bd
--- /dev/null
+++ b/usr.sbin/lpr/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.3 1997/12/02 20:44:46 wollman Exp $
+
+SUBDIR= common_source chkprintcap lp lpc lpd lpq lpr lprm lptest pac \
+ filters filters.ru SMM.doc
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/lpr/Makefile.inc b/usr.sbin/lpr/Makefile.inc
new file mode 100644
index 0000000..bb67834
--- /dev/null
+++ b/usr.sbin/lpr/Makefile.inc
@@ -0,0 +1,11 @@
+# $Id: Makefile.inc,v 1.3 1998/03/07 09:47:52 bde Exp $
+
+CWARNFLAGS= -Wall -Wnested-externs -Wmissing-prototypes -Wno-unused -Wredundant-decls -Wstrict-prototypes
+
+.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..a59265b
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/Makefile
@@ -0,0 +1,12 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/8/93
+# $Id: Makefile,v 1.2 1997/12/02 20:45:05 wollman Exp $
+
+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..25d4775
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.5 1998/06/11 03:51:34 jb Exp $
+
+PROG= chkprintcap
+MAN8= chkprintcap.8
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+.PATH: ${.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..6c689b9
--- /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.
+.\"
+.\" $Id: chkprintcap.8,v 1.1 1997/12/02 20:45:09 wollman Exp $
+.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 chkprintcap
+.Op Fl d
+.Op Fl f Ar printcap
+.Sh DESCRIPTION
+.Nm Chkprintcap
+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
+.Po
+.Sq Li sd=
+capability
+.Pc .
+.El
+.Pp
+.Nm Chkprintcap
+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 chkprintcap
+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 chkprintcap
+command 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..9b4e8c9
--- /dev/null
+++ b/usr.sbin/lpr/chkprintcap/chkprintcap.c
@@ -0,0 +1,277 @@
+/*
+ * 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";
+static const char rcsid[] =
+ "$Id: chkprintcap.c,v 1.1 1997/12/02 20:45:11 wollman Exp $";
+
+#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"
+
+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)
+{
+ int c, error, makedirs, more;
+ struct printer myprinter, *pp;
+
+ makedirs = 0;
+ pp = &myprinter;
+
+ while ((c = getopt(argc, argv, "df:")) != -1) {
+ switch (c) {
+ case 'd':
+ makedirs = 1;
+ break;
+
+ case 'f':
+ setprintcap(optarg);
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (optind != argc)
+ usage();
+
+ more = firstprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+
+ while (more) {
+ struct stat stab;
+
+ 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 validity checks here... */
+
+next:
+ more = nextprinter(pp, &error);
+ if (interpret_error(pp, error) && more)
+ goto next;
+ }
+ check_spool_dirs();
+ 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;
+ dp2 = dirlist.lh_first;
+ while (dp2 && lessp(dp, dp2)) {
+ last = dp2;
+ dp2 = dp2->link.le_next;
+ }
+
+ 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 = dirlist.lh_first; dp; dp = dp2) {
+ dp2 = dp->link.le_next;
+
+ 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 [-d] [-f printcapfile]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/common_source/Makefile b/usr.sbin/lpr/common_source/Makefile
new file mode 100644
index 0000000..a9d7bfc
--- /dev/null
+++ b/usr.sbin/lpr/common_source/Makefile
@@ -0,0 +1,18 @@
+# $Id: Makefile,v 1.3 1998/06/11 03:53:23 jb Exp $
+
+#
+# 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
+SRCS= common.c displayq.c net.c printcap.c request.c rmjob.c \
+ startdaemon.c
+NOMAN= noman
+NOPROFILE= noprofile
+NOPIC= nopic
+CFLAGS+= ${CWARNFLAGS}
+
+install:
+
+.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..be95dc7
--- /dev/null
+++ b/usr.sbin/lpr/common_source/common.c
@@ -0,0 +1,292 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)common.c 8.5 (Berkeley) 4/28/95";
+*/
+static const char rcsid[] =
+ "$Id: common.c,v 1.10 1997/12/02 20:45:18 wollman Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <dirent.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];
+char *name; /* program name */
+
+extern uid_t uid, euid;
+
+static int compar __P((const void *, const void *));
+
+/*
+ * 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(cfp)
+ FILE *cfp;
+{
+ register int linel = 0;
+ register char *lp = line;
+ register int c;
+
+ while ((c = getc(cfp)) != '\n' && linel+1 < sizeof(line)) {
+ if (c == EOF)
+ return(0);
+ if (c == '\t') {
+ do {
+ *lp++ = ' ';
+ linel++;
+ } while ((linel & 07) != 0 && 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(pp, namelist)
+ const struct printer *pp;
+ struct queue *(*namelist[]);
+{
+ register struct dirent *d;
+ register struct queue *q, **queue;
+ register int nitems;
+ struct stat stbuf;
+ DIR *dirp;
+ int arraysz;
+
+ seteuid(euid);
+ if ((dirp = opendir(pp->spool_dir)) == NULL)
+ 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 queue **)malloc(arraysz * sizeof(struct queue *));
+ 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);
+ if (stat(d->d_name, &stbuf) < 0)
+ continue; /* Doesn't exist */
+ seteuid(uid);
+ q = (struct queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1);
+ if (q == NULL)
+ goto errdone;
+ q->q_time = stbuf.st_mtime;
+ strcpy(q->q_name, d->d_name);
+ /*
+ * Check to make sure the array has space left and
+ * realloc the maximum size.
+ */
+ if (++nitems > arraysz) {
+ arraysz *= 2;
+ queue = (struct queue **)realloc((char *)queue,
+ arraysz * sizeof(struct queue *));
+ if (queue == NULL)
+ goto errdone;
+ }
+ queue[nitems-1] = q;
+ }
+ closedir(dirp);
+ if (nitems)
+ qsort(queue, nitems, sizeof(struct queue *), compar);
+ *namelist = queue;
+ return(nitems);
+
+errdone:
+ closedir(dirp);
+ return(-1);
+}
+
+/*
+ * Compare modification times.
+ */
+static int
+compar(p1, p2)
+ const void *p1, *p2;
+{
+ if ((*(struct queue **)p1)->q_time < (*(struct queue **)p2)->q_time)
+ return(-1);
+ if ((*(struct queue **)p1)->q_time > (*(struct queue **)p2)->q_time)
+ return(1);
+ return(0);
+}
+
+/* sleep n milliseconds */
+void
+delay(n)
+ int n;
+{
+ struct timeval tdelay;
+
+ if (n <= 0 || n > 10000)
+ fatal((struct printer *)0, /* fatal() knows how to deal */
+ "unreasonable delay period (%d)", n);
+ tdelay.tv_sec = n / 1000;
+ tdelay.tv_usec = n * 1000 % 1000000;
+ (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay);
+}
+
+char *
+lock_file_name(pp, buf, len)
+ 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] == '/') {
+ buf[0] = '\0';
+ strncpy(buf, pp->lock_file, len);
+ } else {
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->lock_file);
+ }
+ return buf;
+}
+
+char *
+status_file_name(pp, buf, len)
+ 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] == '/') {
+ buf[0] = '\0';
+ strncpy(buf, pp->status_file, len);
+ } else {
+ snprintf(buf, len, "%s/%s", pp->spool_dir, pp->status_file);
+ }
+ return buf;
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#ifdef __STDC__
+fatal(const struct printer *pp, const char *msg, ...)
+#else
+fatal(pp, msg, va_alist)
+ const struct printer *pp;
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ if (from != host)
+ (void)printf("%s: ", host);
+ (void)printf("%s: ", name);
+ 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(start)
+ int start;
+{
+ int stop = getdtablesize();
+ for (; start < stop; start++)
+ close(start);
+}
+
diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c
new file mode 100644
index 0000000..37ee980
--- /dev/null
+++ b/usr.sbin/lpr/common_source/displayq.c
@@ -0,0 +1,487 @@
+/*
+ * 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 char sccsid[] = "@(#)displayq.c 8.4 (Berkeley) 4/28/95";
+*/
+static const char rcsid[] =
+ "$Id: displayq.c,v 1.13 1997/12/02 20:45:19 wollman Exp $";
+#endif /* not lint */
+
+#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[40]; /* current file being printed */
+static char file[132]; /* 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 char *head0 = "Rank Owner Job Files";
+static char *head1 = "Total Size\n";
+
+static void alarmhandler __P((int));
+static void warn __P((const struct printer *pp));
+
+/*
+ * Display the current state of the queue. Format = 1 if long format.
+ */
+void
+displayq(pp, format)
+ struct printer *pp;
+ int format;
+{
+ register struct queue *q;
+ register int i, nitems, fd, ret;
+ register char *cp;
+ struct queue **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: ", 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: ", 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;
+ while ((i = getc(fp)) != EOF && i != '\n')
+ *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;
+ while ((i = getc(fp)) != EOF && i != '\n')
+ *cp++ = i;
+ *cp = '\0';
+ /*
+ * Print the status file.
+ */
+ if (pp->remote)
+ printf("%s: ", 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->q_name);
+ 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)
+ printf("%s: ", 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(pp)
+ const struct printer *pp;
+{
+ if (pp->remote)
+ printf("%s: ", host);
+ puts("Warning: no daemon present");
+ current[0] = '\0';
+}
+
+/*
+ * Print the header for the short listing format
+ */
+void
+header()
+{
+ printf(head0);
+ col = strlen(head0)+1;
+ blankfill(SIZCOL);
+ printf(head1);
+}
+
+void
+inform(pp, cf)
+ const struct printer *pp;
+ char *cf;
+{
+ register int j;
+ 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++;
+ j = 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')
+ continue;
+ if (j == 0 || strcmp(file, line+1) != 0) {
+ (void) strncpy(file, line+1, sizeof(file) - 1);
+ file[sizeof(file) - 1] = '\0';
+ }
+ j++;
+ continue;
+ case 'N':
+ show(line+1, file, j);
+ file[0] = '\0';
+ j = 0;
+ }
+ }
+ fclose(cfp);
+ if (!lflag) {
+ blankfill(SIZCOL);
+ printf("%ld bytes\n", totsize);
+ totsize = 0;
+ }
+}
+
+int
+inlist(name, file)
+ char *name, *file;
+{
+ 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, name))
+ return(1);
+ /*
+ * 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 && !strcmp(cp, from))
+ return(1);
+ return(0);
+}
+
+void
+show(nfile, file, copies)
+ register char *nfile, *file;
+ int copies;
+{
+ if (strcmp(nfile, " ") == 0)
+ nfile = "(standard input)";
+ if (lflag)
+ ldump(nfile, file, copies);
+ else
+ dump(nfile, file, copies);
+}
+
+/*
+ * Fill the line with blanks to the specified column
+ */
+void
+blankfill(n)
+ register int n;
+{
+ while (col++ < n)
+ putchar(' ');
+}
+
+/*
+ * Give the abbreviated dump of the file names
+ */
+void
+dump(nfile, file, copies)
+ char *nfile, *file;
+ int copies;
+{
+ register short n, fill;
+ struct stat lbuf;
+
+ /*
+ * Print as many files as will fit
+ * (leaving room for the total size)
+ */
+ fill = first ? 0 : 2; /* fill space for ``, '' */
+ if (((n = strlen(nfile)) + col + fill) >= SIZCOL-4) {
+ if (col < SIZCOL) {
+ printf(" ..."), col += 4;
+ blankfill(SIZCOL);
+ }
+ } else {
+ if (first)
+ first = 0;
+ else
+ printf(", ");
+ printf("%s", nfile);
+ col += n+fill;
+ }
+ seteuid(euid);
+ if (*file && !stat(file, &lbuf))
+ totsize += copies * lbuf.st_size;
+ seteuid(uid);
+}
+
+/*
+ * Print the long info about the file
+ */
+void
+ldump(nfile, file, copies)
+ char *nfile, *file;
+ int copies;
+{
+ struct stat lbuf;
+
+ putchar('\t');
+ if (copies > 1)
+ printf("%-2d copies of %-19s", copies, nfile);
+ else
+ printf("%-32s", nfile);
+ if (*file && !stat(file, &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(n)
+ int n;
+{
+ char rline[100];
+ static 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(signo)
+ int signo;
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h
new file mode 100644
index 0000000..8b3cd5f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.h
@@ -0,0 +1,233 @@
+/*
+ * 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
+ * $Id$
+ */
+
+#include <sys/queue.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 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 */
+ 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 *status_file; /* ST: status file name */
+ char *trailer; /* TR: trailer string send when Q empties */
+ char *mode_set; /* MS: mode set, a la stty */
+};
+
+/*
+ * 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 char *name; /* program name */
+ /* host machine name */
+extern char host[MAXHOSTNAMELEN];
+extern char *from; /* client's machine name */
+
+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 */
+
+/*
+ * Structure used for building a sorted list of control files.
+ */
+struct queue {
+ time_t q_time; /* modification time */
+ char q_name[MAXNAMLEN+1]; /* control file name */
+};
+
+/*
+ * 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)
+
+/*
+ * 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 <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct dirent;
+
+void blankfill __P((int));
+char *checkremote __P((struct printer *pp));
+int chk __P((char *));
+void closeallfds __P((int start));
+void delay __P((int));
+void displayq __P((struct printer *pp, int format));
+void dump __P((char *, char *, int));
+void fatal __P((const struct printer *pp, const char *fmp, ...));
+int firstprinter __P((struct printer *pp, int *status));
+void free_printer __P((struct printer *pp));
+void free_request __P((struct request *rp));
+int getline __P((FILE *));
+int getport __P((const struct printer *pp, const char *, int));
+int getprintcap __P((const char *printer, struct printer *pp));
+int getq __P((const struct printer *, struct queue *(*[])));
+void header __P((void));
+void inform __P((const struct printer *pp, char *cf));
+void init_printer __P((struct printer *pp));
+void init_request __P((struct request *rp));
+int inlist __P((char *, char *));
+int iscf __P((struct dirent *));
+int isowner __P((char *, char *));
+void ldump __P((char *, char *, int));
+void lastprinter __P((void));
+int lockchk __P((struct printer *pp, char *));
+char *lock_file_name __P((const struct printer *pp, char *buf, size_t len));
+int nextprinter __P((struct printer *pp, int *status));
+const
+char *pcaperr __P((int error));
+void prank __P((int));
+void process __P((const struct printer *pp, char *));
+void rmjob __P((const char *printer));
+void rmremote __P((const struct printer *pp));
+void setprintcap __P((char *newprintcap));
+void show __P((char *, char *, int));
+int startdaemon __P((const struct printer *pp));
+char *status_file_name __P((const struct printer *pp, char *buf,
+ size_t len));
+ssize_t writel __P((int s, ...));
+__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..bc60549
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.local.h
@@ -0,0 +1,81 @@
+/*
+ * 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
+ */
+
+/*
+ * 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 1000
+#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/net.c b/usr.sbin/lpr/common_source/net.c
new file mode 100644
index 0000000..d5886f9
--- /dev/null
+++ b/usr.sbin/lpr/common_source/net.c
@@ -0,0 +1,260 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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"
+
+ /* host machine name */
+char host[MAXHOSTNAMELEN];
+char *from = host; /* client's machine name */
+
+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 hostent *hp;
+ struct servent *sp;
+ struct sockaddr_in sin;
+ int s, timo = 1, lport = IPPORT_RESERVED - 1;
+ int err;
+
+ /*
+ * Get the host address and port number to connect to.
+ */
+ if (rhost == NULL)
+ fatal(pp, "no remote host to connect to");
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_len = sizeof sin;
+ sin.sin_family = AF_INET;
+ if (inet_aton(rhost, &sin.sin_addr) == 0) {
+ hp = gethostbyname2(rhost, AF_INET);
+ if (hp == NULL)
+ fatal(pp, "cannot resolve %s: %s", rhost,
+ hstrerror(h_errno));
+ /* XXX - should deal with more addresses */
+ sin.sin_addr = *(struct in_addr *)hp->h_addr_list[0];
+ }
+ if (rport == 0) {
+ sp = getservbyname("printer", "tcp");
+ if (sp == NULL)
+ fatal(pp, "printer/tcp: unknown service");
+ sin.sin_port = sp->s_port;
+ } else
+ sin.sin_port = htons(rport);
+
+ /*
+ * Try connecting to the server.
+ */
+retry:
+ seteuid(euid);
+ s = rresvport(&lport);
+ seteuid(uid);
+ if (s < 0)
+ return(-1);
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 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 && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ goto retry;
+ }
+ return(-1);
+ }
+ 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 name[MAXHOSTNAMELEN];
+ register struct hostent *hp;
+ char *err;
+ struct in_addr *localaddrs;
+ int i, j, nlocaladdrs, ncommonaddrs;
+
+ pp->remote = 0; /* assume printer is local */
+ if (pp->remote_host != NULL) {
+ /* get the addresses of the local host */
+ gethostname(name, sizeof(name));
+ name[sizeof(name) - 1] = '\0';
+ hp = gethostbyname2(name, AF_INET);
+ if (hp == (struct hostent *) NULL) {
+ asprintf(&err, "unable to get official name "
+ "for local machine %s: %s",
+ name, hstrerror(h_errno));
+ return err;
+ }
+ for (i = 0; hp->h_addr_list[i]; i++)
+ ;
+ nlocaladdrs = i;
+ localaddrs = malloc(i * sizeof(struct in_addr));
+ if (localaddrs == 0) {
+ asprintf(&err, "malloc %lu bytes failed",
+ (u_long)i * sizeof(struct in_addr));
+ return err;
+ }
+ for (i = 0; hp->h_addr_list[i]; i++)
+ localaddrs[i] = *(struct in_addr *)hp->h_addr_list[i];
+
+ /* get the official name of RM */
+ hp = gethostbyname2(pp->remote_host, AF_INET);
+ if (hp == (struct hostent *) NULL) {
+ asprintf(&err, "unable to get address list for "
+ "remote machine %s: %s",
+ pp->remote_host, hstrerror(h_errno));
+ free(localaddrs);
+ return err;
+ }
+
+ ncommonaddrs = 0;
+ for (i = 0; i < nlocaladdrs; i++) {
+ for (j = 0; hp->h_addr_list[j]; j++) {
+ char *them = hp->h_addr_list[j];
+ if (localaddrs[i].s_addr ==
+ (*(struct in_addr *)them).s_addr)
+ 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;
+ free(localaddrs);
+ }
+ 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 s, ...)
+{
+ 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, s);
+ 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, s);
+ 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(s, 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..a061c0e
--- /dev/null
+++ b/usr.sbin/lpr/common_source/printcap.c
@@ -0,0 +1,428 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: printcap.c,v 1.3 1997/12/02 20:45:25 wollman Exp $";
+#endif /* not lint */
+
+#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 *);
+static int capdb_getaltlog(char *, const char *, const char *);
+static int capdb_getaltnum(char *, const char *, const char *, long, long *);
+static int capdb_getaltstr(char *, const char *, const char *, const char *,
+ char **);
+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(bp, pp)
+ char *bp;
+ struct printer *pp;
+{
+ enum lpd_filters filt;
+
+ 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, "st", "spool.status", DEFSTAT,
+ &pp->status_file));
+ CHK(capdb_getaltstr(bp, "tr", "job.trailer", 0, &pp->trailer));
+
+ 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");
+
+ /*
+ * 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->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/recvjob.c b/usr.sbin/lpr/common_source/recvjob.c
new file mode 100644
index 0000000..6b1c8b0
--- /dev/null
+++ b/usr.sbin/lpr/common_source/recvjob.c
@@ -0,0 +1,375 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] =
+ "$Id: recvjob.c,v 1.11 1997/10/06 03:58:48 imp Exp $";
+#endif /* not lint */
+
+/*
+ * 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 <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(1, sp, 1);
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize __P((int));
+static void frecverr __P((const char *, ...));
+static int noresponse __P((void));
+static void rcleanup __P((int));
+static int read_number __P((char *));
+static int readfile __P((char *, int));
+static int readjob __P((struct printer *pp));
+
+
+void
+recvjob(printer)
+ const char *printer;
+{
+ struct stat stb;
+ int status;
+ struct printer myprinter, *pp = &myprinter;
+
+ /*
+ * Perform lookup for printer name or abbreviation
+ */
+ 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(2); /* 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: %s: %m", pp->printer, pp->spool_dir);
+ 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: %s: %m", pp->printer, pp->spool_dir);
+ 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(pp)
+ struct printer *pp;
+{
+ register int size, nfiles;
+ register char *cp;
+
+ ack();
+ nfiles = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ do {
+ if ((size = read(1, cp, 1)) != 1) {
+ if (size < 0)
+ frecverr("%s: lost connection",
+ pp->printer);
+ return(nfiles);
+ }
+ } while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
+ if (cp - line + 1 >= sizeof(line))
+ frecverr("readjob overflow");
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 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
+ */
+ strncpy(cp + 6, from, sizeof(line) + line - cp - 7);
+ line[sizeof(line) - 1 ] = '\0';
+ strncpy(tfname, cp, sizeof(tfname) - 1);
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (strchr(tfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ tfname);
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ if (!readfile(tfname, size)) {
+ rcleanup(0);
+ continue;
+ }
+ if (link(tfname, cp) < 0)
+ frecverr("%s: %m", tfname);
+ (void) unlink(tfname);
+ tfname[0] = '\0';
+ nfiles++;
+ continue;
+
+ case '\3': /* read df file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ (void) strncpy(dfname, cp, sizeof(dfname) - 1);
+ dfname[sizeof(dfname) - 1] = '\0';
+ if (strchr(dfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ dfname);
+ (void) readfile(dfname, size);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(file, size)
+ char *file;
+ int size;
+{
+ register char *cp;
+ char buf[BUFSIZ];
+ register int i, j, amt;
+ int fd, err;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0)
+ frecverr("readfile: %s: illegal path name: %m", file);
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(1, cp, amt);
+ if (j <= 0)
+ frecverr("lost connection");
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err)
+ frecverr("%s: write error", file);
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return(0);
+ }
+ ack();
+ return(1);
+}
+
+static int
+noresponse()
+{
+ char resp;
+
+ if (read(1, &resp, 1) != 1)
+ frecverr("lost connection");
+ 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(size)
+ 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(fn)
+ 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(signo)
+ int signo;
+{
+ 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';
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+frecverr(const char *msg, ...)
+#else
+frecverr(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ rcleanup(0);
+ syslog(LOG_ERR, "%s", fromb);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ putchar('\1'); /* return error code */
+ exit(1);
+}
diff --git a/usr.sbin/lpr/common_source/request.c b/usr.sbin/lpr/common_source/request.c
new file mode 100644
index 0000000..d3e3cdf
--- /dev/null
+++ b/usr.sbin/lpr/common_source/request.c
@@ -0,0 +1,80 @@
+/*
+ * 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";
+static const char rcsid[] =
+ "$Id$";
+
+#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 = rp->users.tqh_first) != 0) {
+ TAILQ_REMOVE(&rp->users, ru, ru_link);
+ free(ru);
+ }
+ while ((rj = rp->jobids.tqh_first) != 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..e2406b9
--- /dev/null
+++ b/usr.sbin/lpr/common_source/rmjob.c
@@ -0,0 +1,399 @@
+/*
+ * 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[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id: rmjob.c,v 1.10 1997/10/14 16:00:37 joerg Exp $";
+#endif /* not lint */
+
+#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[40]; /* active control file name */
+
+extern uid_t uid, euid; /* real and effective user id's */
+
+static void alarmhandler __P((int));
+static void do_unlink __P((char *));
+
+void
+rmjob(printer)
+ 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)
+ fatal(pp, "The login name \"-all\" is reserved");
+ all = 1; /* all those from 'from' */
+ 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(pp, s)
+ struct printer *pp;
+ char *s;
+{
+ register FILE *fp;
+ register int i, n;
+
+ seteuid(euid);
+ if ((fp = fopen(s, "r")) == NULL) {
+ if (errno == EACCES)
+ fatal(pp, "%s: %s", s, 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(pp, file)
+ 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;
+ if (from != host)
+ printf("%s: ", host);
+ do_unlink(line+1);
+ }
+ }
+ (void) fclose(cfp);
+ do_unlink(file);
+}
+
+static void
+do_unlink(file)
+ char *file;
+{
+ int ret;
+
+ if (from != host)
+ printf("%s: ", 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(file)
+ 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 || !strcmp(from, 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(owner, file)
+ char *owner, *file;
+{
+ if (!strcmp(person, root) && (from == host || !strcmp(from, file+6)))
+ return(1);
+ if (!strcmp(person, owner) && !strcmp(from, file+6))
+ return(1);
+ if (from != host)
+ printf("%s: ", 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(pp)
+ const struct printer *pp;
+{
+ int i, rem, niov, 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).
+ */
+ niov = 4 + 2 * users + requests + 1;
+ iov = malloc(niov * sizeof *iov);
+ if (iov == 0)
+ fatal(pp, "out of memory");
+ iov[0].iov_base = "\5";
+ iov[1].iov_base = pp->remote_queue;
+ iov[2].iov_base = " ";
+ iov[3].iov_base = all ? "-all" : person;
+ for (i = 0; i < users; i++) {
+ iov[4 + 2 * i].iov_base = " ";
+ iov[4 + 2 * i + 1].iov_base = user[i];
+ }
+ for (i = 0; i < requests; i++) {
+ asprintf(&iov[4 + 2 * users + i].iov_base, " %d", requ[i]);
+ if (iov[4 + 2 * users + i].iov_base == 0)
+ fatal(pp, "out of memory");
+ }
+ iov[4 + 2 * users + requests].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)
+ printf("%s: ", 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[4 + 2 * users + i].iov_base);
+ free(iov);
+}
+
+/*
+ * Return 1 if the filename begins with 'cf'
+ */
+int
+iscf(d)
+ struct dirent *d;
+{
+ return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
+}
+
+void
+alarmhandler(signo)
+ int signo;
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c
new file mode 100644
index 0000000..931a7ef
--- /dev/null
+++ b/usr.sbin/lpr/common_source/startdaemon.c
@@ -0,0 +1,110 @@
+/*
+ * 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
+#if 0
+static char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94";
+#endif
+static const char rcsid[] =
+ "$Id: startdaemon.c,v 1.6 1997/09/24 06:47:32 charnier Exp $";
+#endif /* not lint */
+
+
+#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(pp)
+ const struct printer *pp;
+{
+ struct sockaddr_un un;
+ register int s, n;
+ 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);
+ if (connect(s, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
+ seteuid(uid);
+ warn("connect");
+ (void) close(s);
+ return(0);
+ }
+ seteuid(uid);
+
+ /*
+ * 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..0ddef1d
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/Makefile
@@ -0,0 +1,12 @@
+PROG= koi2alt
+NOMAN= noman
+BINDIR= /usr/libexec/lpr/ru
+
+SAMPLES=bjc-240.sh.sample
+
+afterinstall:
+ cd ${.CURDIR} && \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${SHAREMODE} \
+ ${SAMPLES} ${DESTDIR}${BINDIR}
+
+.include <bsd.prog.mk>
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/koi2alt.c b/usr.sbin/lpr/filters.ru/koi2alt.c
new file mode 100644
index 0000000..928e97e
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/*
+ * KOI8-R -> CP866 conversion filter (Russian character sets)
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.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.ru/koi2alt/koi2alt.c b/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c
new file mode 100644
index 0000000..928e97e
--- /dev/null
+++ b/usr.sbin/lpr/filters.ru/koi2alt/koi2alt.c
@@ -0,0 +1,101 @@
+/*
+ * 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.
+ */
+
+/*
+ * KOI8-R -> CP866 conversion filter (Russian character sets)
+ */
+
+#include <sys/types.h>
+#include <signal.h>
+#include <stdio.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..c9d5c4f
--- /dev/null
+++ b/usr.sbin/lpr/filters/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpf
+NOMAN= noman
+BINDIR= /usr/libexec/lpr
+
+.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..59b3cbf
--- /dev/null
+++ b/usr.sbin/lpr/filters/lpf.c
@@ -0,0 +1,219 @@
+/*
+ * 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 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[] = "From: @(#)lpf.c 8.1 (Berkeley) 6/6/93";
+static char id[] = "$Id$";
+#endif /* not lint */
+
+/*
+ * 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(argc, argv)
+ 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..cf0304e
--- /dev/null
+++ b/usr.sbin/lpr/lp/Makefile
@@ -0,0 +1,10 @@
+# $Id$
+
+BINDIR = /usr/bin
+MAN1 = lp.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/lp.sh ${DESTDIR}${BINDIR}/lp
+
+.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..6bbd342
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.1
@@ -0,0 +1,107 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: lp.1,v 1.6 1997/09/24 06:47:40 charnier Exp $
+.\"
+.Dd January 22, 1995
+.Dt LP 1
+.Os
+.Sh NAME
+.Nm lp
+.Nd front-end to the print spooler
+.Sh SYNOPSIS
+.Nm lp
+.Op Fl c
+.Op Fl d Ar printer
+.Op Fl n Ar num
+.Op Ar name ...
+.Sh DESCRIPTION
+.Nm Lp
+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.
+
+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
+.Pq 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.
+.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
+.if t J\(:org Wunsch.
+.if n Joerg 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..0a9c8e9
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.sh
@@ -0,0 +1,71 @@
+#!/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.
+#
+# $Id$
+#
+
+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.
+#
+while getopts "cd:n:o:" option
+do
+ case $option in
+
+ c) # copy files before printing
+ symlink="";;
+ d) # destination
+ dest="${OPTARG}";;
+ n) # number of copies
+ ncopies="-#${OPTARG}";;
+ o) # (printer 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..46a4166
--- /dev/null
+++ b/usr.sbin/lpr/lpc/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.4 1997/12/16 17:53:19 bde Exp $
+
+PROG= lpc
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+MAN8= lpc.8
+SRCS= lpc.c cmds.c cmdtab.c
+BINGRP= daemon
+BINMODE=2555
+.PATH: ${.CURDIR}/../common_source
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+.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..4aef15d
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmds.c
@@ -0,0 +1,890 @@
+/*
+ * 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 */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95";
+*/
+static const char rcsid[] =
+ "$Id: cmds.c,v 1.11 1997/12/02 20:45:37 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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"
+
+static void abortpr __P((struct printer *, int));
+static int doarg __P((char *));
+static int doselect __P((struct dirent *));
+static void putmsg __P((struct printer *, int, char **));
+static int sortq __P((const void *, const void *));
+static void startpr __P((struct printer *, int));
+static int touch __P((struct queue *));
+static void unlinkf __P((char *));
+static void upstat __P((struct printer *, char *));
+
+/*
+ * generic framework for commands which operate on all or a specified
+ * set of printers
+ */
+void
+generic(doit, argc, argv)
+ void (*doit) __P((struct printer *));
+ int argc;
+ char *argv[];
+{
+ int status, more;
+ struct printer myprinter, *pp = &myprinter;
+
+ if (argc == 1) {
+ printf("Usage: %s {all | printer ...}\n", argv[0]);
+ return;
+ }
+ if (argc == 2 && strcmp(argv[1], "all") == 0) {
+ more = firstprinter(pp, &status);
+ if (status)
+ goto looperr;
+ while (more) {
+ (*doit)(pp);
+ 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, pcaperr(status));
+ }
+ } while (more && status);
+ }
+ return;
+ }
+ while (--argc) {
+ ++argv;
+ init_printer(pp);
+ status = getprintcap(*argv, pp);
+ switch(status) {
+ default:
+ fatal(pp, pcaperr(status));
+ 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;
+ }
+ (*doit)(pp);
+ }
+}
+
+/*
+ * kill an existing daemon and disable printing.
+ */
+void
+doabort(pp)
+ struct printer *pp;
+{
+ abortpr(pp, 1);
+}
+
+static void
+abortpr(pp, dis)
+ struct printer *pp;
+ int dis;
+{
+ register FILE *fp;
+ struct stat stbuf;
+ int pid, fd;
+ 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.
+ */
+ if (dis) {
+ seteuid(euid);
+ if (stat(lf, &stbuf) >= 0) {
+ if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0)
+ printf("\tcannot disable printing: %s\n",
+ strerror(errno));
+ else {
+ upstat(pp, "printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else if (errno == ENOENT) {
+ if ((fd = open(lf, O_WRONLY|O_CREAT,
+ LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0)
+ printf("\tcannot create lock file: %s\n",
+ strerror(errno));
+ else {
+ (void) close(fd);
+ upstat(pp, "printing disabled\n");
+ printf("\tprinting disabled\n");
+ printf("\tno daemon to abort\n");
+ }
+ goto out;
+ } else {
+ printf("\tcannot stat lock file\n");
+ goto out;
+ }
+ }
+ /*
+ * Kill the current daemon to stop printing now.
+ */
+ if ((fp = fopen(lf, "r")) == NULL) {
+ printf("\tcannot open lock file\n");
+ goto out;
+ }
+ if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) {
+ (void) fclose(fp); /* unlocks as well */
+ printf("\tno daemon to abort\n");
+ goto out;
+ }
+ (void) fclose(fp);
+ if (kill(pid = atoi(line), SIGTERM) < 0) {
+ if (errno == ESRCH)
+ printf("\tno daemon to abort\n");
+ else
+ printf("\tWarning: daemon (pid %d) not killed\n", pid);
+ } else
+ printf("\tdaemon (pid %d) killed\n", pid);
+out:
+ seteuid(uid);
+}
+
+/*
+ * Write a message into the status file.
+ */
+static void
+upstat(pp, msg)
+ struct printer *pp;
+ char *msg;
+{
+ register int fd;
+ char statfile[MAXPATHLEN];
+
+ status_file_name(pp, statfile, sizeof statfile);
+ umask(0);
+ fd = open(statfile, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ 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);
+}
+
+static int
+doselect(d)
+ struct dirent *d;
+{
+ int c = d->d_name[0];
+
+ if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
+ return(1);
+ return(0);
+}
+
+/*
+ * Comparison routine for scandir. Sort by job number and machine, then
+ * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z.
+ */
+static int
+sortq(a, b)
+ const void *a, *b;
+{
+ struct dirent **d1, **d2;
+ int c1, c2;
+
+ d1 = (struct dirent **)a;
+ d2 = (struct dirent **)b;
+ if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)))
+ return(c1);
+ c1 = (*d1)->d_name[0];
+ c2 = (*d2)->d_name[0];
+ if (c1 == c2)
+ return((*d1)->d_name[2] - (*d2)->d_name[2]);
+ if (c1 == 'c')
+ return(-1);
+ if (c1 == 'd' || c2 == 'c')
+ return(1);
+ return(-1);
+}
+
+/*
+ * Remove all spool files and temporaries from the spooling area.
+ * Or, perhaps:
+ * Remove incomplete jobs from spooling area.
+ */
+void
+clean(pp)
+ struct printer *pp;
+{
+ register int i, n;
+ register char *cp, *cp1, *lp;
+ struct dirent **queue;
+ int nitems;
+
+ printf("%s:\n", pp->printer);
+
+ lp = line;
+ cp = pp->spool_dir;
+ while (lp < &line[sizeof(line) - 1]) {
+ if ((*lp++ = *cp++) == 0)
+ break;
+ }
+ lp[-1] = '/';
+
+ seteuid(euid);
+ nitems = scandir(pp->spool_dir, &queue, doselect, sortq);
+ seteuid(uid);
+ if (nitems < 0) {
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ if (nitems == 0)
+ return;
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ if (*cp == 'c') {
+ 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) {
+ strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ unlinkf(line);
+ }
+ } else {
+ /*
+ * Must be a df with no cf (otherwise, it would have
+ * been skipped above) or a tf file (which can always
+ * be removed).
+ */
+ strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ unlinkf(line);
+ }
+ } while (++i < nitems);
+}
+
+static void
+unlinkf(name)
+ char *name;
+{
+ seteuid(euid);
+ if (unlink(name) < 0)
+ printf("\tcannot remove %s\n", name);
+ else
+ printf("\tremoved %s\n", name);
+ seteuid(uid);
+}
+
+/*
+ * Enable queuing to the printer (allow lpr's).
+ */
+void
+enable(pp)
+ struct printer *pp;
+{
+ struct stat stbuf;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ /*
+ * Turn off the group execute bit of the lock file to enable queuing.
+ */
+ seteuid(euid);
+ if (stat(lf, &stbuf) >= 0) {
+ if (chmod(lf, stbuf.st_mode & ~LFM_QUEUE_DIS) < 0)
+ printf("\tcannot enable queuing\n");
+ else
+ printf("\tqueuing enabled\n");
+ }
+ seteuid(uid);
+}
+
+/*
+ * Disable queuing.
+ */
+void
+disable(pp)
+ struct printer *pp;
+{
+ register int fd;
+ struct stat stbuf;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+ /*
+ * Turn on the group execute bit of the lock file to disable queuing.
+ */
+ seteuid(euid);
+ if (stat(lf, &stbuf) >= 0) {
+ if (chmod(lf, stbuf.st_mode | LFM_QUEUE_DIS) < 0)
+ printf("\tcannot disable queuing: %s\n",
+ strerror(errno));
+ else
+ printf("\tqueuing disabled\n");
+ } else if (errno == ENOENT) {
+ if ((fd = open(lf, O_WRONLY|O_CREAT,
+ LOCK_FILE_MODE | LFM_QUEUE_DIS)) < 0)
+ printf("\tcannot create lock file: %s\n",
+ strerror(errno));
+ else {
+ (void) close(fd);
+ printf("\tqueuing disabled\n");
+ }
+ } else
+ printf("\tcannot stat lock file\n");
+ seteuid(uid);
+}
+
+/*
+ * Disable queuing and printing and put a message into the status file
+ * (reason for being down).
+ */
+void
+down(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int status, more;
+ struct printer myprinter, *pp = &myprinter;
+
+ if (argc == 1) {
+ printf("Usage: down {all | printer} [message ...]\n");
+ return;
+ }
+ if (!strcmp(argv[1], "all")) {
+ more = firstprinter(pp, &status);
+ if (status)
+ goto looperr;
+ while (more) {
+ putmsg(pp, argc - 2, argv + 2);
+ 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, pcaperr(status));
+ }
+ } while (more && status);
+ }
+ return;
+ }
+ init_printer(pp);
+ status = getprintcap(argv[1], pp);
+ switch(status) {
+ default:
+ fatal(pp, pcaperr(status));
+ case PCAPERR_NOTFOUND:
+ printf("unknown printer %s\n", argv[1]);
+ return;
+ case PCAPERR_TCOPEN:
+ printf("warning: %s: unresolved tc= reference(s)", argv[1]);
+ break;
+ case PCAPERR_SUCCESS:
+ break;
+ }
+ putmsg(pp, argc - 2, argv + 2);
+}
+
+static void
+putmsg(pp, argc, argv)
+ struct printer *pp;
+ int argc;
+ char **argv;
+{
+ register int fd;
+ register char *cp1, *cp2;
+ char buf[1024];
+ char file[MAXPATHLEN];
+ struct stat stbuf;
+
+ printf("%s:\n", pp->printer);
+ /*
+ * Turn on the group execute bit of the lock file to disable queuing;
+ * turn on the owner execute bit of the lock file to disable printing.
+ */
+ lock_file_name(pp, file, sizeof file);
+ seteuid(euid);
+ if (stat(file, &stbuf) >= 0) {
+ if (chmod(file, stbuf.st_mode|LFM_PRINT_DIS|LFM_QUEUE_DIS) < 0)
+ printf("\tcannot disable queuing: %s\n",
+ strerror(errno));
+ else
+ printf("\tprinter and queuing disabled\n");
+ } else if (errno == ENOENT) {
+ if ((fd = open(file, O_WRONLY|O_CREAT,
+ LOCK_FILE_MODE|LFM_PRINT_DIS|LFM_QUEUE_DIS)) < 0)
+ printf("\tcannot create lock file: %s\n",
+ strerror(errno));
+ else {
+ (void) close(fd);
+ printf("\tprinter and queuing disabled\n");
+ }
+ seteuid(uid);
+ return;
+ } else
+ printf("\tcannot stat lock file\n");
+ /*
+ * Write the message into the status file.
+ */
+ status_file_name(pp, file, sizeof file);
+ fd = open(file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ if (fd < 0) {
+ printf("\tcannot create status file: %s\n", strerror(errno));
+ seteuid(uid);
+ return;
+ }
+ seteuid(uid);
+ (void) ftruncate(fd, 0);
+ if (argc <= 0) {
+ (void) write(fd, "\n", 1);
+ (void) close(fd);
+ return;
+ }
+ cp1 = buf;
+ while (--argc >= 0) {
+ cp2 = *argv++;
+ while ((cp1 - buf) < sizeof(buf) && (*cp1++ = *cp2++))
+ ;
+ cp1[-1] = ' ';
+ }
+ cp1[-1] = '\n';
+ *cp1 = '\0';
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd);
+}
+
+/*
+ * Exit lpc
+ */
+void
+quit(argc, argv)
+ int argc;
+ char *argv[];
+{
+ exit(0);
+}
+
+/*
+ * Kill and restart the daemon.
+ */
+void
+restart(pp)
+ struct printer *pp;
+{
+ abortpr(pp, 0);
+ startpr(pp, 0);
+}
+
+/*
+ * Enable printing on the specified printer and startup the daemon.
+ */
+void
+startcmd(pp)
+ struct printer *pp;
+{
+ startpr(pp, 1);
+}
+
+static void
+startpr(pp, enable)
+ struct printer *pp;
+ int enable;
+{
+ struct stat stbuf;
+ char lf[MAXPATHLEN];
+
+ lock_file_name(pp, lf, sizeof lf);
+ printf("%s:\n", pp->printer);
+
+ /*
+ * Turn off the owner execute bit of the lock file to enable printing.
+ */
+ seteuid(euid);
+ if (enable && stat(lf, &stbuf) >= 0) {
+ mode_t bits = (enable == 2 ? 0
+ : (LFM_PRINT_DIS | LFM_QUEUE_DIS));
+ if (chmod(lf, stbuf.st_mode & (LOCK_FILE_MODE | bits)) < 0)
+ printf("\tcannot enable printing\n");
+ else
+ printf("\tprinting enabled\n");
+ }
+ if (!startdaemon(pp))
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+ seteuid(uid);
+}
+
+/*
+ * Print the status of the printer queue.
+ */
+void
+status(pp)
+ 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(pp)
+ struct printer *pp;
+{
+ register int fd;
+ struct stat stbuf;
+ 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.
+ */
+ seteuid(euid);
+ if (stat(lf, &stbuf) >= 0) {
+ if (chmod(lf, stbuf.st_mode | LFM_PRINT_DIS) < 0)
+ printf("\tcannot disable printing: %s\n",
+ strerror(errno));
+ else {
+ upstat(pp, "printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else if (errno == ENOENT) {
+ if ((fd = open(lf, O_WRONLY|O_CREAT,
+ LOCK_FILE_MODE | LFM_PRINT_DIS)) < 0)
+ printf("\tcannot create lock file: %s\n",
+ strerror(errno));
+ else {
+ (void) close(fd);
+ upstat(pp, "printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else
+ printf("\tcannot stat lock file\n");
+ seteuid(uid);
+}
+
+struct queue **queue;
+int nitems;
+time_t mtime;
+
+/*
+ * Put the specified jobs at the top of printer queue.
+ */
+void
+topq(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ struct stat stbuf;
+ int status, changed;
+ struct printer myprinter, *pp = &myprinter;
+
+ if (argc < 3) {
+ printf("Usage: topq printer [jobnum ...] [user ...]\n");
+ return;
+ }
+
+ --argc;
+ ++argv;
+ init_printer(pp);
+ status = getprintcap(*argv, pp);
+ switch(status) {
+ default:
+ fatal(pp, pcaperr(status));
+ 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]->q_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(q)
+ struct queue *q;
+{
+ 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(q->q_name, 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(job)
+ char *job;
+{
+ register struct queue **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)->q_name+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)->q_name);
+ cnt++;
+ }
+ }
+ return(cnt);
+ }
+ /*
+ * Process item consisting of owner's name (example: henry).
+ */
+ for (qq = queue + nitems; --qq >= queue; ) {
+ seteuid(euid);
+ fp = fopen((*qq)->q_name, "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)->q_name);
+ cnt++;
+ }
+ }
+ return(cnt);
+}
+
+/*
+ * Enable everything and start printer (undo `down').
+ */
+void
+up(pp)
+ struct printer *pp;
+{
+ startpr(pp, 2);
+}
diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c
new file mode 100644
index 0000000..b4720a8
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmdtab.c
@@ -0,0 +1,83 @@
+/*
+ * 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 char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+
+#include "lpc.h"
+#include "extern.h"
+
+/*
+ * lpc -- command tables
+ */
+char aborthelp[] = "terminate a spooling daemon immediately and disable printing";
+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 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 topqhelp[] = "put job at top of printer queue";
+char uphelp[] = "enable everything and restart spooling daemon";
+
+struct cmd cmdtab[] = {
+ { "abort", aborthelp, 0, 1, doabort },
+ { "clean", cleanhelp, 0, 1, clean },
+ { "enable", enablehelp, 0, 1, enable },
+ { "exit", quithelp, quit, 0 },
+ { "disable", disablehelp, 0, 1, disable },
+ { "down", downhelp, down, 1 },
+ { "help", helphelp, help, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "restart", restarthelp, 0, 0, restart },
+ { "start", starthelp, 0, 1, startcmd },
+ { "status", statushelp, 0, 0, status },
+ { "stop", stophelp, 0, 1, stop },
+ { "topq", topqhelp, topq, 1 },
+ { "up", uphelp, 0, 1, up },
+ { "?", helphelp, help, 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..a07a09a
--- /dev/null
+++ b/usr.sbin/lpr/lpc/extern.h
@@ -0,0 +1,61 @@
+/*
+ * 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
+ */
+
+
+#include <sys/types.h>
+#include <sys/cdefs.h>
+
+
+__BEGIN_DECLS
+void clean __P((struct printer *));
+void disable __P((struct printer *));
+void doabort __P((struct printer *));
+void down __P((int, char **));
+void enable __P((struct printer *));
+void generic __P((void (*) __P((struct printer *)), int, char **));
+void help __P((int, char **));
+void quit __P((int, char **));
+void restart __P((struct printer *));
+void startcmd __P((struct printer *));
+void status __P((struct printer *));
+void stop __P((struct printer *));
+void topq __P((int, char **));
+void up __P((struct printer *));
+__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..b3a0c52
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -0,0 +1,175 @@
+.\" 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
+.\"
+.Dd April 28, 1995
+.Dt LPC 8
+.Os BSD 4.2
+.Sh NAME
+.Nm lpc
+.Nd line printer control program
+.Sh SYNOPSIS
+.Nm lpc
+.Oo
+.Ar command
+.Op Ar argument ...
+.Oc
+.Sh DESCRIPTION
+.Nm Lpc
+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 lpc
+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.
+.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 Ds -compact
+.It Ic \&? No [ command ... ]
+.It Ic help No [ 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 No {\ all\ |\ 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 clean No {\ all\ |\ 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.
+.Pp
+.It Ic disable No {\ all\ |\ 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 No {\ all\ |\ printer\ } message ...
+Turn the specified printer queue off, disable printing and put
+.Em message
+in the printer status file. 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 others know why
+.Xr lpq 1
+will indicate the printer is down and print the status message).
+.Pp
+.It Ic enable No {\ all\ |\ 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 lpc.
+.\" ne 1i
+.Pp
+.It Ic restart No {\ all\ |\ 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 start No {\ all\ |\ printer\ }
+Enable printing and start a spooling daemon for the listed printers.
+.Pp
+.It Ic status No {\ all\ |\ printer\ }
+Display the status of daemons and queues on the local machine.
+.Pp
+.It Ic stop No {\ all\ |\ printer\ }
+Stop a spooling daemon after the current job completes and disable
+printing.
+.Pp
+.It Ic topq No printer\ [\ jobnum\ ...\ ]\ [\ user\ ...\ ]
+Place the jobs in the order listed at the top of the printer queue.
+.Pp
+.It Ic up No {\ all\ |\ printer\ }
+Enable everything and start a new printer daemon. Undoes the effects of
+.Ic down .
+.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 lpd 8
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds
+.It Sy "?Ambiguous command"
+abbreviation matches more than one command
+.It Sy "?Invalid command"
+no match was found
+.It Sy "?Privileged command"
+you must be a member of group "operator" or root to execute this command
+.El
+.Sh HISTORY
+The
+.Nm
+command 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..7724b36
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.c
@@ -0,0 +1,330 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id: lpc.c,v 1.7 1998/03/22 20:19:27 jb Exp $";
+#endif /* not lint */
+
+#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 "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];
+static int top;
+uid_t uid, euid;
+
+static jmp_buf toplevel;
+
+int main __P((int, char *[]));
+static void cmdscanner __P((int));
+static struct cmd *getcmd __P((char *));
+static void intr __P((int));
+static void makeargv __P((void));
+static int ingroup __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ name = 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_priv && getuid() && ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ if (c->c_generic != 0)
+ generic(c->c_generic, argc, argv);
+ else
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+ fromatty = isatty(fileno(stdin));
+ top = setjmp(toplevel) == 0;
+ if (top)
+ signal(SIGINT, intr);
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+static void
+intr(signo)
+ int signo;
+{
+ if (!fromatty)
+ exit(0);
+ longjmp(toplevel, 1);
+}
+
+/*
+ * Command parser.
+ */
+static void
+cmdscanner(top)
+ int top;
+{
+ register struct cmd *c;
+
+ if (!top)
+ putchar('\n');
+ for (;;) {
+ if (fromatty) {
+ printf("lpc> ");
+ fflush(stdout);
+ }
+ if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
+ quit(0, NULL);
+ if (cmdline[0] == 0 || cmdline[0] == '\n')
+ break;
+ makeargv();
+ 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() && ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ continue;
+ }
+ if (c->c_generic != 0)
+ generic(c->c_generic, margc, margv);
+ else
+ (*c->c_handler)(margc, margv);
+ }
+ longjmp(toplevel, 0);
+}
+
+static struct cmd *
+getcmd(name)
+ register char *name;
+{
+ register 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()
+{
+ register char *cp;
+ register char **argp = margv;
+ register int n = 0;
+
+ margc = 0;
+ for (cp = cmdline; *cp && (cp - cmdline) < sizeof(cmdline) &&
+ n < MAX_MARGV; 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(argc, argv)
+ 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(grname)
+ char *grname;
+{
+ static struct group *gptr=NULL;
+ 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);
+ }
+ if (getgroups(NGROUPS, groups) < 0)
+ err(1, "getgroups");
+ }
+ gid = gptr->gr_gid;
+ for (i = 0; i < NGROUPS; i++)
+ if (gid == groups[i])
+ return(1);
+ return(0);
+}
diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h
new file mode 100644
index 0000000..eca7b91
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.h
@@ -0,0 +1,48 @@
+/*
+ * 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
+ */
+
+/*
+ * Line printer control program.
+ */
+struct printer;
+
+struct cmd {
+ char *c_name; /* command name */
+ char *c_help; /* help message */
+ /* routine to do the work */
+ void (*c_handler) __P((int, char *[]));
+ int c_priv; /* privileged command */
+ void (*c_generic) __P((struct printer *)); /* generic command */
+};
diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile
new file mode 100644
index 0000000..c97dc8e
--- /dev/null
+++ b/usr.sbin/lpr/lpd/Makefile
@@ -0,0 +1,11 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.8 1998/06/11 03:52:41 jb Exp $
+
+PROG= lpd
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+MAN8= lpd.8
+SRCS= lpd.c printjob.c recvjob.c lpdchar.c modes.c
+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..9d85082
--- /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
+ * $Id: extern.h,v 1.3 1997/12/02 20:45:53 wollman Exp $
+ */
+
+#include <sys/cdefs.h>
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern char fromb[];
+
+struct printer;
+struct termios;
+
+__BEGIN_DECLS
+void printjob __P((struct printer *pp));
+void startprinting __P((const char *printer));
+void recvjob __P((const char *printer));
+int msearch __P((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..2323f40
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -0,0 +1,256 @@
+.\" 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
+.\"
+.Dd April 19, 1994
+.Dt LPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm lpd
+.Nd line printer spooler daemon
+.Sh SYNOPSIS
+.Nm lpd
+.Op Fl dl
+.Op Ar port#
+.Sh DESCRIPTION
+.Nm Lpd
+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 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 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
+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
+was invoked.
+.It P
+Person. Login name of the person who invoked
+.Xr lpr .
+This is used to verify ownership by
+.Xr lprm .
+.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 Standford.
+.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
+is invoked in a pipeline).
+.El
+.Pp
+If a file cannot be opened, a message will be logged via
+.Xr syslog 3
+using the
+.Em LOG_LPR
+facility.
+.Nm Lpd
+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
+.Nm Lpd
+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 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..827bf0d
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.c
@@ -0,0 +1,634 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95";
+#endif
+static const char rcsid[] =
+ "$Id: lpd.c,v 1.9 1997/12/02 20:45:54 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 from_remote; /* from remote socket */
+
+int main __P((int, char **));
+static void reapchild __P((int));
+static void mcleanup __P((int));
+static void doit __P((void));
+static void startup __P((void));
+static void chkhost __P((struct sockaddr_in *));
+static int ckqueue __P((struct printer *));
+static void usage __P((void));
+/* From rcmd.c: */
+int __ivaliduser __P((FILE *, u_long, const char *,
+ const char *));
+
+uid_t uid, euid;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int f, funix, finet, options, fromlen, i, errs;
+ fd_set defreadfds;
+ struct sockaddr_un un, fromunix;
+ struct sockaddr_in sin, frominet;
+ int lfd;
+ sigset_t omask, nmask;
+ struct servent *sp, serv;
+
+ euid = geteuid(); /* these shouldn't be different */
+ uid = getuid();
+ options = 0;
+ gethostname(host, sizeof(host));
+
+ name = "lpd";
+
+ if (euid != 0)
+ errx(EX_NOPERM,"must run as root");
+
+ errs = 0;
+ while ((i = getopt(argc, argv, "dl")) != -1)
+ switch (i) {
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ default:
+ errs++;
+ }
+ 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, "restarted");
+ (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 deamon 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);
+ finet = socket(AF_INET, SOCK_STREAM, 0);
+ if (finet >= 0) {
+ if (options & SO_DEBUG)
+ if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ mcleanup(0);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = sp->s_port;
+ if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ syslog(LOG_ERR, "bind: %m");
+ mcleanup(0);
+ }
+ FD_SET(finet, &defreadfds);
+ listen(finet, 5);
+ }
+ /*
+ * Main loop: accept, do a request, continue.
+ */
+ memset(&frominet, 0, sizeof(frominet));
+ memset(&fromunix, 0, sizeof(fromunix));
+ /*
+ * 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;
+ }
+ if (FD_ISSET(funix, &readfds)) {
+ domain = AF_UNIX, fromlen = sizeof(fromunix);
+ s = accept(funix,
+ (struct sockaddr *)&fromunix, &fromlen);
+ } else /* if (FD_ISSET(finet, &readfds)) */ {
+ domain = AF_INET, fromlen = sizeof(frominet);
+ s = accept(finet,
+ (struct sockaddr *)&frominet, &fromlen);
+ if (frominet.sin_port == htons(20)) {
+ close(s);
+ continue;
+ }
+ }
+ if (s < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "accept: %m");
+ continue;
+ }
+ if (fork() == 0) {
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ (void) close(funix);
+ (void) close(finet);
+ dup2(s, 1);
+ (void) close(s);
+ if (domain == AF_INET) {
+ from_remote = 1;
+ chkhost(&frominet);
+ } else
+ from_remote = 0;
+ doit();
+ exit(0);
+ }
+ (void) close(s);
+ }
+}
+
+static void
+reapchild(signo)
+ int signo;
+{
+ union wait status;
+
+ while (wait3((int *)&status, WNOHANG, 0) > 0)
+ ;
+}
+
+static void
+mcleanup(signo)
+ int signo;
+{
+ if (lflag)
+ 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 */
+
+char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */
+char cbuf[BUFSIZ]; /* command line buffer */
+char *cmdnames[] = {
+ "null",
+ "printjob",
+ "recvjob",
+ "displayq short",
+ "displayq long",
+ "rmjob"
+};
+
+static void
+doit()
+{
+ 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(1, 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, cmdnames[(u_char)*cp], cp+1);
+ else
+ syslog(LOG_INFO, "bad request (%d) from %s",
+ *cp, from);
+ }
+ 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, 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()
+{
+ 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, "work for %s", pp->printer);
+ if ((pid = fork()) < 0) {
+ syslog(LOG_WARNING, "startup: cannot fork");
+ mcleanup(0);
+ }
+ if (pid == 0) {
+ lastprinter();
+ printjob(pp);
+ /* NOTREACHED */
+ }
+ do {
+next:
+ more = nextprinter(pp, &status);
+errloop:
+ if (status)
+ syslog(LOG_WARNING,
+ "printcap for %s has errors, skipping",
+ pp->printer ? pp->printer : "<???>");
+ } while (more && status);
+ }
+}
+
+/*
+ * Make sure there's some work to do before forking off a child
+ */
+static int
+ckqueue(pp)
+ 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 from host has access to the line printer.
+ */
+static void
+chkhost(f)
+ struct sockaddr_in *f;
+{
+ register struct hostent *hp;
+ register FILE *hostf;
+ int first = 1;
+ int good = 0;
+
+ /* Need real hostname for temporary filenames */
+ hp = gethostbyaddr((char *)&f->sin_addr,
+ sizeof(struct in_addr), f->sin_family);
+ if (hp == NULL)
+ fatal(0, "Host name for your address (%s) unknown",
+ inet_ntoa(f->sin_addr));
+
+ (void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1);
+ from[sizeof(fromb) - 1] = '\0';
+ from = fromb;
+
+ /* Check for spoof, ala rlogind */
+ hp = gethostbyname(fromb);
+ if (!hp)
+ fatal(0, "hostname for your address (%s) unknown",
+ inet_ntoa(f->sin_addr));
+ for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) {
+ if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr,
+ sizeof(f->sin_addr)))
+ good = 1;
+ }
+ if (good == 0)
+ fatal(0, "address for your hostname (%s) not matched",
+ inet_ntoa(f->sin_addr));
+
+ hostf = fopen(_PATH_HOSTSEQUIV, "r");
+again:
+ if (hostf) {
+ if (__ivaliduser(hostf, f->sin_addr.s_addr,
+ DUMMY, DUMMY) == 0) {
+ (void) fclose(hostf);
+ return;
+ }
+ (void) fclose(hostf);
+ }
+ if (first == 1) {
+ first = 0;
+ hostf = fopen(_PATH_HOSTSLPD, "r");
+ goto again;
+ }
+ fatal(0, "Your host does not have line printer access");
+ /*NOTREACHED*/
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: lpd [-dl] [port#]\n");
+ 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..f5c542a
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpdchar.c
@@ -0,0 +1,1071 @@
+/*
+ * 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 char sccsid[] = "@(#)lpdchar.c 8.1 (Berkeley) 6/6/93";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * 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..b24cffa
--- /dev/null
+++ b/usr.sbin/lpr/lpd/modes.c
@@ -0,0 +1,236 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94";
+*/
+static const char rcsid[] =
+ "$Id: modes.c,v 1.4 1997/12/02 20:45:56 wollman Exp $";
+#endif /* not lint */
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+#include "lp.local.h"
+#include "extern.h"
+
+struct modes {
+ char *name;
+ long set;
+ 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 },
+};
+
+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 },
+};
+
+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 },
+};
+
+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 },
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(str, ip)
+ 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..c635341
--- /dev/null
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -0,0 +1,1650 @@
+/*
+ * 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 */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95";
+*/
+static const char rcsid[] =
+ "$Id: printjob.c,v 1.19 1998/08/21 18:08:46 brian Exp $";
+#endif /* not lint */
+
+
+/*
+ * 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 /* absorb fork error */
+#define DOABORT 1 /* abort if dofork fails */
+
+/*
+ * 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 int child; /* id of any filters */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int ofilter; /* id of output filter, if any */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int pid; /* pid of lpd process */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+
+static char class[32]; /* classification field */
+static char fromhost[32]; /* 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 */
+static char tempfile[] = "errsXXXXXX"; /* file name for filter errors */
+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 __P((int));
+static void alarmhandler __P((int));
+static void banner __P((struct printer *pp, char *name1, char *name2));
+static int dofork __P((const struct printer *pp, int action));
+static int dropit __P((int));
+static void init __P((struct printer *pp));
+static void openpr __P((const struct printer *pp));
+static void opennet __P((const struct printer *pp));
+static void opentty __P((const struct printer *pp));
+static void openrem __P((const struct printer *pp));
+static int print __P((struct printer *pp, int format, char *file));
+static int printit __P((struct printer *pp, char *file));
+static void pstatus __P((const struct printer *, const char *, ...));
+static char response __P((const struct printer *pp));
+static void scan_out __P((struct printer *pp, int scfd, char *scsp,
+ int dlm));
+static char *scnline __P((int, char *, int));
+static int sendfile __P((struct printer *pp, int type, char *file,
+ int format));
+static int sendit __P((struct printer *pp, char *file));
+static void sendmail __P((struct printer *pp, char *user, int bombed));
+static void setty __P((const struct printer *pp));
+
+void
+printjob(pp)
+ struct printer *pp;
+{
+ struct stat stb;
+ register struct queue *q, **qp;
+ struct queue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ int errcnt, count = 0;
+
+ init(pp); /* set up capabilities */
+ (void) write(1, "", 1); /* ack that daemon is started */
+ (void) close(2); /* set up log file */
+ if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
+ syslog(LOG_ERR, "%s: %m", pp->log_file);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ pid = getpid(); /* for use with lprm */
+ setpgrp(0, pid);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ (void) mktemp(tempfile);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(pp->spool_dir) < 0) {
+ syslog(LOG_ERR, "%s: %m", 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: %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: %s: %m", pp->printer, pp->lock_file);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", pid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: %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: %s: %m", pp->printer,
+ pp->lock_file);
+ }
+ 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->q_name, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->q_name);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: %s: %m", pp->printer,
+ pp->lock_file);
+ if (!pp->remote)
+ i = printit(pp, q->q_name);
+ else
+ i = sendit(pp, q->q_name);
+ /*
+ * 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: %s: %m",
+ pp->printer, pp->lock_file);
+ break;
+ }
+ }
+ if (i == OK) /* file ok and printed */
+ count++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", pp->printer);
+ if (ofilter > 0) {
+ kill(ofilter, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: %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->q_name);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->q_name);
+ q->q_name[0] = 'd';
+ (void) unlink(q->q_name);
+ 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 (count > 0) { /* Files 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(tempfile);
+ 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(pp, file)
+ struct printer *pp;
+ char *file;
+{
+ register int i;
+ char *cp;
+ int bombed = OK;
+
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: %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");
+
+ /*
+ * 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
+ * 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
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strncpy(fromhost, line+1, sizeof(fromhost) - 1);
+ fromhost[sizeof(fromhost) - 1] = '\0';
+ if (class[0] == '\0') {
+ strncpy(class, line+1, sizeof(class) - 1);
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'P':
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ logname[sizeof(logname) - 1] = '\0';
+ 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') {
+ strncpy(jobname, line+1, sizeof(jobname) - 1);
+ jobname[sizeof(jobname) - 1] = '\0';
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ else if (class[0] == '\0')
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ continue;
+
+ case 'T': /* header title for pr */
+ strncpy(title, line+1, sizeof(title) - 1);
+ title[sizeof(title) - 1] = '\0';
+ 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') {
+ strncpy(fonts[line[0]-'1'], line+1,
+ 50-1);
+ fonts[line[0]-'1'][50-1] = '\0';
+ }
+ continue;
+
+ case 'W': /* page width */
+ strncpy(width+2, line+1, sizeof(width) - 3);
+ width[2+sizeof(width) - 3] = '\0';
+ continue;
+
+ case 'I': /* indent amount */
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ indent[2+sizeof(indent) - 3] = '\0';
+ continue;
+
+ default: /* some file to print */
+ switch (i = print(pp, line[0], line+1)) {
+ 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(pp, format, file)
+ struct printer *pp;
+ int format;
+ char *file;
+{
+ register int n;
+ register char *prog;
+ int fi, fo;
+ FILE *fp;
+ char *av[15], buf[BUFSIZ];
+ int pid, p[2], stopped = 0;
+ union wait status;
+ struct stat stb;
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
+ 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);
+ 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')) {
+ pp->tof = 0;
+ while ((n = read(fi, buf, 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;
+ av[0] = "pr";
+ av[1] = width;
+ av[2] = length;
+ av[3] = "-h";
+ av[4] = *title ? title : " ";
+ av[5] = "-F";
+ av[6] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0); /* file is stdin */
+ dup2(p[1], 1); /* pipe is stdout */
+ closelog();
+ closeallfds(3);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ", "-F", 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 '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++] = fromhost;
+ av[n++] = pp->acct_file;
+ av[n] = 0;
+ fo = pfd;
+ if (ofilter > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((pid =
+ wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
+ ;
+ if (status.w_stopval != WSTOPPED) {
+ (void) close(fi);
+ syslog(LOG_WARNING,
+ "%s: output filter died "
+ "(retcode=%d termsig=%d)",
+ pp->printer, status.w_retcode,
+ status.w_termsig);
+ return(REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0);
+ dup2(fo, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ closeallfds(3);
+ execv(prog, av);
+ syslog(LOG_ERR, "cannot execv %s", prog);
+ exit(2);
+ }
+ (void) close(fi);
+ if (child < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 && pid != child)
+ ;
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(ofilter, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ pp->tof = 0;
+
+ /* Copy filter output to "lf" logfile */
+ if ((fp = fopen(tempfile, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (!WIFEXITED(status)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ pp->printer, format, status.w_termsig);
+ return(ERROR);
+ }
+ switch (status.w_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, status.w_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(pp, file)
+ struct printer *pp;
+ char *file;
+{
+ register int i, err = OK;
+ char *cp, last[BUFSIZ];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(OK);
+ /*
+ * 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
+ */
+ 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') {
+ strcpy(fromhost, line+1);
+ if (class[0] == '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ } else if (line[0] == 'P') {
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(pp, line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ strcpy(last, line);
+ while ((i = getline(cfp)) != 0)
+ if (strcmp(last, line))
+ break;
+ switch (sendfile(pp, '\3', last+1, *last)) {
+ 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') > 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(pp, type, file, format)
+ struct printer *pp;
+ int type;
+ char *file;
+ char format;
+{
+ register int f, i, amt;
+ struct stat stb;
+ char buf[BUFSIZ];
+ int sizerr, resp, closedpr;
+
+ if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
+ 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(f, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+
+ sizerr = 0;
+ closedpr = 0;
+ if (type == '\3') {
+ if (pp->filters[LPF_INPUT]) {
+ /*
+ * We're sending something with an ifilter, we have to
+ * run the ifilter and store the output as a
+ * temporary file (tfile)... the protocol requires us
+ * to send the file size
+ */
+ char *av[15];
+ int n;
+ int ifilter;
+ union wait status; /* XXX */
+
+ strcpy(tfile,TFILENAME);
+ if ((tfd = mkstemp(tfile)) == -1) {
+ syslog(LOG_ERR, "mkstemp: %m");
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(pp->filters[LPF_INPUT], '/')) == NULL)
+ av[0] = pp->filters[LPF_INPUT];
+ else
+ av[0]++;
+ if (format == 'l')
+ av[n=1] = "-c";
+ else
+ n = 0;
+ av[++n] = width;
+ av[++n] = length;
+ av[++n] = indent;
+ av[++n] = "-n";
+ av[++n] = logname;
+ av[++n] = "-h";
+ av[++n] = fromhost;
+ av[++n] = pp->acct_file;
+ av[++n] = 0;
+ if ((ifilter = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(f, 0);
+ dup2(tfd, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC,
+ TEMP_FILE_MODE);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ closeallfds(3);
+ execv(pp->filters[LPF_INPUT], av);
+ syslog(LOG_ERR, "cannot execv %s",
+ pp->filters[LPF_INPUT]);
+ exit(2);
+ }
+ (void) close(f);
+ if (ifilter < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 &&
+ pid != ifilter)
+ ;
+ switch (status.w_retcode) {
+ case 0:
+ break;
+ case 1:
+ unlink(tfile);
+ return(REPRINT);
+ case 2:
+ unlink(tfile);
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited"
+ " (retcode=%d)",
+ pp->printer, format, status.w_retcode);
+ unlink(tfile);
+ return(FILTERERR);
+ }
+ if (fstat(tfd, &stb) < 0) /* the size of tfile */
+ return(ERROR);
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ } else if (ofilter) {
+ /*
+ * We're sending something with an ofilter, we have to
+ * store the output as a temporary file (tfile)... the
+ * protocol requires us to send the file size
+ */
+ int i;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt) {
+ sizerr = 1;
+ break;
+ }
+ if (write(ofd, buf, amt) != amt) {
+ (void) close(f);
+ return(REPRINT);
+ }
+ }
+ close(ofd);
+ close(f);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ if (fstat(tfd, &stb) < 0) { /* the size of tfile */
+ openpr(pp);
+ return(ERROR);
+ }
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ closedpr = 1;
+ }
+ }
+
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response(pp)) < 0 || resp == '\1') {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr(pp);
+ }
+ return(REPRINT);
+ } 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);
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr(pp);
+ }
+ return(REPRINT);
+ }
+ }
+
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ }
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ if (closedpr)
+ openpr(pp);
+ return(ERROR);
+ }
+ if (write(pfd, "", 1) != 1 || response(pp)) {
+ if (closedpr)
+ openpr(pp);
+ return(REPRINT);
+ }
+ if (closedpr)
+ openpr(pp);
+ return(OK);
+}
+
+/*
+ * 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(pp)
+ 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(pp, name1, name2)
+ struct printer *pp;
+ char *name1, *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(key, p, c)
+ register int key;
+ register 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(pp, scfd, scsp, dlm)
+ struct printer *pp;
+ int scfd, dlm;
+ char *scsp;
+{
+ 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(c)
+ 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(pp, user, bombed)
+ struct printer *pp;
+ char *user;
+ int bombed;
+{
+ register int i;
+ int p[2], s;
+ register char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(p[0], 0);
+ closelog();
+ closeallfds(3);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", 0);
+ _exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], 1);
+ printf("To: %s@%s\n", user, fromhost);
+ printf("Subject: %s printer job \"%s\"\n", pp->printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+
+ cp = "XXX compiler confusion"; /* XXX shut GCC up */
+ switch (bombed) {
+ case OK:
+ printf("\ncompleted successfully\n");
+ cp = "OK";
+ break;
+ default:
+ case FATALERR:
+ printf("\ncould not be printed\n");
+ cp = "FATALERR";
+ break;
+ case NOACCT:
+ printf("\ncould not be printed without an account on %s\n", host);
+ cp = "NOACCT";
+ break;
+ case FILTERERR:
+ if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
+ (fp = fopen(tempfile, "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);
+ cp = "FILTERERR";
+ break;
+ case ACCESS:
+ printf("\nwas not printed because it was not linked to the original file\n");
+ cp = "ACCESS";
+ }
+ fflush(stdout);
+ (void) close(1);
+ } else {
+ syslog(LOG_WARNING, "unable to send mail to %s: %m", user);
+ 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)",
+ user, *jobname ? jobname : "<unknown>", pp->printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(pp, action)
+ const struct printer *pp;
+ int action;
+{
+ register int i, pid;
+
+ for (i = 0; i < 20; i++) {
+ if ((pid = fork()) < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (pid == 0)
+ setuid(pp->daemon_user);
+ return(pid);
+ }
+ syslog(LOG_ERR, "can't fork");
+
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /*FALL THRU*/
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ kill(0, SIGINT);
+ if (ofilter > 0)
+ kill(ofilter, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (ofilter > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init(pp)
+ 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(printer)
+ 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(pp)
+ const struct printer *pp;
+{
+ int p[2];
+ char *cp;
+
+ if (pp->remote) {
+ openrem(pp);
+ } 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] && !ofilter) {
+ pipe(p);
+ if (pp->remote) {
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((ofilter = dofork(pp, DOABORT)) == 0) { /* child */
+ dup2(p[0], 0); /* pipe is std in */
+ /* tfile/printer is stdout */
+ dup2(pp->remote ? tfd : pfd, 1);
+ 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, 0);
+ syslog(LOG_ERR, "%s: %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;
+ ofilter = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(pp)
+ 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 %d", ep, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty(pp)
+ 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(pp)
+ 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(pp)
+ 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);
+ }
+ }
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+pstatus(const struct printer *pp, const char *msg, ...)
+#else
+pstatus(pp, msg, va_alist)
+ const struct printer *pp;
+ char *msg;
+ va_dcl
+#endif
+{
+ int fd;
+ char *buf;
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+
+ umask(0);
+ fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: %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(signo)
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c
new file mode 100644
index 0000000..74fc253
--- /dev/null
+++ b/usr.sbin/lpr/lpd/recvjob.c
@@ -0,0 +1,376 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] =
+ "$Id: recvjob.c,v 1.12 1997/12/02 20:45:59 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(1, sp, 1);
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize __P((int));
+static void frecverr __P((const char *, ...));
+static int noresponse __P((void));
+static void rcleanup __P((int));
+static int read_number __P((char *));
+static int readfile __P((char *, int));
+static int readjob __P((struct printer *pp));
+
+
+void
+recvjob(printer)
+ 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(2); /* 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: %s: %m", pp->printer, pp->spool_dir);
+ 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: %s: %m", pp->printer, pp->spool_dir);
+ 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(pp)
+ struct printer *pp;
+{
+ register int size, nfiles;
+ register char *cp;
+
+ ack();
+ nfiles = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ do {
+ if ((size = read(1, cp, 1)) != 1) {
+ if (size < 0)
+ frecverr("%s: lost connection",
+ pp->printer);
+ return(nfiles);
+ }
+ } while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
+ if (cp - line + 1 >= sizeof(line))
+ frecverr("readjob overflow");
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 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
+ */
+ strncpy(cp + 6, from, sizeof(line) + line - cp - 7);
+ line[sizeof(line) - 1 ] = '\0';
+ strncpy(tfname, cp, sizeof(tfname) - 1);
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (strchr(tfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ tfname);
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ if (!readfile(tfname, size)) {
+ rcleanup(0);
+ continue;
+ }
+ if (link(tfname, cp) < 0)
+ frecverr("%s: %m", tfname);
+ (void) unlink(tfname);
+ tfname[0] = '\0';
+ nfiles++;
+ continue;
+
+ case '\3': /* read df file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ (void) strncpy(dfname, cp, sizeof(dfname) - 1);
+ dfname[sizeof(dfname) - 1] = '\0';
+ if (strchr(dfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ dfname);
+ (void) readfile(dfname, size);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(file, size)
+ char *file;
+ int size;
+{
+ register char *cp;
+ char buf[BUFSIZ];
+ register int i, j, amt;
+ int fd, err;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0)
+ frecverr("readfile: %s: illegal path name: %m", file);
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(1, cp, amt);
+ if (j <= 0)
+ frecverr("lost connection");
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err)
+ frecverr("%s: write error", file);
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return(0);
+ }
+ ack();
+ return(1);
+}
+
+static int
+noresponse()
+{
+ char resp;
+
+ if (read(1, &resp, 1) != 1)
+ frecverr("lost connection");
+ 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(size)
+ 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(fn)
+ 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(signo)
+ int signo;
+{
+ 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';
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+frecverr(const char *msg, ...)
+#else
+frecverr(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ rcleanup(0);
+ syslog(LOG_ERR, "%s", fromb);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ 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..d89ed439
--- /dev/null
+++ b/usr.sbin/lpr/lpq/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.5 1998/03/07 09:48:02 bde Exp $
+
+PROG= lpq
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+
+.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..04ecdad
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.1
@@ -0,0 +1,136 @@
+.\" 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
+.\"
+.Dd April 28, 1995
+.Dt LPQ 1
+.Os BSD 4.2
+.Sh NAME
+.Nm lpq
+.Nd spool queue examination program
+.Sh SYNOPSIS
+.Nm lpq
+.Op Fl a
+.Op Fl l
+.Op Fl P Ns Ar printer
+.Op job # ...
+.Op user ...
+.Sh DESCRIPTION
+.Nm Lpq
+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.
+.Nm Lpq
+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 lpq :
+.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
+.Nm Lpq
+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..04adc94
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.c
@@ -0,0 +1,201 @@
+/*
+ * 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 */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)lpq.c 8.3 (Berkeley) 5/10/95";
+*/
+static const char rcsid[] =
+ "$Id: lpq.c,v 1.5 1997/12/02 20:46:04 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 __P((const struct printer *));
+static void usage __P((void));
+int main __P((int, char **));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, aflag, lflag;
+ char *printer;
+ struct printer myprinter, *pp = &myprinter;
+
+ printer = NULL;
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ name = *argv;
+ if (gethostname(host, sizeof(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, pcaperr(status));
+ }
+ } while (more && status);
+ }
+ } else {
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(printer, pp);
+ if (status < 0)
+ fatal(pp, pcaperr(status));
+
+ displayq(pp, lflag);
+ }
+ exit(0);
+}
+
+static int
+ckqueue(pp)
+ 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()
+{
+ 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..e3c0546
--- /dev/null
+++ b/usr.sbin/lpr/lpr/Makefile
@@ -0,0 +1,15 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.6 1998/03/07 09:48:07 bde Exp $
+
+PROG= lpr
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+MAN1= lpr.1
+MAN5= printcap.5
+.PATH: ${.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..672edad
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.1
@@ -0,0 +1,253 @@
+.\" 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
+.\" $Id: lpr.1,v 1.7 1997/02/22 16:06:19 peter Exp $
+.\" "
+.Dd June 6, 1993
+.Dt LPR 1
+.Os BSD 4
+.Sh NAME
+.Nm lpr
+.Nd off line print
+.Sh SYNOPSIS
+.Nm lpr
+.Op Fl P Ns Ar printer
+.Op Fl \&# Ns Ar num
+.Op Fl C Ar class
+.Op Fl J Ar job
+.Op Fl T Ar title
+.Op Fl U Ar user
+.Op Fl i Ar numcols
+.Op Fl 1234 Ar font
+.Op Fl w Ns Ar num
+.Op Fl cdfghlnmprstv
+.Op Ar name ...
+.Sh DESCRIPTION
+.Nm Lpr
+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.
+.Bl -tag -width indent
+.It Fl c
+The files are assumed to contain data produced by
+.Xr cifplot 1
+.It Fl d
+The files are assumed to contain data from
+.Em tex
+.Pf ( Tn DVI
+format from Stanford).
+.It Fl f
+Use a filter which interprets the first character of each line as a
+standard
+.Tn FORTRAN
+carriage control character.
+.It Fl g
+The files are assumed to contain standard plot data as produced by the
+.Xr plot
+routines (see also
+.Xr plot
+for the filters used by the printer spooler).
+.It Fl l
+Use a filter which allows control characters to be printed and suppresses
+page breaks.
+.It Fl n
+The files are assumed to contain data from
+.Em ditroff
+(device independent troff).
+.It Fl p
+Use
+.Xr pr 1
+to format the files.
+.It Fl t
+The files are assumed to contain data from
+.Xr troff 1
+(cat phototypesetter commands).
+.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 Oo Cm 1234 Oc 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 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 i numcols
+The output is indented by
+.Pq Ar numcols .
+.It Fl w Ns 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 lpr :
+.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.
+.Nm Lpr
+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
+.Xr tex
+reside on the host with the printer. It is currently not possible to
+use local font libraries.
diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c
new file mode 100644
index 0000000..f865574
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.c
@@ -0,0 +1,787 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: lpr.c 8.4 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id: lpr.c,v 1.28 1999/02/01 19:53:08 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 <dirent.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <err.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 = 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 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 *person; /* user name */
+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 int userid; /* user id */
+static char *Uflag; /* user name specified with -U flag */
+static char *width; /* width for versatec printing */
+
+static struct stat statb;
+
+static void card __P((int, char *));
+static int checkwriteperm __P((char*, char *));
+static void chkprinter __P((char *printer, struct printer *pp));
+static void cleanup __P((int));
+static void copy __P((const struct printer *, int, char []));
+static char *itoa __P((int));
+static char *linked __P((char *));
+int main __P((int, char **));
+static char *lmktemp __P((const struct printer *pp, char *, int, int));
+static void mktemps __P((const struct printer *pp));
+static int nfile __P((char *));
+static int test __P((char *));
+static void usage __P((void));
+
+uid_t uid, euid;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pw;
+ struct group *gptr;
+ char *arg, *cp, *printer, *p;
+ char buf[BUFSIZ];
+ int c, i, f, errs;
+ struct stat stb;
+ 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);
+
+ name = argv[0];
+ gethostname(host, sizeof(host));
+ openlog("lpd", 0, LOG_LPR);
+
+ errs = 0;
+ while ((c = getopt(argc, argv,
+ ":#:1:2:3:4:C:J:P:T:U: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 'T': /* pr's title line */
+ title = optarg;
+ break;
+
+ case 'U': /* user name */
+ hdr++;
+ Uflag = 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");
+ person = Uflag;
+ } else {
+ person = getlogin();
+ if (userid != pp->daemon_user || person == 0) {
+ if ((pw = getpwuid(userid)) == NULL)
+ errx(1, "Who are you?");
+ person = 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(person, *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', host);
+ card('P', person);
+ if (hdr) {
+ if (jobname == NULL) {
+ if (argc == 0)
+ jobname = "stdin";
+ else
+ jobname = ((arg = strrchr(argv[0], '/'))
+ ? arg + 1 : argv[0]);
+ }
+ card('J', jobname);
+ card('C', class);
+ card('L', person);
+ }
+ if (iflag)
+ card('I', itoa(indent));
+ if (mailflg)
+ card('M', person);
+ 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);
+
+ /*
+ * 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", name, arg);
+ if ((i = open(arg, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", name, arg);
+ } else {
+ copy(pp, i, arg);
+ (void) close(i);
+ if (f && unlink(arg) < 0)
+ printf("%s: %s: not removed\n", name, 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 c;
+
+ if (read(tfd, &c, 1) == 1 &&
+ lseek(tfd, (off_t)0, 0) == 0 &&
+ write(tfd, &c, 1) != 1) {
+ printf("%s: cannot touch %s\n", name, tfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ (void) close(tfd);
+ }
+ if (link(tfname, cfname) < 0) {
+ printf("%s: cannot rename %s\n", name, 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(pp, f, n)
+ const struct printer *pp;
+ int f;
+ 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", name, 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",
+ name, n);
+ break;
+ }
+ }
+ }
+ (void) close(fd);
+ if (nc==0 && nr==0)
+ printf("%s: %s: empty input file\n", name, f ? n : "stdin");
+ else
+ nact++;
+}
+
+/*
+ * Try and link the file to dfname. Return a pointer to the full
+ * path name if successful.
+ */
+static char *
+linked(file)
+ register 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(c, p2)
+ register int c;
+ register char *p2;
+{
+ char buf[BUFSIZ];
+ register char *p1 = buf;
+ register int 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(n)
+ 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", name, n);
+ cleanup(0);
+ }
+ if (fchown(f, userid, -1) < 0) {
+ printf("%s: cannot chown %s\n", name, 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(signo)
+ int signo;
+{
+ 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(file)
+ char *file;
+{
+ struct exec execb;
+ char *path;
+ register int fd;
+ register char *cp;
+
+ if (access(file, 4) < 0) {
+ printf("%s: cannot access %s\n", name, file);
+ return(-1);
+ }
+ if (stat(file, &statb) < 0) {
+ printf("%s: cannot stat %s\n", name, file);
+ return(-1);
+ }
+ if ((statb.st_mode & S_IFMT) == S_IFDIR) {
+ printf("%s: %s is a directory\n", name, file);
+ return(-1);
+ }
+ if (statb.st_size == 0) {
+ printf("%s: %s is an empty file\n", name, file);
+ return(-1);
+ }
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", name, 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", name, file);
+ goto error1;
+ }
+ (void) close(fd);
+ if (rflag) {
+ if ((cp = strrchr(file, '/')) == NULL) {
+ if (checkwriteperm(file,".") == 0)
+ return(1);
+ } else {
+ if (cp == file) {
+ fd = checkwriteperm(file,"/");
+ } else {
+ path = alloca(strlen(file) + 1);
+ strcpy(path,file);
+ *cp = '\0';
+ fd = checkwriteperm(path,file);
+ *cp = '/';
+ }
+ if (fd == 0)
+ return(1);
+ }
+ printf("%s: %s: is not removable by you\n", name, file);
+ }
+ return(0);
+
+error1:
+ printf(" and is unprintable\n");
+ (void) close(fd);
+ return(-1);
+}
+
+static int
+checkwriteperm(file, directory)
+ char *file, *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(i)
+ register 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(s, pp)
+ char *s;
+ struct printer *pp;
+{
+ int status;
+
+ init_printer(pp);
+ status = getprintcap(s, pp);
+ switch(status) {
+ case PCAPERR_OSERR:
+ case PCAPERR_TCLOOP:
+ errx(1, "%s: %s", s, pcaperr(status));
+ case PCAPERR_NOTFOUND:
+ errx(1, "%s: unknown printer", s);
+ case PCAPERR_TCOPEN:
+ warnx("%s: unresolved tc= reference(s)", s);
+ }
+}
+
+/*
+ * Tell the user what we wanna get.
+ */
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]",
+"[-i[numcols]] [-1234 font] [-wnum] [-cdfghlnmprstv] [name ...]");
+ exit(1);
+}
+
+
+/*
+ * Make the temp files.
+ */
+static void
+mktemps(pp)
+ 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", name, buf);
+ exit(1);
+ }
+ if (flock(fd, LOCK_EX)) {
+ printf("%s: cannot lock %s\n", name, 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(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(pp, id, num, len)
+ const struct printer *pp;
+ char *id;
+ int num, 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, 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..2fbb044
--- /dev/null
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -0,0 +1,369 @@
+.\" 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
+.\" $Id: printcap.5,v 1.14 1998/06/10 12:34:26 phk Exp $
+.\"
+.Dd December 11, 1993
+.Dt PRINTCAP 5
+.Os BSD 4.2
+.Sh NAME
+.Nm printcap
+.Nd printer capability data base
+.Sh SYNOPSIS
+.Nm printcap
+.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 printcap
+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"
+.Pf ( 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"
+.Pf ( 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"
+.Pf ( 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"
+.It "ms str" Ta Dv NULL Ta No "if lp is a tty, a comma-separated, "
+.Xr stty 1
+-like list describing the tty modes
+.It "mx num 1000 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 "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 ``lp'' 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 "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 "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 "st spool.status"
+.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 -filled -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 printcap
+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 -filled -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
+If
+.Cm lp
+is specified as
+.Em port Ns No @ Ns Em machine
+(and
+.Cm rm
+is not in use), printing will be send directly to the given
+.Em port
+on the given
+.Em machine .
+.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 syslog 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 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..1d1d491
--- /dev/null
+++ b/usr.sbin/lpr/lprm/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.6 1998/03/07 09:48:09 bde Exp $
+
+PROG= lprm
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+.PATH: ${.CURDIR}/../common_source
+DPADD= ${LIBLPR}
+LDADD= ${LIBLPR}
+
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+
+.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..02da034
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.1
@@ -0,0 +1,145 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt LPRM 1
+.Os BSD 4.2
+.Sh NAME
+.Nm lprm
+.Nd remove jobs from the line printer spooling queue
+.Sh SYNOPSIS
+.Nm lprm
+.Op Fl P Ns Ar printer
+.Op Fl
+.Op job # ...
+.Op Ar user ...
+.Sh DESCRIPTION
+.Nm Lprm
+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 lprm .
+.Pp
+.Nm Lprm
+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
+.Nm Lprm
+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 lprm .
+.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..91c3e44
--- /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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lprm.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: lprm.c,v 1.4 1997/12/02 20:46:15 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 __P((int, char *[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *arg, *printer;
+ struct passwd *p;
+ static char root[] = "root";
+
+ printer = NULL;
+ uid = getuid();
+ euid = geteuid();
+ seteuid(uid); /* be safe */
+ name = argv[0];
+ gethostname(host, sizeof(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()
+{
+ 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..fe13560
--- /dev/null
+++ b/usr.sbin/lpr/lptest/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lptest
+
+.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..95b28f3
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.1
@@ -0,0 +1,74 @@
+.\" 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
+.\"
+.Dd December 30, 1993
+.Dt LPTEST 1
+.Os BSD 4.3
+.Sh NAME
+.Nm lptest
+.Nd generate lineprinter ripple pattern
+.Sh SYNOPSIS
+.Nm lptest
+.Op Ar length
+.Op Ar count
+.Sh DESCRIPTION
+.Nm Lptest
+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
+.Nm Lptest
+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..1cf2206
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.c
@@ -0,0 +1,83 @@
+/*
+ * 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 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[] = "@(#)lptest.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * lptest -- line printer test program (and other devices).
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int len, count;
+ register 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..41105db
--- /dev/null
+++ b/usr.sbin/lpr/pac/Makefile
@@ -0,0 +1,11 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.5 1998/03/07 09:48:14 bde Exp $
+
+PROG= pac
+CFLAGS+=-I${.CURDIR}/../common_source ${CWARNFLAGS}
+MAN8= pac.8
+.PATH: ${.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..433cfe4
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.8
@@ -0,0 +1,106 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt PAC 8
+.Os BSD 4.2
+.Sh NAME
+.Nm pac
+.Nd printer/plotter accounting information
+.Sh SYNOPSIS
+.Nm pac
+.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
+.Nm Pac
+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
+command 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..727c67f
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.c
@@ -0,0 +1,462 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: pac.c,v 1.8 1997/12/02 20:46:22 wollman Exp $";
+#endif /* not lint */
+
+/*
+ * 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 int 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 long price100; /* per-page cost in 100th of a cent */
+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 __P((int, char **));
+static void account __P((FILE *));
+static int any __P((int, char []));
+static int chkprinter __P((char *));
+static void dumpit __P((void));
+static int hash __P((char []));
+static struct hent *enter __P((char []));
+static struct hent *lookup __P((char []));
+static int qucmp __P((const void *, const void *));
+static void rewrite __P((void));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *acct;
+ 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 ((acct = fopen(acctfile, "r")) == NULL) {
+ perror(acctfile);
+ exit(1);
+ }
+ account(acct);
+ fclose(acct);
+ if ((acct = fopen(sumfile, "r")) != NULL) {
+ account(acct);
+ fclose(acct);
+ }
+ if (summarize)
+ rewrite();
+ else
+ dumpit();
+ exit(errs);
+}
+
+static void
+usage()
+{
+ 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(acct)
+ register FILE *acct;
+{
+ char linebuf[BUFSIZ];
+ double t;
+ register char *cp, *cp2;
+ register struct hent *hp;
+ register int ic;
+
+ while (fgets(linebuf, BUFSIZ, acct) != 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()
+{
+ struct hent **base;
+ register struct hent *hp, **ap;
+ register int hno, c, runs;
+ 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()
+{
+ register struct hent *hp;
+ register int i;
+ register 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(name)
+ 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, 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(name)
+ 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(name)
+ char name[];
+{
+ register int h;
+ register char *cp;
+
+ for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
+ ;
+ return((h & 0x7fffffff) % HSHSIZE);
+}
+
+/*
+ * Other stuff
+ */
+static int
+any(ch, str)
+ int ch;
+ char str[];
+{
+ register int c = ch;
+ register 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(a, b)
+ const void *a, *b;
+{
+ register struct hent *h1, *h2;
+ register int r;
+
+ h1 = *(struct hent **)a;
+ h2 = *(struct hent **)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(s)
+ register char *s;
+{
+ int stat;
+ struct printer myprinter, *pp = &myprinter;
+
+ init_printer(&myprinter);
+ stat = getprintcap(s, 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));
+ }
+ acctfile = pp->acct_file;
+ 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/lpr/runqueue/extern.h b/usr.sbin/lpr/runqueue/extern.h
new file mode 100644
index 0000000..db732f5
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/extern.h
@@ -0,0 +1,48 @@
+/*
+ * 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
+ * $Id$
+ */
+
+#include <sys/cdefs.h>
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern char fromb[];
+
+struct printer;
+
+__BEGIN_DECLS
+void printjob __P((struct printer *pp));
+void startprinting __P((const char *printer));
+void recvjob __P((const char *printer));
+__END_DECLS
diff --git a/usr.sbin/lpr/runqueue/lpdchar.c b/usr.sbin/lpr/runqueue/lpdchar.c
new file mode 100644
index 0000000..f5c542a
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/lpdchar.c
@@ -0,0 +1,1071 @@
+/*
+ * 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 char sccsid[] = "@(#)lpdchar.c 8.1 (Berkeley) 6/6/93";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * 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/runqueue/modes.c b/usr.sbin/lpr/runqueue/modes.c
new file mode 100644
index 0000000..c85c669
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/modes.c
@@ -0,0 +1,234 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+
+struct modes {
+ char *name;
+ long set;
+ 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 },
+};
+
+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 },
+};
+
+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 },
+};
+
+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 },
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(str, ip)
+ 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/runqueue/printjob.c b/usr.sbin/lpr/runqueue/printjob.c
new file mode 100644
index 0000000..929d6a3
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/printjob.c
@@ -0,0 +1,1652 @@
+/*
+ * 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 */
+
+#ifndef lint
+/*
+static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95";
+*/
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+
+/*
+ * 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 /* absorb fork error */
+#define DOABORT 1 /* abort if dofork fails */
+
+/*
+ * 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 int child; /* id of any filters */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int ofilter; /* id of output filter, if any */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int pid; /* pid of lpd process */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+
+static char class[32]; /* classification field */
+static char fromhost[32]; /* 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 */
+static char tempfile[] = "errsXXXXXX"; /* file name for filter errors */
+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 __P((int));
+static void alarmhandler __P((int));
+static void banner __P((struct printer *pp, char *name1, char *name2));
+static int dofork __P((const struct printer *pp, int action));
+static int dropit __P((int));
+static void init __P((struct printer *pp));
+static void openpr __P((const struct printer *pp));
+static void opennet __P((const struct printer *pp));
+static void opentty __P((const struct printer *pp));
+static void openrem __P((const struct printer *pp));
+static int print __P((struct printer *pp, int format, char *file));
+static int printit __P((struct printer *pp, char *file));
+static void pstatus __P((const struct printer *, const char *, ...));
+static char response __P((const struct printer *pp));
+static void scan_out __P((struct printer *pp, int scfd, char *scsp,
+ int dlm));
+static char *scnline __P((int, char *, int));
+static int sendfile __P((struct printer *pp, int type, char *file,
+ int format));
+static int sendit __P((struct printer *pp, char *file));
+static void sendmail __P((struct printer *pp, char *user, int bombed));
+static void setty __P((const struct printer *pp));
+
+void msearch __P((char *, struct termios *));
+
+void
+printjob(pp)
+ struct printer *pp;
+{
+ struct stat stb;
+ register struct queue *q, **qp;
+ struct queue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ int errcnt, count = 0;
+
+ init(pp); /* set up capabilities */
+ (void) write(1, "", 1); /* ack that daemon is started */
+ (void) close(2); /* set up log file */
+ if (open(pp->log_file, O_WRONLY|O_APPEND, LOG_FILE_MODE) < 0) {
+ syslog(LOG_ERR, "%s: %m", pp->log_file);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ pid = getpid(); /* for use with lprm */
+ setpgrp(0, pid);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ (void) mktemp(tempfile);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(pp->spool_dir) < 0) {
+ syslog(LOG_ERR, "%s: %m", 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: %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: %s: %m", pp->printer, pp->lock_file);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", pid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: %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: %s: %m", pp->printer,
+ pp->lock_file);
+ }
+ 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->q_name, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->q_name);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: %s: %m", pp->printer,
+ pp->lock_file);
+ if (!pp->remote)
+ i = printit(pp, q->q_name);
+ else
+ i = sendit(pp, q->q_name);
+ /*
+ * 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: %s: %m",
+ pp->printer, pp->lock_file);
+ break;
+ }
+ }
+ if (i == OK) /* file ok and printed */
+ count++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", pp->printer);
+ if (ofilter > 0) {
+ kill(ofilter, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: %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->q_name);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->q_name);
+ q->q_name[0] = 'd';
+ (void) unlink(q->q_name);
+ 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 (count > 0) { /* Files 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(tempfile);
+ 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(pp, file)
+ struct printer *pp;
+ char *file;
+{
+ register int i;
+ char *cp;
+ int bombed = OK;
+
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: %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");
+
+ /*
+ * 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
+ * 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
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strncpy(fromhost, line+1, sizeof(fromhost) - 1);
+ fromhost[sizeof(fromhost) - 1] = '\0';
+ if (class[0] == '\0') {
+ strncpy(class, line+1, sizeof(class) - 1);
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'P':
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ logname[sizeof(logname) - 1] = '\0';
+ 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') {
+ strncpy(jobname, line+1, sizeof(jobname) - 1);
+ jobname[sizeof(jobname) - 1] = '\0';
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ else if (class[0] == '\0')
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ continue;
+
+ case 'T': /* header title for pr */
+ strncpy(title, line+1, sizeof(title) - 1);
+ title[sizeof(title) - 1] = '\0';
+ 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') {
+ strncpy(fonts[line[0]-'1'], line+1,
+ 50-1);
+ fonts[line[0]-'1'][50-1] = '\0';
+ }
+ continue;
+
+ case 'W': /* page width */
+ strncpy(width+2, line+1, sizeof(width) - 3);
+ width[2+sizeof(width) - 3] = '\0';
+ continue;
+
+ case 'I': /* indent amount */
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ indent[2+sizeof(indent) - 3] = '\0';
+ continue;
+
+ default: /* some file to print */
+ switch (i = print(pp, line[0], line+1)) {
+ 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(pp, format, file)
+ struct printer *pp;
+ int format;
+ char *file;
+{
+ register int n;
+ register char *prog;
+ int fi, fo;
+ FILE *fp;
+ char *av[15], buf[BUFSIZ];
+ int pid, p[2], stopped = 0;
+ union wait status;
+ struct stat stb;
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
+ 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);
+ 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')) {
+ pp->tof = 0;
+ while ((n = read(fi, buf, 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;
+ av[0] = "pr";
+ av[1] = width;
+ av[2] = length;
+ av[3] = "-h";
+ av[4] = *title ? title : " ";
+ av[5] = "-F";
+ av[6] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0); /* file is stdin */
+ dup2(p[1], 1); /* pipe is stdout */
+ closelog();
+ closeallfds(3);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ", "-F", 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 '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++] = fromhost;
+ av[n++] = pp->acct_file;
+ av[n] = 0;
+ fo = pfd;
+ if (ofilter > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((pid =
+ wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
+ ;
+ if (status.w_stopval != WSTOPPED) {
+ (void) close(fi);
+ syslog(LOG_WARNING,
+ "%s: output filter died "
+ "(retcode=%d termsig=%d)",
+ pp->printer, status.w_retcode,
+ status.w_termsig);
+ return(REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(fi, 0);
+ dup2(fo, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ closeallfds(3);
+ execv(prog, av);
+ syslog(LOG_ERR, "cannot execv %s", prog);
+ exit(2);
+ }
+ (void) close(fi);
+ if (child < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 && pid != child)
+ ;
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(ofilter, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ pp->tof = 0;
+
+ /* Copy filter output to "lf" logfile */
+ if ((fp = fopen(tempfile, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (!WIFEXITED(status)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ pp->printer, format, status.w_termsig);
+ return(ERROR);
+ }
+ switch (status.w_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, status.w_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(pp, file)
+ struct printer *pp;
+ char *file;
+{
+ register int i, err = OK;
+ char *cp, last[BUFSIZ];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(OK);
+ /*
+ * 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
+ */
+ 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') {
+ strcpy(fromhost, line+1);
+ if (class[0] == '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ } else if (line[0] == 'P') {
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ if (pp->restricted) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(pp, line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ strcpy(last, line);
+ while ((i = getline(cfp)) != 0)
+ if (strcmp(last, line))
+ break;
+ switch (sendfile(pp, '\3', last+1, *last)) {
+ 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') > 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(pp, type, file, format)
+ struct printer *pp;
+ int type;
+ char *file;
+ char format;
+{
+ register int f, i, amt;
+ struct stat stb;
+ char buf[BUFSIZ];
+ int sizerr, resp, closedpr;
+
+ if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
+ 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(f, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+
+ sizerr = 0;
+ closedpr = 0;
+ if (type == '\3') {
+ if (pp->filters[LPF_INPUT]) {
+ /*
+ * We're sending something with an ifilter, we have to
+ * run the ifilter and store the output as a
+ * temporary file (tfile)... the protocol requires us
+ * to send the file size
+ */
+ char *av[15];
+ int n;
+ int ifilter;
+ union wait status; /* XXX */
+
+ strcpy(tfile,TFILENAME);
+ if ((tfd = mkstemp(tfile)) == -1) {
+ syslog(LOG_ERR, "mkstemp: %m");
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(pp->filters[LPF_INPUT], '/')) == NULL)
+ av[0] = pp->filters[LPF_INPUT];
+ else
+ av[0]++;
+ if (format == 'l')
+ av[n=1] = "-c";
+ else
+ n = 0;
+ av[++n] = width;
+ av[++n] = length;
+ av[++n] = indent;
+ av[++n] = "-n";
+ av[++n] = logname;
+ av[++n] = "-h";
+ av[++n] = fromhost;
+ av[++n] = pp->acct_file;
+ av[++n] = 0;
+ if ((ifilter = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(f, 0);
+ dup2(tfd, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC,
+ TEMP_FILE_MODE);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ closeallfds(3);
+ execv(pp->filters[LPF_INPUT], av);
+ syslog(LOG_ERR, "cannot execv %s",
+ pp->filters[LPF_INPUT]);
+ exit(2);
+ }
+ (void) close(f);
+ if (ifilter < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 &&
+ pid != ifilter)
+ ;
+ switch (status.w_retcode) {
+ case 0:
+ break;
+ case 1:
+ unlink(tfile);
+ return(REPRINT);
+ case 2:
+ unlink(tfile);
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited"
+ " (retcode=%d)",
+ pp->printer, format, status.w_retcode);
+ unlink(tfile);
+ return(FILTERERR);
+ }
+ if (fstat(tfd, &stb) < 0) /* the size of tfile */
+ return(ERROR);
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ } else if (ofilter) {
+ /*
+ * We're sending something with an ofilter, we have to
+ * store the output as a temporary file (tfile)... the
+ * protocol requires us to send the file size
+ */
+ int i;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt) {
+ sizerr = 1;
+ break;
+ }
+ if (write(ofd, buf, amt) != amt) {
+ (void) close(f);
+ return(REPRINT);
+ }
+ }
+ close(ofd);
+ close(f);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ if (fstat(tfd, &stb) < 0) { /* the size of tfile */
+ openpr(pp);
+ return(ERROR);
+ }
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ closedpr = 1;
+ }
+ }
+
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response(pp)) < 0 || resp == '\1') {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr(pp);
+ }
+ return(REPRINT);
+ } 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);
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr(pp);
+ }
+ return(REPRINT);
+ }
+ }
+
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ }
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", pp->printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ if (closedpr)
+ openpr(pp);
+ return(ERROR);
+ }
+ if (write(pfd, "", 1) != 1 || response(pp)) {
+ if (closedpr)
+ openpr(pp);
+ return(REPRINT);
+ }
+ if (closedpr)
+ openpr(pp);
+ return(OK);
+}
+
+/*
+ * 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(pp)
+ 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(pp, name1, name2)
+ struct printer *pp;
+ char *name1, *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(key, p, c)
+ register int key;
+ register char *p;
+ int c;
+{
+ register scnwidth;
+
+ for (scnwidth = WIDTH; --scnwidth;) {
+ key <<= 1;
+ *p++ = key & 0200 ? c : BACKGND;
+ }
+ return (p);
+}
+
+#define TRC(q) (((q)-' ')&0177)
+
+static void
+scan_out(pp, scfd, scsp, dlm)
+ struct printer *pp;
+ int scfd, dlm;
+ char *scsp;
+{
+ register char *strp;
+ register 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(c)
+ 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(pp, user, bombed)
+ struct printer *pp;
+ char *user;
+ int bombed;
+{
+ register int i;
+ int p[2], s;
+ register char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(pp, DORETURN)) == 0) { /* child */
+ dup2(p[0], 0);
+ closelog();
+ closeallfds(3);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", 0);
+ _exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], 1);
+ printf("To: %s@%s\n", user, fromhost);
+ printf("Subject: %s printer job \"%s\"\n", pp->printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+
+ cp = "XXX compiler confusion"; /* XXX shut GCC up */
+ switch (bombed) {
+ case OK:
+ printf("\ncompleted successfully\n");
+ cp = "OK";
+ break;
+ default:
+ case FATALERR:
+ printf("\ncould not be printed\n");
+ cp = "FATALERR";
+ break;
+ case NOACCT:
+ printf("\ncould not be printed without an account on %s\n", host);
+ cp = "NOACCT";
+ break;
+ case FILTERERR:
+ if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
+ (fp = fopen(tempfile, "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);
+ cp = "FILTERERR";
+ break;
+ case ACCESS:
+ printf("\nwas not printed because it was not linked to the original file\n");
+ cp = "ACCESS";
+ }
+ fflush(stdout);
+ (void) close(1);
+ } else {
+ syslog(LOG_WARNING, "unable to send mail to %s: %m", user);
+ 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)",
+ user, *jobname ? jobname : "<unknown>", pp->printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(pp, action)
+ const struct printer *pp;
+ int action;
+{
+ register int i, pid;
+
+ for (i = 0; i < 20; i++) {
+ if ((pid = fork()) < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (pid == 0)
+ setuid(pp->daemon_user);
+ return(pid);
+ }
+ syslog(LOG_ERR, "can't fork");
+
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /*FALL THRU*/
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ kill(0, SIGINT);
+ if (ofilter > 0)
+ kill(ofilter, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (ofilter > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init(pp)
+ 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(printer)
+ 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(pp)
+ const struct printer *pp;
+{
+ int p[2];
+ char *cp;
+
+ if (pp->remote) {
+ openrem(pp);
+ } 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] && !ofilter) {
+ pipe(p);
+ if (pp->remote) {
+ strcpy(tfile, TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((ofilter = dofork(pp, DOABORT)) == 0) { /* child */
+ dup2(p[0], 0); /* pipe is std in */
+ /* tfile/printer is stdout */
+ dup2(pp->remote ? tfd : pfd, 1);
+ 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, 0);
+ syslog(LOG_ERR, "%s: %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;
+ ofilter = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(pp)
+ 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 > 65536) {
+ 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 %d", ep, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty(pp)
+ 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(pp)
+ 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(pp)
+ 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) {
+ 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);
+ }
+ }
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+pstatus(const struct printer *pp, const char *msg, ...)
+#else
+pstatus(pp, msg, va_alist)
+ const struct printer *pp;
+ char *msg;
+ va_dcl
+#endif
+{
+ int fd;
+ char *buf;
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+
+ umask(0);
+ fd = open(pp->status_file, O_WRONLY|O_CREAT|O_EXLOCK, STAT_FILE_MODE);
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: %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(signo)
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lptcontrol/Makefile b/usr.sbin/lptcontrol/Makefile
new file mode 100644
index 0000000..76c40e7
--- /dev/null
+++ b/usr.sbin/lptcontrol/Makefile
@@ -0,0 +1,5 @@
+PROG= lptcontrol
+CFLAGS+= -Wall
+MAN8= lptcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lptcontrol/lptcontrol.8 b/usr.sbin/lptcontrol/lptcontrol.8
new file mode 100644
index 0000000..9162a99
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.8
@@ -0,0 +1,86 @@
+.\"
+.\" 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.
+.\"
+.\"
+.\" $Id: lptcontrol.8,v 1.7 1998/03/23 08:23:49 charnier Exp $
+.Dd September 3, 1994
+.Dt LPTCONTROL 8
+.Os FreeBSD 2
+.Sh NAME
+.Nm \&lptcontrol
+.Nd a utility for manipulating the lpt printer driver
+.Sh SYNOPSIS
+.Nm \&lptcontrol
+.Cm -i
+|
+.Cm -p
+|
+.Cm -e
+.Op Fl d Ar device
+.Sh DESCRIPTION
+The
+.Nm
+command 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.
+.Sh OPTIONS
+.TP
+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 d Ar dev
+Set the mode of the printer device specified by
+.Em dev .
+The default value for
+.Em dev
+is
+.Em /dev/lpt0
+.El
+.Pp
+One of
+.Fl i
+,
+.Fl p
+or
+.Fl e
+must be specified.
+.Pp
+.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
+.Nm Lptcontrol
+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..d3856d0
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.c
@@ -0,0 +1,97 @@
+/*
+ * 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 withough 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[] =
+ "$Id: lptcontrol.c,v 1.7 1999/01/10 12:04:56 nsouch Exp $";
+#endif /* not lint */
+
+#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 <machine/lpt.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+
+#define PATH_LPCTL _PATH_DEV "lpctl"
+#define DEFAULT_DEVICE "/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()
+{
+ 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;
+ 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..d8bec3e
--- /dev/null
+++ b/usr.sbin/mailstats/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+
+PROG= mailstats
+MAN8= mailstats.8
+CFLAGS+=-I${.CURDIR}/../../contrib/sendmail/src
+
+.PATH: ${.CURDIR}/../../contrib/sendmail/mailstats
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/makemap/Makefile b/usr.sbin/makemap/Makefile
new file mode 100644
index 0000000..735e285
--- /dev/null
+++ b/usr.sbin/makemap/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.4 (Berkeley) 6/10/97
+
+PROG= makemap
+SRCS= makemap.c safefile.c snprintf.c
+MAN8= makemap.8
+CFLAGS+=-I${.CURDIR}/../../contrib/sendmail/src -DNEWDB -DNOT_SENDMAIL
+
+.PATH: ${.CURDIR}/../../contrib/sendmail/makemap \
+ ${.CURDIR}/../../contrib/sendmail/src
+
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/Makefile b/usr.sbin/manctl/Makefile
new file mode 100644
index 0000000..d812530
--- /dev/null
+++ b/usr.sbin/manctl/Makefile
@@ -0,0 +1,10 @@
+# Makefile
+# $Id$
+
+MAN8= manctl.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/manctl.sh ${DESTDIR}${BINDIR}/manctl
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/manctl.8 b/usr.sbin/manctl/manctl.8
new file mode 100644
index 0000000..03540c3
--- /dev/null
+++ b/usr.sbin/manctl/manctl.8
@@ -0,0 +1,55 @@
+.\" 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.
+.\"
+.\" $Id: manctl.8,v 1.6 1997/02/22 16:06:28 peter Exp $
+.Dd January 1, 1996
+.Dt MANCTL 8
+.Os FreeBSD
+.Sh NAME
+.Nm manctl
+.Nd manipulating manual pages
+.Sh SYNOPSIS
+.Nm manctl
+.Op Fl compress
+.Op Fl uncompress
+.Op Fl purge
+.Op Fl help
+.Ar path ...
+.Sh DESCRIPTION
+.Nm Manctl
+compress or uncompress manual pages in directory path.
+If possible, .so's are replaced with hard links.
+.Sh OPTIONS
+.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).
+.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..7f81369
--- /dev/null
+++ b/usr.sbin/manctl/manctl.sh
@@ -0,0 +1,376 @@
+#!/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.
+#
+# $Id: manctl.sh,v 1.9 1997/02/22 16:06:29 peter Exp $
+#
+# 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
+ gunzip -c $pname > /tmp/manager.$$
+ chmod u+w $pname
+ cp /tmp/manager.$$ $pname
+ chmod 444 $pname
+ mv $pname $fname.$sect
+ rm /tmp/manager.$$
+ 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
+ cat $fname | \
+ (cd .. ; soelim ) > /tmp/manager.$$
+ chmod u+w $fname
+ cp /tmp/manager.$$ $fname
+ chmod 444 $fname
+ 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
+ cat $pname | \
+ (cd .. ; soelim )| gzip -c -- > /tmp/manager.$$
+ chmod u+w $pname
+ cp /tmp/manager.$$ $pname
+ chmod 444 $pname
+ mv $pname $pname.gz
+ rm /tmp/manager.$$
+ 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/mixer/Makefile b/usr.sbin/mixer/Makefile
new file mode 100644
index 0000000..d11934f
--- /dev/null
+++ b/usr.sbin/mixer/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:06:33 peter Exp $
+
+PROG = mixer
+MAN8= mixer.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
new file mode 100644
index 0000000..30ea680
--- /dev/null
+++ b/usr.sbin/mixer/mixer.8
@@ -0,0 +1,137 @@
+.\" 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.
+.\"
+.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
+.Oo
+.Oo Ns
+.Ar dev Op Ar lvol Ns Op Ar :rvol
+.Li | recsrc |
+.Ar {^|+|-|=}rec rdev
+.Oc
+.Ar ... \&
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+command 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
+When
+.Nm
+is run without any arguments, all supported devices are displayed
+along with current values.
+If the
+.Ar dev
+argument is specified, the value for the
+.Ar dev
+device will be displayed.
+.Pp
+To modify the mixer value
+.Ar dev ,
+the optional left and right channel settings of
+.Ar lvol Ns Op Ar :rvol
+may be specified. The
+.Ar lvol
+and
+.Ar rvol
+arguments may be from 0 - 100.
+.Pp
+To change the recording device you use one of:
+.Bl -tag -width =rec -offset indent
+.It ^rec
+toggles
+.Ar rdev
+of possible recording devices
+.It +rec
+adds
+.Ar rdev
+to possible recording devices
+.It -rec
+removes
+.Ar rdev
+from possible recording devices
+.It =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.
+This will ONLY work if the
+.Fl f
+option is immediately after the
+.Nm
+command.
+.Sh FILES
+.Bl -tag -width /dev/mixer -compact
+.It Pa /dev/mixer
+The default mixer device.
+.Sh SEE ALSO
+.Xr cdcontrol 1 ,
+.Xr cdplay 1
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 2.0.5 .
+.Sh AUTHORS
+Original source by
+.An Craig Metz Aq cmetz@thor.tjhsst.edu
+and
+.An Hannu Savolainen .
+Mostly rewriten 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..4c993b3
--- /dev/null
+++ b/usr.sbin/mixer/mixer.c
@@ -0,0 +1,233 @@
+/*
+ * 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)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: mixer.c,v 1.8 1997/10/01 06:34:09 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef __FreeBSD__
+#include <machine/soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif
+
+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 [[dev [voll[:volr]] | recsrc | {^|+|-|=}rec recdev] ... ]\n");
+ printf(" devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask) {
+ if (n)
+ printf(", ");
+ printf(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(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, 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;
+ int l, r;
+
+ 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");
+
+ if (argc > 2 && strcmp(argv[1], "-f") == 0) {
+ name = strdup(argv[2]);
+ argc -= 2; argv += 2;
+ }
+
+ 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) {
+ 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;
+ }
+ printf("Mixer %-8s is currently set to %3d:%d\n", names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
+ }
+ return(0);
+ }
+
+ argc--; argv++;
+
+ while (argc) {
+ if (!strcmp("recsrc", *argv)) {
+ drecsrc = 1;
+ argc--; argv++;
+ continue;
+ } else if (argc > 1 && !strcmp("rec", *argv + 1)) {
+ if (**argv != '+' && **argv != '-' &&
+ **argv != '=' && **argv != '^') {
+ dusage = 1;
+ argc -= 1; argv += 1;
+ continue;
+ }
+ if ((dev = res_name(argv[1], recmask)) == -1) {
+ dusage = 1;
+ argc -= 1; argv += 1;
+ continue;
+ }
+ 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 ((dev = res_name(*argv, devmask)) == -1) {
+ dusage = 1;
+ argc--; argv++;
+ continue;
+ }
+
+ switch(argc > 1 ? sscanf(argv[1], "%d:%d", &l, &r) : 0) {
+ case 0:
+ if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
+ warn("MIXER_READ");
+ argc--; argv++;
+ continue;
+ }
+ 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 (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 to %d:%d.\n", names[dev],
+ l, r);
+
+ l |= r << 8;
+ if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
+ warn("WRITE_MIXER");
+
+ argc -= 2; argv += 2;
+ break;
+ }
+ }
+
+ 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);
+
+ if (dusage)
+ usage(devmask, recmask);
+
+ exit(0);
+}
diff --git a/usr.sbin/mount_portalfs/Makefile b/usr.sbin/mount_portalfs/Makefile
new file mode 100644
index 0000000..7734df4
--- /dev/null
+++ b/usr.sbin/mount_portalfs/Makefile
@@ -0,0 +1,13 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id: Makefile,v 1.8 1998/01/20 10:40:04 bde Exp $
+
+PROG= mount_portal
+SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_tcp.c pt_tcplisten.c
+MAN8= mount_portal.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.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..6eb0c58
--- /dev/null
+++ b/usr.sbin/mount_portalfs/activate.c
@@ -0,0 +1,214 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: activate.c,v 1.5 1998/07/06 07:19:23 charnier Exp $";
+#endif /* not lint */
+
+#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;
+ struct {
+ struct cmsghdr cmsg;
+ int fd;
+ } 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.fd = fd;
+ ctl.cmsg.cmsg_len = sizeof(ctl);
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ 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..373e62d
--- /dev/null
+++ b/usr.sbin/mount_portalfs/conf.c
@@ -0,0 +1,340 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: conf.c,v 1.6 1998/07/06 07:19:24 charnier Exp $";
+#endif /* not lint */
+
+#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..5a5750f
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,149 @@
+.\"
+.\" 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
+.\" $Id: mount_portal.8,v 1.3 1998/07/06 07:19:25 charnier Exp $
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_PORTAL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_portal
+.Nd mount the portal daemon
+.Sh SYNOPSIS
+.Nm mount_portal
+.Op Fl o Ar options
+.Ar /etc/portal.conf
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm
+command attaches an instance of the portal daemon
+to the global filesystem 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 filesystem 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 8
+with the returned file descriptor. Privilaged 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 filesystem 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..6de71ea
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,293 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mount_portal.c 8.6 (Berkeley) 4/26/95";
+#endif
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.13 1998/07/06 07:19:25 charnier Exp $";
+#endif /* not lint */
+
+#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 __P((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;
+ int mntflags = 0;
+ char tag[32];
+ struct vfsconf vfc;
+ 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];
+ mountpt = argv[optind+1];
+
+ /*
+ * 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;
+
+ error = getvfsbyname("portal", &vfc);
+ if (error && vfsisloadable("portal")) {
+ if (vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent();
+ error = getvfsbyname("portal", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc.vfc_name, 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_portal [-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..3203b00
--- /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
+ *
+ * $Id$
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* 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..3a4ddf0a
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id$
+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..f01e8a8
--- /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
+ *
+ * $Id: portald.h,v 1.3 1997/02/22 14:32:55 peter Exp $
+ */
+
+#include <sys/cdefs.h>
+#include <miscfs/portal/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) __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_file __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_tcp __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_tcplisten __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+
+/*
+ * Global functions
+ */
+extern void activate __P((qelem *q, int so));
+extern char **conf_match __P((qelem *q, char *key));
+extern void conf_read __P((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..11088ea
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,55 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pt_conf.c,v 1.4 1998/07/06 07:19:25 charnier Exp $";
+#endif /* not lint */
+
+#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..6ee326d
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,60 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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..32207f3
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,109 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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..954388e
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pt_tcp.c,v 1.7 1998/07/06 07:19:27 charnier Exp $";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <strings.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..43a20c5
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcplisten.c
@@ -0,0 +1,206 @@
+/*
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <strings.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/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 0000000..6a2097a
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,9 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $Id: Makefile,v 1.7 1997/03/11 12:43:42 peter Exp $
+
+PROG= mountd
+CFLAGS+=-DNFS -DMFS -DCD9660 -DMSDOSFS
+MAN5= exports.5 netgroup.5
+MAN8= mountd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
new file mode 100644
index 0000000..470c7d5
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,293 @@
+.\" 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
+.\" $Id: exports.5,v 1.6 1997/07/16 09:27:50 dfr Exp $
+.\"
+.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 exports
+.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 \\*(tNRFC\\*(sP 1094, 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
+filesystem for one or more hosts.
+A host may be specified only once for each local filesystem on the
+server and there may be only one default entry for each server
+filesystem that applies to all other hosts.
+The latter exports the filesystem to the ``world'' and should
+be used only when the filesystem contains public information.
+.Pp
+In a mount entry,
+the first field(s) specify the directory path(s) within a server filesystem
+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 filesystem
+followed by the
+.Fl alldirs
+flag;
+this form allows the host(s) to mount at any point within the filesystem,
+including regular files if the
+.Fl r
+option is used on mountd.
+The pathnames must not have any symbolic links in them and should not have
+any "." or ".." components.
+Mount points for a filesystem may appear on multiple lines each with
+different sets of hosts and export options.
+.Pp
+The second component of a line specifies how the filesystem is to be
+exported to the host set.
+The option flags specify whether the filesystem
+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 kerb
+option specifies that the Kerberos authentication server should be
+used to authenticate and map client credentials.
+This option requires that the kernel be built with the NFSKERB option.
+.Pp
+The
+.Fl ro
+option specifies that the filesystem 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 filesystem, 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 .
+.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 (
+.Tn WebNFS Ns ).
+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
+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 ).
+.Pp
+For example:
+.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 -kerb -network cis-net -mask cis-mask
+.Ed
+.Pp
+Given that
+.Sy /usr ,
+.Sy /u
+and
+.Sy /u2
+are
+local filesystem mount points, the above example specifies the following:
+.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 and mapping all uids to credentials for the principal
+that is authenticated by a Kerberos ticket.
+.Sh FILES
+.Bl -tag -width /etc/exports -compact
+.It Pa /etc/exports
+the default remote mount-point file
+.El
+.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
+filesystem 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..91404f9
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,147 @@
+.\" 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
+.\" $Id: mountd.8,v 1.13 1998/07/15 06:21:39 charnier Exp $
+.\"
+.Dd April 28, 1995
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm mountd
+.Op Fl 2dlnr
+.Op Ar exportsfile
+.Sh DESCRIPTION
+.Nm Mountd
+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 filesystems 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.nfs.nfs_privport sysctl flag, which
+controls if the kernel will accept NFS requests from reserved ports only.
+.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 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 modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM 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 filesystems
+.It Pa /var/run/mountd.pid
+the pid of the currently running mountd
+.It Pa /var/db/mountdtab
+the current list of remote mounted filesystems
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr exports 5 ,
+.Xr modload 8 ,
+.Xr nfsd 8 ,
+.Xr portmap 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..ab9fdc7
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2211 @@
+/*
+ * 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*/
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95";
+#endif
+static const char rcsid[] =
+ "$Id: mountd.c,v 1.33 1998/08/02 16:06:34 bde Exp $";
+#endif /*not lint*/
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/sysctl.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.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 <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
+
+/*
+ * 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
+#define DP_KERB 0x4
+
+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 {
+ u_int32_t nt_net;
+ u_int32_t nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct hostent *gt_hostent;
+ struct netmsk gt_net;
+#ifdef ISO
+ struct sockaddr_iso *gt_isoaddr;
+#endif
+};
+
+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_ISO 0x4
+#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 __P((struct dirlist **, char *, int));
+void add_dlist __P((struct dirlist **, struct dirlist *,
+ struct grouplist *, int));
+void add_mlist __P((char *, char *));
+int check_dirpath __P((char *));
+int check_options __P((struct dirlist *));
+int chk_host __P((struct dirlist *, u_int32_t, int *, int *));
+void del_mlist __P((char *, char *));
+struct dirlist *dirp_search __P((struct dirlist *, char *));
+int do_mount __P((struct exportlist *, struct grouplist *, int,
+ struct ucred *, char *, int, struct statfs *));
+int do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct ucred *));
+struct exportlist *ex_search __P((fsid_t *));
+struct exportlist *get_exp __P((void));
+void free_dir __P((struct dirlist *));
+void free_exp __P((struct exportlist *));
+void free_grp __P((struct grouplist *));
+void free_host __P((struct hostlist *));
+void get_exportlist __P((void));
+int get_host __P((char *, struct grouplist *, struct grouplist *));
+int get_num __P((char *));
+struct hostlist *get_ht __P((void));
+int get_line __P((void));
+void get_mountlist __P((void));
+int get_net __P((char *, struct netmsk *, int));
+void getexp_err __P((struct exportlist *, struct grouplist *));
+struct grouplist *get_grp __P((void));
+void hang_dirp __P((struct dirlist *, struct grouplist *,
+ struct exportlist *, int));
+void mntsrv __P((struct svc_req *, SVCXPRT *));
+void nextfield __P((char **, char **));
+void out_of_mem __P((void));
+void parsecred __P((char *, struct ucred *));
+int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
+int scan_tree __P((struct dirlist *, u_int32_t));
+void send_umntall __P((void));
+int umntall_each __P((caddr_t, struct sockaddr_in *));
+static void usage __P((void));
+int xdr_dir __P((XDR *, char *));
+int xdr_explist __P((XDR *, caddr_t));
+int xdr_fhs __P((XDR *, caddr_t));
+int xdr_mlist __P((XDR *, caddr_t));
+
+/* C library */
+int getnetgrent();
+void endnetgrent();
+void setnetgrent();
+
+#ifdef ISO
+struct iso_addr *iso_addr();
+#endif
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char exname[MAXPATHLEN];
+struct ucred def_anon = {
+ 1,
+ (uid_t) -2,
+ 1,
+ { (gid_t) -2 }
+};
+int force_v2 = 0;
+int resvport_only = 1;
+int dir_only = 1;
+int log = 0;
+int opt_flags;
+/* Bits for above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+#define OP_KERB 0x04
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ISO 0x20
+#define OP_ALLDIRS 0x40
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG __P((int, const char *, ...));
+#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;
+{
+ SVCXPRT *udptransp, *tcptransp;
+ int c, error, mib[3];
+ struct vfsconf vfc;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS support is not available in the running kernel");
+
+ while ((c = getopt(argc, argv, "2dlnr")) != -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':
+ log = 1;
+ 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, (void (*) __P((int))) get_exportlist);
+ signal(SIGTERM, (void (*) __P((int))) send_umntall);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+ if (!resvport_only) {
+ mib[0] = CTL_VFS;
+ mib[1] = vfc.vfc_typenum;
+ mib[2] = NFS_NFSPRIVPORT;
+ if (sysctl(mib, 3, NULL, NULL, &resvport_only,
+ sizeof(resvport_only)) != 0 && errno != ENOENT) {
+ syslog(LOG_ERR, "sysctl: %m");
+ exit(1);
+ }
+ }
+ if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
+ (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "can't create socket");
+ exit(1);
+ }
+ pmap_unset(RPCPROG_MNT, 1);
+ pmap_unset(RPCPROG_MNT, 3);
+ if (!force_v2)
+ if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "can't register mount");
+ exit(1);
+ }
+ if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "can't register mount");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "mountd died");
+ exit(1);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mountd [-2] [-d] [-l] [-n] [-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 hostent *hp;
+ struct in_addr saddrin;
+ u_int32_t 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 = transp->xp_raddr.sin_addr.s_addr;
+ saddrin = transp->xp_raddr.sin_addr;
+ sport = ntohs(transp->xp_raddr.sin_port);
+ hp = (struct hostent *)NULL;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)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",
+ inet_ntoa(saddrin));
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ syslog(LOG_NOTICE, "undecodable mount request from %s",
+ inet_ntoa(saddrin));
+ 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) == 0 ||
+ 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",
+ inet_ntoa(saddrin), 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, 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, xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
+ syslog(LOG_ERR, "can't send reply");
+ if (hp == NULL)
+ hp = gethostbyaddr((caddr_t)&saddr,
+ sizeof(saddr), AF_INET);
+ if (hp)
+ add_mlist(hp->h_name, dirpath);
+ else
+ add_mlist(inet_ntoa(saddrin),
+ dirpath);
+ if (debug)
+ warnx("mount successful");
+ if (log)
+ syslog(LOG_NOTICE,
+ "mount request succeeded from %s for %s",
+ inet_ntoa(saddrin), dirpath);
+ } else {
+ bad = EACCES;
+ syslog(LOG_NOTICE,
+ "mount request denied from %s for %s",
+ inet_ntoa(saddrin), dirpath);
+ }
+
+ if (bad && !svc_sendreply(transp, 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, xdr_mlist, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ else if (log)
+ syslog(LOG_NOTICE,
+ "dump request succeeded from %s",
+ inet_ntoa(saddrin));
+ return;
+ case RPCMNT_UMOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umount request from %s from unprivileged port",
+ inet_ntoa(saddrin));
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, dirpath)) {
+ syslog(LOG_NOTICE, "undecodable umount request from %s",
+ inet_ntoa(saddrin));
+ svcerr_decode(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, dirpath);
+ del_mlist(inet_ntoa(saddrin), dirpath);
+ if (log)
+ syslog(LOG_NOTICE,
+ "umount request succeeded from %s for %s",
+ inet_ntoa(saddrin), dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ syslog(LOG_NOTICE,
+ "umountall request from %s from unprivileged port",
+ inet_ntoa(saddrin));
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, (char *)NULL);
+ del_mlist(inet_ntoa(saddrin), (char *)NULL);
+ if (log)
+ syslog(LOG_NOTICE,
+ "umountall request succeeded from %s",
+ inet_ntoa(saddrin));
+ return;
+ case RPCMNT_EXPORT:
+ if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
+ syslog(LOG_ERR, "can't send reply");
+ if (log)
+ syslog(LOG_NOTICE,
+ "export request succeeded from %s",
+ inet_ntoa(saddrin));
+ 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;
+{
+ register 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);
+ if (fhrp->fhr_flag & DP_KERB)
+ auth = RPCAUTH_KERB4;
+ else
+ 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(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ 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))
+ goto errout;
+ if (ep->ex_defdir && putdef == 0 &&
+ put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+ &putdef))
+ 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)
+ struct dirlist *dp;
+ XDR *xdrsp;
+ struct dirlist *adp;
+ int *putdefp;
+{
+ 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))
+ 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 ((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_hostent->h_name;
+ 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))
+ return (1);
+ }
+ return (0);
+}
+
+#define LINESIZ 10240
+char line[LINESIZ];
+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 hostent *hpe;
+ struct ucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ /*
+ * 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
+ * file systems.
+ * XXX: Should know how to handle all local exportable file systems
+ * 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 mfs_args ma;
+ struct msdosfs_args da;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, "mfs") ||
+ !strcmp(fsp->f_fstypename, "ufs") ||
+ !strcmp(fsp->f_fstypename, "msdos") ||
+ !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)
+ syslog(LOG_ERR, "can't delete exports for %s",
+ 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_HOST;
+ if (debug)
+ warnx("adding a default entry");
+ /* add a default group and make the grp list NULL */
+ hpe = (struct hostent *)malloc(sizeof(struct hostent));
+ if (hpe == (struct hostent *)NULL)
+ out_of_mem();
+ hpe->h_name = strdup("Default");
+ hpe->h_addrtype = AF_INET;
+ hpe->h_length = sizeof (u_int32_t);
+ hpe->h_addr_list = (char **)NULL;
+ grp->gr_ptr.gt_hostent = hpe;
+
+ /*
+ * 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) {
+ 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;
+
+ 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;
+ if (flags & OP_KERB)
+ ep->ex_defdir->dp_flag |= DP_KERB;
+ } else while (grp) {
+ hp = get_ht();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ 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();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ 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;
+ if (flags & OP_KERB)
+ dp->dp_flag |= DP_KERB;
+ }
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirpath)
+ struct dirlist *dp;
+ char *dirpath;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirpath);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirpath));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirpath));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp, hostsetp)
+ struct dirlist *dp;
+ u_int32_t saddr;
+ int *defsetp;
+ int *hostsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ u_int32_t **addrp;
+
+ 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:
+ addrp = (u_int32_t **)
+ grp->gr_ptr.gt_hostent->h_addr_list;
+ while (*addrp) {
+ if (**addrp == saddr) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ addrp++;
+ }
+ break;
+ case GT_NET:
+ if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+ grp->gr_ptr.gt_net.nt_net) {
+ *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;
+ u_int32_t 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 ucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ 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 (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
+ *exflagsp |= MNT_EXKERB;
+ opt_flags |= OP_KERB;
+ } 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 (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);
+#ifdef ISO
+ } else if (cpoptarg && !strcmp(cpopt, "iso")) {
+ if (get_isoaddr(cpoptarg, grp)) {
+ syslog(LOG_ERR, "bad iso addr: %s", cpoptarg);
+ return (1);
+ }
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_ISO;
+#endif /* ISO */
+ } 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 hostent *hp, *nhp;
+ char **addrp, **naddrp;
+ struct hostent t_host;
+ int i;
+ u_int32_t saddr;
+ char *aptr[2];
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((hp = gethostbyname(cp)) == NULL) {
+ if (isdigit(*cp)) {
+ saddr = inet_addr(cp);
+ if (saddr == -1) {
+ syslog(LOG_ERR, "inet_addr failed for %s", cp);
+ return (1);
+ }
+ if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
+ AF_INET)) == NULL) {
+ hp = &t_host;
+ hp->h_name = cp;
+ hp->h_addrtype = AF_INET;
+ hp->h_length = sizeof (u_int32_t);
+ hp->h_addr_list = aptr;
+ aptr[0] = (char *)&saddr;
+ aptr[1] = (char *)NULL;
+ }
+ } else {
+ syslog(LOG_ERR, "gethostbyname failed for %s", cp);
+ return (1);
+ }
+ }
+ /*
+ * Sanity check: make sure we don't already have an entry
+ * for this host in the grouplist.
+ */
+ checkgrp = tgrp;
+ while (checkgrp != NULL) {
+ if (checkgrp->gr_type == GT_HOST &&
+ checkgrp->gr_ptr.gt_hostent != NULL &&
+ (!strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)
+ || *(u_int32_t *)checkgrp->gr_ptr.gt_hostent->h_addr ==
+ *(u_int32_t *)hp->h_addr)) {
+ grp->gr_type = GT_IGNORE;
+ return(0);
+ }
+ checkgrp = checkgrp->gr_next;
+ }
+
+ grp->gr_type = GT_HOST;
+ nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+ malloc(sizeof(struct hostent));
+ if (nhp == (struct hostent *)NULL)
+ out_of_mem();
+ memmove(nhp, hp, sizeof(struct hostent));
+ i = strlen(hp->h_name)+1;
+ nhp->h_name = (char *)malloc(i);
+ if (nhp->h_name == (char *)NULL)
+ out_of_mem();
+ memmove(nhp->h_name, hp->h_name, i);
+ addrp = hp->h_addr_list;
+ i = 1;
+ while (*addrp++)
+ i++;
+ naddrp = nhp->h_addr_list = (char **)malloc(i*sizeof(char *));
+ if (naddrp == (char **)NULL)
+ out_of_mem();
+ addrp = hp->h_addr_list;
+ while (*addrp) {
+ *naddrp = (char *)malloc(hp->h_length);
+ if (*naddrp == (char *)NULL)
+ out_of_mem();
+ memmove(*naddrp, *addrp, hp->h_length);
+ addrp++;
+ naddrp++;
+ }
+ *naddrp = (char *)NULL;
+ if (debug)
+ warnx("got host %s", hp->h_name);
+ 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);
+}
+
+#ifdef ISO
+/*
+ * Translate an iso address.
+ */
+get_isoaddr(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct iso_addr *isop;
+ struct sockaddr_iso *isoaddr;
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((isop = iso_addr(cp)) == NULL) {
+ syslog(LOG_ERR, "iso_addr failed, ignored");
+ return (1);
+ }
+ isoaddr = (struct sockaddr_iso *)malloc(sizeof (struct sockaddr_iso));
+ if (isoaddr == (struct sockaddr_iso *)NULL)
+ out_of_mem();
+ memset(isoaddr, 0, sizeof(struct sockaddr_iso));
+ memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
+ isoaddr->siso_len = sizeof(struct sockaddr_iso);
+ isoaddr->siso_family = AF_ISO;
+ grp->gr_type = GT_ISO;
+ grp->gr_ptr.gt_isoaddr = isoaddr;
+ return (0);
+}
+#endif /* ISO */
+
+/*
+ * 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 ucred *anoncrp;
+ char *dirp;
+ int dirplen;
+ struct statfs *fsb;
+{
+ char *cp = (char *)NULL;
+ u_int32_t **addrp;
+ int done;
+ char savedc = '\0';
+ struct sockaddr_in sin, imask;
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+#ifdef __NetBSD__
+ struct msdosfs_args da;
+#endif
+ } args;
+ u_int32_t net;
+
+ args.ua.fspec = 0;
+ args.ua.export.ex_flags = exflags;
+ args.ua.export.ex_anon = *anoncrp;
+ args.ua.export.ex_indexfile = ep->ex_indexfile;
+ memset(&sin, 0, sizeof(sin));
+ memset(&imask, 0, sizeof(imask));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ imask.sin_family = AF_INET;
+ imask.sin_len = sizeof(sin);
+ if (grp->gr_type == GT_HOST)
+ addrp = (u_int32_t **)grp->gr_ptr.gt_hostent->h_addr_list;
+ else
+ addrp = (u_int32_t **)NULL;
+ done = FALSE;
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (addrp) {
+ sin.sin_addr.s_addr = **addrp;
+ args.ua.export.ex_addrlen = sizeof(sin);
+ } else
+ args.ua.export.ex_addrlen = 0;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_mask)
+ imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
+ else {
+ net = ntohl(grp->gr_ptr.gt_net.nt_net);
+ if (IN_CLASSA(net))
+ imask.sin_addr.s_addr = inet_addr("255.0.0.0");
+ else if (IN_CLASSB(net))
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.0.0");
+ else
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.255.0");
+ grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
+ }
+ sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_addrlen = sizeof (sin);
+ args.ua.export.ex_mask = (struct sockaddr *)&imask;
+ args.ua.export.ex_masklen = sizeof (imask);
+ break;
+#ifdef ISO
+ case GT_ISO:
+ args.ua.export.ex_addr =
+ (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+ args.ua.export.ex_addrlen =
+ sizeof(struct sockaddr_iso);
+ args.ua.export.ex_masklen = 0;
+ break;
+#endif /* ISO */
+ 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 file systems 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 (errno == EPERM) {
+ syslog(LOG_ERR,
+ "can't change attributes for %s", dirp);
+ return (1);
+ }
+ if (opt_flags & OP_ALLDIRS) {
+ 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';
+ }
+ if (addrp) {
+ ++addrp;
+ if (*addrp == (u_int32_t *)NULL)
+ done = TRUE;
+ } else
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np;
+ long netaddr;
+ struct in_addr inetaddr, inetaddr2;
+ char *name;
+
+ if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
+ inetaddr = inet_makeaddr(netaddr, 0);
+ /*
+ * Due to arbitrary subnet masks, you don't know how many
+ * bits to shift the address to make it into a network,
+ * however you do know how to make a network address into
+ * a host with host == 0 and then compare them.
+ * (What a pest)
+ */
+ if (!maskflg) {
+ setnetent(0);
+ while ((np = getnetent())) {
+ inetaddr2 = inet_makeaddr(np->n_net, 0);
+ if (inetaddr2.s_addr == inetaddr.s_addr)
+ break;
+ }
+ endnetent();
+ }
+ } else if ((np = getnetbyname(cp)) != NULL) {
+ inetaddr = inet_makeaddr(np->n_net, 0);
+ } else
+ return (1);
+
+ if (maskflg)
+ net->nt_mask = inetaddr.s_addr;
+ else {
+ if (np)
+ name = np->n_name;
+ else
+ name = inet_ntoa(inetaddr);
+ net->nt_name = (char *)malloc(strlen(name) + 1);
+ if (net->nt_name == (char *)NULL)
+ out_of_mem();
+ strcpy(net->nt_name, name);
+ net->nt_net = inetaddr.s_addr;
+ }
+ return (0);
+}
+
+/*
+ * 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;
+ int len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
+ return (0);
+ len = strlen(p);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ *++cp = '\0';
+ if (len > 0) {
+ totlen += len;
+ if (totlen >= LINESIZ) {
+ syslog(LOG_ERR, "exports line too long");
+ exit(2);
+ }
+ p = cp;
+ }
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct ucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ int ngroups, groups[NGROUPS + 1];
+
+ /*
+ * Set up the unprivileged user.
+ */
+ cr->cr_ref = 1;
+ 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) {
+ 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(hostp, dirp)
+ char *hostp, *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);
+}
+
+/*
+ * This function is called via. SIGTERM when the system is going down.
+ * It sends a broadcast RPCMNT_UMNTALL.
+ */
+void
+send_umntall()
+{
+ (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
+ xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
+ exit(0);
+}
+
+int
+umntall_each(resultsp, raddr)
+ caddr_t resultsp;
+ struct sockaddr_in *raddr;
+{
+ return (1);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ char **addrp;
+
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_hostent->h_name) {
+ addrp = grp->gr_ptr.gt_hostent->h_addr_list;
+ while (addrp && *addrp)
+ free(*addrp++);
+ free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
+ free(grp->gr_ptr.gt_hostent->h_name);
+ }
+ free((caddr_t)grp->gr_ptr.gt_hostent);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+#ifdef ISO
+ else if (grp->gr_type == GT_ISO)
+ free((caddr_t)grp->gr_ptr.gt_isoaddr);
+#endif
+ 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) ||
+ (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
+ (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
+ syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -net");
+ return (1);
+ }
+ if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
+ syslog(LOG_ERR, "-net and -iso mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldir has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ * if no symbolic links are found.
+ */
+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);
+}
+
+/*
+ * Just translate an ascii string to an integer.
+ */
+int
+get_num(cp)
+ register char *cp;
+{
+ register int res = 0;
+
+ while (*cp) {
+ if (*cp < '0' || *cp > '9')
+ return (-1);
+ res = res * 10 + (*cp++ - '0');
+ }
+ return (res);
+}
diff --git a/usr.sbin/mountd/netgroup.5 b/usr.sbin/mountd/netgroup.5
new file mode 100644
index 0000000..83cc9c7
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,186 @@
+.\" 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
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm netgroup
+.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
+.Bx Free ,
+.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
+.Bx Free
+.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..1c45d5e
--- /dev/null
+++ b/usr.sbin/moused/Makefile
@@ -0,0 +1,8 @@
+PROG= moused
+SRCS= moused.c
+MAN8= 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..64cd108
--- /dev/null
+++ b/usr.sbin/moused/moused.8
@@ -0,0 +1,570 @@
+.\" 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.
+.\"
+.\" $Id: moused.8,v 1.17 1998/11/20 11:19:20 yokota Exp $
+.\"
+.Dd December 3, 1997
+.Dt MOUSED 8
+.Os FreeBSD
+.Sh NAME
+.Nm moused
+.Nd pass mouse data to the console driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl 3DPRcdfs
+.Op Fl I Ar file
+.Op Fl F Ar rate
+.Op Fl r Ar resolution
+.Op Fl S Ar baudrate
+.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
+.Fl p Ar port
+.Pp
+.Nm
+.Op Fl Pd
+.Fl p Ar port
+.Fl i Ar info
+.Sh DESCRIPTION
+The mouse daemon
+.Nm
+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
+.Pq 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 4 .
+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 reinitializes 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
+won't 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 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
+daemon 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
+command won't 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 c
+Some mice report middle button down events
+as if the left and right buttons are 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
+command 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
+command 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
+command to automatically select an appropriate protocol for the given
+mouse.
+If you entirely ommit this options in the command line,
+.Fl t Ar auto
+is assumed.
+Under normal circumstances,
+you need to use this option only if the
+.Nm
+command is not able to detect the protocol automatically
+.Pq see the Sx Configuring Mouse Daemon .
+.Pp
+Also 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
+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, 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.
+.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
+.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 the virtual buttons
+.Ar N
+and
+.Ar N+1
+down events respectively when negative and positive Z axis movement
+is detected. There doesn't 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.
+.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.
+.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 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
+command may be able to automatically determine the protocol type.
+Run the
+.Nm
+command 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
+.Pq see Sx EXAMPLE .
+.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
+command by the
+.Fl t
+option. You have to make a guess and try.
+There is rule of thumb:
+.Pp
+.Bl -tag -compact -width 1.X
+.It 1.
+The bus and InPort mice always use
+.Ar busmouse
+protocol regardless of the brand of the mouse.
+.It 2.
+The
+.Ar ps/2
+protocol should always be specified for the PS/2 mouse
+regardless of the brand of the mouse.
+.It 3.
+Most 2-button serial mice support the
+.Ar microsoft
+protocol.
+.It 4.
+3-button serial mice may work with the
+.Ar mousesystems
+protocol. If it doesn't, it may work with the
+.Ar microsoft
+protocol although
+the third (middle) button won't function.
+3-button serial mice may also work with the
+.Ar mouseman
+protocol under which the third button may function as expected.
+.It 5.
+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 6.
+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
+.Pq such as the X Window System
+to use
+.Xr sysmouse ,
+then the application program will always see mouse data from either mice.
+When the serial mouse is not attached, the corresponding mouse daemon
+won't 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 /var/run/moused.pid
+process id of the currently running
+.Nm
+daemon
+.It Pa /var/run/MouseRemote
+UNIX-domain stream socket for X10 MouseRemote events
+.El
+.Sh EXAMPLE
+.Pp
+.Dl moused -p /dev/cuaa0 -i type
+.Pp
+Let the
+.Nm
+command 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
+command 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 (roller) movement as the button 4 pressed
+and positive Z axis movement as the button 5 pressed.
+.Sh CAVEATS
+The
+.Nm
+command 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 pad 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
+.Sh STANDARD
+The
+.Nm
+command partially supports ``Plag 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
+The
+.Nm
+command 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
+command 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..ecb9053
--- /dev/null
+++ b/usr.sbin/moused/moused.c
@@ -0,0 +1,2434 @@
+/**
+ ** 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!
+ **
+ **/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: moused.c,v 1.25 1999/02/28 09:18:57 yokota Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <termios.h>
+#include <syslog.h>
+
+#include <machine/console.h>
+#include <machine/mouse.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#define MAX_CLICKTHRESHOLD 2000 /* 2 seconds */
+
+#define TRUE 1
+#define FALSE 0
+
+#define MOUSE_XAXIS (-1)
+#define MOUSE_YAXIS (-2)
+
+#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...) \
+ if (debug&&nodaemon) warnx(fmt, ##args)
+
+#define logerr(e, fmt, args...) { \
+ if (background) { \
+ syslog(LOG_DAEMON | LOG_ERR, fmt ": %m", ##args); \
+ exit(e); \
+ } else \
+ err(e, fmt, ##args); \
+}
+
+#define logerrx(e, fmt, args...) { \
+ if (background) { \
+ syslog(LOG_DAEMON | LOG_ERR, fmt, ##args); \
+ exit(e); \
+ } else \
+ errx(e, fmt, ##args); \
+}
+
+#define logwarn(fmt, args...) { \
+ if (background) \
+ syslog(LOG_DAEMON | LOG_WARNING, fmt ": %m", ##args); \
+ else \
+ warn(fmt, ##args); \
+}
+
+#define logwarnx(fmt, args...) { \
+ if (background) \
+ syslog(LOG_DAEMON | LOG_WARNING, fmt, ##args); \
+ else \
+ warnx(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 },
+#ifdef __i386__
+ { "usb", MOUSE_IF_USB },
+#endif /* __i386__ */
+ { 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",
+#if notyet
+ "mariqua",
+#endif
+ NULL
+};
+
+/* models */
+static symtab_t rmodels[] = {
+ { "NetScroll", MOUSE_MODEL_NETSCROLL },
+ { "NetMouse", MOUSE_MODEL_NET },
+ { "GlidePoint", MOUSE_MODEL_GLIDEPOINT },
+ { "ThinkingMouse", MOUSE_MODEL_THINK },
+ { "IntelliMouse", MOUSE_MODEL_INTELLI },
+ { "EasyScroll", MOUSE_MODEL_EASYSCROLL },
+ { "MouseMan+", MOUSE_MODEL_MOUSEMANPLUS },
+ { "Kidspad", MOUSE_MODEL_KIDSPAD },
+ { "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 },
+ /* Genius PnP Mouse */
+ { "KYE0001", MOUSE_PROTO_MS, MOUSE_MODEL_GENERIC },
+ /* 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 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 },
+
+ /* 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_???, MOUSE_MODEL_GENERIC },
+ /* Genius Mouse */
+ { "PNP0F07", MOUSE_PROTO_???, 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_???, 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_???, MOUSE_MODEL_GENERIC },
+#endif
+ /* Logitech bus */
+ { "PNP0F15", MOUSE_PROTO_BUS, MOUSE_MODEL_GENERIC },
+#if notyet
+ /* Logitech SWIFT */
+ { "PNP0F16", MOUSE_PROTO_???, 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_???, MOUSE_MODEL_GENERIC },
+ /* HP Omnibook */
+ { "PNP0F1B", MOUSE_PROTO_???, MOUSE_MODEL_GENERIC },
+ /* Compaq LTE TrackBall PS/2 */
+ { "PNP0F1C", MOUSE_PROTO_???, MOUSE_MODEL_GENERIC },
+ /* Compaq LTE TrackBall serial */
+ { "PNP0F1D", MOUSE_PROTO_???, MOUSE_MODEL_GENERIC },
+ /* MS Kidts Trackball */
+ { "PNP0F1E", MOUSE_PROTO_???, MOUSE_MODEL_GENERIC },
+#endif
+
+ { 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. */
+#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; /* 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 */
+ mousehw_t hw; /* mouse device hardware information */
+ mousemode_t mode; /* protocol information */
+} rodent = {
+ flags : 0,
+ portname : NULL,
+ rtype : MOUSE_PROTO_UNKNOWN,
+ level : -1,
+ baudrate : 1200,
+ rate : 0,
+ resolution : MOUSE_RES_UNKNOWN,
+ zmap: 0,
+ wmode: 0,
+ mfd : -1,
+ cfd : -1,
+ mremsfd : -1,
+ mremcfd : -1,
+ clickthreshold : 500, /* 0.5 sec */
+};
+
+/* button status */
+static struct {
+ int count; /* 0: up, 1: single click, 2: double click,... */
+ struct timeval tv; /* timestamp on the last `up' event */
+} buttonstate[MOUSE_MAXBUTTON];
+
+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 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_installmap(char *arg);
+static void r_map(mousestatus_t *act1, mousestatus_t *act2);
+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);
+
+void
+main(int argc, char *argv[])
+{
+ int c;
+ int i;
+
+ while((c = getopt(argc,argv,"3C:DF:I:PRS:cdfhi:l:m:p:r:st:w:z:")) != -1)
+ switch(c) {
+
+ case '3':
+ rodent.flags |= Emulate3Button;
+ 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 = MOUSE_XAXIS;
+ else if (strcmp(optarg, "y") == 0)
+ rodent.zmap = 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 = 1 << (i - 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();
+ }
+
+ /* the default port name */
+ switch(rodent.rtype) {
+
+ case MOUSE_PROTO_INPORT:
+ /* INPORT and BUS are the same... */
+ rodent.rtype = MOUSE_PROTO_BUS;
+ /* FALL THROUGH */
+ 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();
+ }
+
+ for (;;) {
+ if (setjmp(env) == 0) {
+ signal(SIGHUP, hup);
+ signal(SIGINT , cleanup);
+ signal(SIGQUIT, cleanup);
+ signal(SIGTERM, cleanup);
+ if ((rodent.mfd = open(rodent.portname, O_RDWR | O_NONBLOCK, 0))
+ == -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 void
+moused(void)
+{
+ struct mouse_info mouse;
+ mousestatus_t action; /* original mouse action */
+ mousestatus_t action2; /* mapped action */
+ fd_set fds;
+ u_char b;
+ FILE *fp;
+
+ if ((rodent.cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
+ logerr(1, "cannot open /dev/consolectl", 0);
+
+ if (!nodaemon && !background)
+ if (daemon(0, 0)) {
+ logerr(1, "failed to become a daemon", 0);
+ } else {
+ background = TRUE;
+ fp = fopen(pidfile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ fclose(fp);
+ }
+ }
+
+ /* clear mouse data */
+ bzero(&action, sizeof(action));
+ bzero(&action2, sizeof(action2));
+ bzero(&buttonstate, sizeof(buttonstate));
+ bzero(&mouse, sizeof(mouse));
+
+ /* choose which ioctl command to use */
+ mouse.operation = MOUSE_MOTION_EVENT;
+ extioctl = (ioctl(rodent.cfd, CONS_MOUSECTL, &mouse) == 0);
+
+ /* process mouse data */
+ 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);
+
+ if (select(FD_SETSIZE, &fds, NULL, NULL, NULL) <= 0)
+ logwarn("failed to read from mouse", 0);
+
+ /* 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 event */
+ read(rodent.mfd, &b, 1);
+ if (r_protocol(b, &action)) { /* 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;
+ mouse.u.data.y = action2.dy;
+ 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;
+ mouse.u.data.y = action2.dy;
+ mouse.u.data.z = action2.dz;
+ if (debug < 2)
+ ioctl(rodent.cfd, CONS_MOUSECTL, &mouse);
+ }
+
+ /*
+ * If the Z axis movement is mapped to a imaginary physical
+ * button, we need to cook up a corresponding button `up' event
+ * after sending a button `down' event.
+ */
+ if ((rodent.zmap > 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",
+ "usage: moused [-3DRcdfs] [-I file] [-F rate] [-r resolution] [-S baudrate]",
+ " [-C threshold] [-m N=M] [-w N] [-z N] [-t <mousetype>] -p <port>",
+ " moused [-d] -i <info> -p <port>");
+ exit(1);
+}
+
+/**
+ ** 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 */
+#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 an 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)
+{
+ 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_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]);
+ /* fall through */
+
+ 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;
+
+
+ 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,
+ };
+ static int pBufP = 0;
+ static unsigned char pBuf[8];
+
+ 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 preceeding 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 preceeding
+ * 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 */
+
+ /*
+ * 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);
+ 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 = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (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 = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act->dy = (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 = (char)(pBuf[1]) + (char)(pBuf[3]);
+ act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
+ 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_BUS: /* Bus */
+ case MOUSE_PROTO_INPORT: /* InPort */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_MSC_BUTTONS];
+ act->dx = (char)pBuf[1];
+ act->dy = - (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_INTELLI:
+ case MOUSE_MODEL_NET:
+ /* wheel data is in the fourth byte */
+ act->dz = (char)pBuf[3];
+ break;
+ case MOUSE_MODEL_MOUSEMANPLUS:
+ if ((pBuf[0] & ~MOUSE_PS2_BUTTONS) == 0xc8) {
+ /* the extended data packet encodes button and wheel events */
+ act->dx = act->dy = 0;
+ act->dz = (pBuf[1] & MOUSE_PS2PLUS_ZNEG)
+ ? (pBuf[2] & 0x0f) - 16 : (pBuf[2] & 0x0f);
+ act->button |= ((pBuf[2] & MOUSE_PS2PLUS_BUTTON4DOWN)
+ ? MOUSE_BUTTON4DOWN : 0);
+ } 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 button and wheel events */
+ act->button |= (pBuf[3] & MOUSE_PS2_BUTTON3DOWN)
+ ? MOUSE_BUTTON4DOWN : 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_GENERIC:
+ default:
+ break;
+ }
+ break;
+
+ case MOUSE_PROTO_SYSMOUSE: /* sysmouse */
+ act->button = butmapmsc[(~pBuf[0]) & MOUSE_SYS_STDBUTTONS];
+ act->dx = (char)(pBuf[1]) + (char)(pBuf[3]);
+ act->dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
+ if (rodent.level == 1) {
+ act->dz = ((char)(pBuf[5] << 1) + (char)(pBuf[6] << 1))/2;
+ 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);
+
+ if (rodent.flags & Emulate3Button) {
+ if (((act->flags & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
+ == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
+ && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
+ == (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
+ act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN);
+ act->button |= MOUSE_BUTTON2DOWN;
+ } else if ((act->obutton & MOUSE_BUTTON2DOWN)
+ && ((act->button & (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))
+ != (MOUSE_BUTTON1DOWN | MOUSE_BUTTON3DOWN))) {
+ act->button &= ~(MOUSE_BUTTON1DOWN | MOUSE_BUTTON2DOWN
+ | MOUSE_BUTTON3DOWN);
+ }
+ act->flags &= MOUSE_POSCHANGED;
+ act->flags |= act->obutton ^ act->button;
+ }
+
+ return act->flags;
+}
+
+/* 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);
+ }
+
+ 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) {
+ 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 | (rodent.zmap << 1));
+ if (act1->dz < 0)
+ pbuttons |= rodent.zmap;
+ else if (act1->dz > 0)
+ pbuttons |= (rodent.zmap << 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_click(mousestatus_t *act)
+{
+ struct mouse_info mouse;
+ struct timeval tv;
+ struct timeval tv1;
+ struct timeval tv2;
+ struct timezone tz;
+ int button;
+ int mask;
+ int i;
+
+ mask = act->flags & MOUSE_BUTTONS;
+ if (mask == 0)
+ return;
+
+ gettimeofday(&tv1, &tz);
+ 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);
+ 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",
+ buttonstate[i].tv.tv_sec, buttonstate[i].tv.tv_usec);
+ if (timercmp(&tv, &buttonstate[i].tv, >)) {
+ buttonstate[i].tv.tv_sec = 0;
+ buttonstate[i].tv.tv_usec = 0;
+ buttonstate[i].count = 1;
+ } else {
+ ++buttonstate[i].count;
+ }
+ mouse.u.event.value = buttonstate[i].count;
+ } else {
+ /* the button is up */
+ buttonstate[i].tv = tv1;
+ 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", 0);
+ 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", 0);
+ 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", 0);
+ return;
+ }
+ }
+ usleep(100000);
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ logwarn("unable to set status of mouse fd", 0);
+}
+
+/*
+ * 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.
+ */
+disconnect_idle:
+ 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 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 ;
+ static int x_idle = -1, y_idle = -1 ;
+
+ int deltat, 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..039e78c
--- /dev/null
+++ b/usr.sbin/mptable/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG = mptable
+MAN1 = mptable.1
+
+BINMODE = 550
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mptable/mptable.1 b/usr.sbin/mptable/mptable.1
new file mode 100644
index 0000000..c36231f
--- /dev/null
+++ b/usr.sbin/mptable/mptable.1
@@ -0,0 +1,65 @@
+.\" 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.
+.Dd April 28, 1997
+.Dt MPTABLE 1
+.Os
+.Sh NAME
+.Nm mptable
+.Nd display MP configuration table
+.Sh SYNOPSIS
+.Nm mptable
+.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 both for determining kernel config options and debugging
+an SMP kernel that will not boot. It can be run with a UniProcessor kernel.
+.Pp
+It must be run as root.
+.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..9c79aa2
--- /dev/null
+++ b/usr.sbin/mptable/mptable.c
@@ -0,0 +1,1130 @@
+/*
+ * 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[] =
+ "$Id: mptable.c,v 1.9 1997/09/25 06:47:33 charnier Exp $";
+#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 <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, "---" }
+};
+
+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 ];
+ void* 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 ];
+ void* oem_table_pointer;
+ u_short oem_table_size;
+ u_short entry_count;
+ void* 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_long cpuSignature;
+ u_long featureFlags;
+ u_long reserved1;
+ u_long 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;
+ void* 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;
+} 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( vm_offset_t* paddr, int* where );
+
+static void MPConfigDefault( int featureByte );
+
+static void MPFloatingPointer( vm_offset_t paddr, int where, mpfps_t* mpfps );
+static void MPConfigTableHeader( void* pap );
+
+static int readType( void );
+static void seekEntry( vm_offset_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 doOptionList( 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[] )
+{
+ vm_offset_t paddr;
+ int where;
+ mpfps_t mpfps;
+ int defaultConfig;
+
+ extern int optreset;
+ 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:")) != EOF) {
+ 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( "/dev/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 );
+
+ /* build "options" entries for the kernel config file */
+ doOptionList();
+
+ /* 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( vm_offset_t* paddr, int* where )
+{
+ /*
+ * c rewrite of apic_probe() by Jack F. Vogel
+ */
+
+ int x;
+ u_short segment;
+ vm_offset_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( (vm_offset_t)EBDA_POINTER );
+ readEntry( &segment, 2 );
+ if ( segment ) { /* search EBDA */
+ target = (vm_offset_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 / 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( (vm_offset_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 / 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 / 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 / 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 / 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 / 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 / sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 7;
+ *paddr = (x * sizeof( unsigned int )) + GROPE_AREA2;
+ return;
+ }
+ }
+ }
+
+ *where = 0;
+ *paddr = (vm_offset_t)0;
+}
+
+
+/*
+ *
+ */
+static void
+MPFloatingPointer( vm_offset_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( void* pap )
+{
+ vm_offset_t paddr;
+ mpcth_t cth;
+ int x;
+ int totalSize, t;
+ int count, c;
+ int type;
+
+ if ( pap == 0 ) {
+ printf( "MP Configuration Table Header MISSING!\n" );
+ exit( 1 );
+ }
+
+ /* convert physical address to virtual address */
+ paddr = (vm_offset_t)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;
+
+ /* process all the CPUs */
+ printf( "--\nProcessors:\tAPIC ID\tVersion\tState"
+ "\t\tFamily\tModel\tStep\tFlags\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 0 )
+ processorEntry();
+ totalSize -= basetableEntryTypes[ 0 ].length;
+ }
+
+ /* process all the busses */
+ printf( "--\nBus:\t\tBus ID\tType\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 1 )
+ busEntry();
+ totalSize -= basetableEntryTypes[ 1 ].length;
+ }
+
+ /* process all the apics */
+ printf( "--\nI/O APICs:\tAPIC ID\tVersion\tState\t\tAddress\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 2 )
+ ioApicEntry();
+ totalSize -= basetableEntryTypes[ 2 ].length;
+ }
+
+ /* process all the I/O Ints */
+ printf( "--\nI/O Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 3 )
+ intEntry();
+ totalSize -= basetableEntryTypes[ 3 ].length;
+ }
+
+ /* process all the Local Ints */
+ printf( "--\nLocal Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 4 )
+ intEntry();
+ totalSize -= basetableEntryTypes[ 4 ].length;
+ }
+
+
+#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 = (vm_offset_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( vm_offset_t addr )
+{
+ if ( lseek( pfd, (off_t)addr, SEEK_SET ) < 0 )
+ err( 1, "/dev/mem seek" );
+}
+
+
+/*
+ *
+ */
+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;
+}
+
+
+char* intTypes[] = {
+ "INT", "NMI", "SMI", "ExtINT"
+};
+
+char* polarityMode[] = {
+ "conforms", "active-hi", "reserved", "active-lo"
+};
+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 ].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%qx\n", entry.addressBase );
+ printf( " address range: 0x%qx\n", entry.addressLength );
+}
+
+
+static void
+bhdEntry( void )
+{
+ BhdEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[ entry.type ].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " bus info: 0x%02x", entry.busInfo );
+ printf( " parent bus ID: %d", entry.busParent );
+}
+
+
+static void
+cbasmEntry( void )
+{
+ CbasmEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[ entry.type ].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address modifier: %s\n", (entry.addressMod & 0x01) ?
+ "subtract" : "add" );
+ printf( " predefined range: 0x%08x", entry.predefinedRange );
+}
+
+
+/*
+ * do a dmesg output
+ */
+static void
+doDmesg( void )
+{
+ puts( SEP_LINE );
+
+ printf( "dmesg output:\n\n" );
+ fflush( stdout );
+ system( "dmesg" );
+}
+
+
+/*
+ * build "options" entries for the kernel config file
+ */
+static void
+doOptionList( void )
+{
+ puts( SEP_LINE );
+
+ printf( "# SMP kernel config file options:\n\n" );
+ printf( "\n# Required:\n" );
+ printf( "options SMP\t\t\t# Symmetric MultiProcessor Kernel\n" );
+ printf( "options APIC_IO\t\t\t# Symmetric (APIC) I/O\n" );
+
+ printf( "\n# Optional (built-in defaults will work in most cases):\n" );
+ printf( "#options NCPU=%d\t\t\t# number of CPUs\n", ncpu );
+ printf( "#options NBUS=%d\t\t\t# number of busses\n", nbus );
+ printf( "#options NAPIC=%d\t\t\t# number of IO APICs\n", napic );
+ printf( "#options NINTR=%d\t\t# number of INTs\n",
+ (nintr < 24) ? 24 : nintr );
+}
+
+
+/*
+ *
+ */
+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..862ba67
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+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..46ce8d7
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile.inc
@@ -0,0 +1,11 @@
+# $Id$
+
+CFLAGS+= -DRSRR
+
+.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..673973f
--- /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[] =
+ "$Id: callout.c,v 1.10 1998/01/16 07:17:41 charnier Exp $";
+#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..14e206e
--- /dev/null
+++ b/usr.sbin/mrouted/cfparse.y
@@ -0,0 +1,932 @@
+%{
+/*
+ * Configuration file parser for mrouted.
+ *
+ * Written by Bill Fenner, NRL, 1994
+ *
+ * $Id: cfparse.y,v 1.8 1998/06/09 05:01:27 imp Exp $
+ * 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, ...));
+static void warn __P((char *fmt, ...));
+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[200];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+fatal(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap);
+#endif
+ vsprintf(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
+ vsprintf(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..801a207
--- /dev/null
+++ b/usr.sbin/mrouted/common/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.3 1997/02/22 16:07:20 peter Exp $
+
+LIB= mrouted
+NOPROFILE= yes
+NOPIC= yes
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+SRCS= igmp.c inet.c kern.c
+NOMAN=
+
+# nothing to install
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
new file mode 100644
index 0000000..544199e
--- /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[] =
+ "$Id: config.c,v 1.12 1998/06/09 05:01:29 imp Exp $";
+#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;
+ short 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..00a8b5b
--- /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.
+ *
+ *
+ * $Id: defs.h,v 1.10 1997/02/22 16:06:51 peter Exp $
+ * 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 *, ...));
+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..a7aee84
--- /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.
+ *
+ *
+ * $Id: dvmrp.h,v 1.7 1997/02/22 16:06:52 peter Exp $
+ * 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..5feab13
--- /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[] =
+ "$Id: igmp.c,v 1.14 1998/01/16 07:17:41 charnier Exp $";
+#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..fb10161
--- /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[] =
+ "$Id: inet.c,v 1.9 1998/01/16 07:17:42 charnier Exp $";
+#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..af8d007
--- /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[] =
+ "$Id: kern.c,v 1.10 1998/01/16 07:17:42 charnier Exp $";
+#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..875ec97
--- /dev/null
+++ b/usr.sbin/mrouted/main.c
@@ -0,0 +1,1061 @@
+/*
+ * 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[] =
+ "$Id: main.c,v 1.14 1998/06/29 17:38:35 bde Exp $";
+#endif
+
+#include <err.h>
+#include "defs.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <fcntl.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));
+#else
+ srandom(gethostid());
+#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);
+
+ FD_ZERO(&readers);
+ FD_SET(igmp_socket, &readers);
+ nfds = igmp_socket + 1;
+ for (i = 0; i < nhandlers; i++) {
+ 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("/dev/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
+ vsprintf(&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);
+ sprintf(logmsg[logmsgno++], "%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..5815761
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone.8
@@ -0,0 +1,80 @@
+.Dd May 8, 1995
+.Dt MAP-MBONE 8
+.UC 5
+.Sh NAME
+.Nm map-mbone
+.Nd multicast connection mapper
+.Sh SYNOPSIS
+.Nm map-mbone
+.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
+.Nm Map-mbone
+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
+.Nm Map-mbone
+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
+.Nm Map-mbone
+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..862ad29
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.6 1997/12/17 15:11:42 bde Exp $
+
+PROG= map-mbone
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+SRCS= mapper.c
+MAN8= map-mbone.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
new file mode 100644
index 0000000..0a5f33c
--- /dev/null
+++ b/usr.sbin/mrouted/mapper.c
@@ -0,0 +1,1046 @@
+/* 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[] =
+ "$Id: mapper.c,v 1.13 1998/06/09 05:01:30 imp Exp $";
+#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;
+
+ 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..ec9d061
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.8
@@ -0,0 +1,76 @@
+.Dd May 8, 1995
+.Dt MRINFO 8
+.UC 5
+.Sh NAME
+.Nm mrinfo
+.Nd display configuration info from a multicast router
+.Sh SYNOPSIS
+.Nm mrinfo
+.Op Fl d Ar debug_level
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Ar multicast_router
+.Sh DESCRIPTION
+.Nm Mrinfo
+attempts to display the configuration information from the multicast router
+.Ar multicast_router .
+.Pp
+.Nm Mrinfo
+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
+.nf
+.Nm mrinfo 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]
+.fi
+.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 treashold (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
+.Nm Mrinfo
+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..ff8a7b8
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.c
@@ -0,0 +1,634 @@
+/*
+ * 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[] =
+ "$Id: mrinfo.c,v 1.15 1998/06/09 05:01:34 imp Exp $";
+/* 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;
+
+ 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..59d8c3f
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.9 1998/03/07 09:49:06 bde Exp $
+
+PROG= mrinfo
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+DPADD= ${LIBMROUTED}
+LDADD= ${LIBMROUTED}
+
+MAN8= mrinfo.8
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
new file mode 100644
index 0000000..b2f51c8
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.8
@@ -0,0 +1,583 @@
+.\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
+.\"$Id: mrouted.8,v 1.12 1999/01/20 07:55:51 fenner Exp $
+.Dd May 8, 1995
+.Dt MROUTED 8
+.UC 5
+.Sh NAME
+.Nm mrouted
+.Nd IP multicast routing daemon
+.Sh SYNOPSIS
+.Nm mrouted
+.Op Fl c Ar config_file
+.Op Fl d Op Ar debug_level
+.Op Fl p
+.Sh DESCRIPTION
+.Nm Mrouted
+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
+.Nm Mrouted
+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.
+.Nm Mrouted
+suffers from the well-known scaling problems of any distance-vector
+routing protocol, and does not (yet) support hierarchical multicast routing.
+.Pp
+.Nm Mrouted
+handles multicast routing only; there may or may not be unicast routing
+software running on the same machine as
+.Nm mrouted .
+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 demon. The
+.Fl debug-level
+argument is a comma-seperated 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 .
+.Sh CONFIGURATION
+.Nm Mrouted
+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 mrouted 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
+Sepcifies, 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. mrouted <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
+.Nm Mrouted
+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]
+.Nm Mrouted 's
+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.
+mrouted 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.
+
+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 mrouted '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.
+
+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 on point-to-point links and tunnels, and off on
+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.
+
+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.
+
+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
+.Nm Mrouted
+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.
+.sp
+.nf
+#
+# 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
+.fi
+.Sh SIGNALS
+.Nm Mrouted
+responds to the following signals:
+.Bl -tag -width indent
+.It HUP
+Restarts
+.Nm mrouted .
+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 EXAMPLE
+The routing tables look like this:
+.nf
+.ft C
+
+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*
+ .
+ .
+ .
+
+.fi
+.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
+.Nm Mrouted
+also maintains a copy of the kernel forwarding cache table. Entries
+are created and deleted by
+.Nm mrouted .
+.Pp
+The cache tables look like this:
+.nf
+.ft C
+
+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
+
+.fi
+.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..d016f48
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.conf
@@ -0,0 +1,44 @@
+# $Id: mrouted.conf,v 1.7 1997/02/22 16:07:00 peter Exp $
+# 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..1c45823
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.11 1999/01/20 07:55:57 fenner Exp $
+
+PROG= mrouted
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+YFLAGS=
+
+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
+MAN8= mrouted.8
+
+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..4cfaf30
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.8
@@ -0,0 +1,550 @@
+.\" 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.
+.\"
+.\" mtrace.8,v 5.2 1998/12/04 04:48:16 fenner Exp
+.\"
+.Dd May 8, 1995
+.Dt MTRACE 8
+.UC 6
+.Sh NAME
+.Nm mtrace
+.Nd print multicast path from a source to a receiver
+.Sh SYNOPSIS
+.Nm mtrace
+.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.
+.Nm Mtrace
+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 mtrace ,
+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
+tool 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 mtrace .
+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
+isntead 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.
+.Nm
+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
+.nf
+.ft C
+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.
+.fi
+.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.
+.ie t \{\
+.ft C
+. ie \w'i'<>\w'm' \{\" looks like this is not proper Courier font
+(If this example is not properly columned with a fixed-width font, get
+.B groff
+and try again.)
+. \}
+.\}
+.Pp
+.ft C
+.nf
+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
+.fi
+.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 \*(lqOutput pruned\*(rq error code
+indicates that traffic for group 224.2.143.24 would not be forwarded.
+.Pp
+.nf
+.ft C
+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
+.fi
+.Sh AUTHORS
+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..0018aec
--- /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[] =
+ "$Id: mtrace.c,v 1.15 1998/06/29 17:51:39 bde Exp $";
+#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)random() >> 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) {
+ 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) - 1, "%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';
+ /* Fall through */
+
+ 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';
+ /* Fall through */
+#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);
+#else
+ srandom(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..c292510
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.8 1998/03/07 09:49:12 bde Exp $
+
+PROG= mtrace
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+MAN8= mtrace.8
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h
new file mode 100644
index 0000000..0c30420
--- /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.
+ *
+ *
+ * $Id: pathnames.h,v 1.5 1997/02/22 16:07:01 peter Exp $
+ * 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..3586137
--- /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[] =
+ "$Id: prune.c,v 1.15 1998/01/16 07:17:44 charnier Exp $";
+#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 + (random() % (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..dbc232f
--- /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.
+ *
+ *
+ * $Id: prune.h,v 1.8 1997/02/22 16:07:04 peter Exp $
+ * 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..ff59a23
--- /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[] =
+ "$Id: route.c,v 1.10 1998/01/16 07:17:45 charnier Exp $";
+#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..8a0f281
--- /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.
+ *
+ *
+ * $Id: route.h,v 1.7 1997/02/22 16:07:07 peter Exp $
+ * 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..b8a0a82
--- /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[] =
+ "$Id: rsrr.c,v 1.6 1998/01/16 07:17:45 charnier Exp $";
+#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 %d",
+ 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..2f242bf
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+PROG= testrsrr
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+SRCS= testrsrr.c
+NOMAN=
+
+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..b99b593
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/testrsrr.c
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.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..98c11ce
--- /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[] =
+ "$Id: vif.c,v 1.13 1998/06/09 05:01:38 imp Exp $";
+#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..52062a7
--- /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.
+ *
+ *
+ * $Id: vif.h,v 1.8 1997/02/22 16:07:09 peter Exp $
+ * 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..75e2cf6
--- /dev/null
+++ b/usr.sbin/mtest/Makefile
@@ -0,0 +1,4 @@
+PROG= mtest
+MAN8= 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..bdb4aec
--- /dev/null
+++ b/usr.sbin/mtest/mtest.8
@@ -0,0 +1,52 @@
+.\" 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
+.Nm
+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..1e2e373
--- /dev/null
+++ b/usr.sbin/mtest/mtest.c
@@ -0,0 +1,224 @@
+/*
+ * 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 <err.h>
+#include <stdio.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..e61aa4a
--- /dev/null
+++ b/usr.sbin/mtree/Makefile
@@ -0,0 +1,15 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.8 1999/02/27 03:16:28 jkh Exp $
+
+PROG= mtree
+SRCS= compare.c crc.c create.c misc.c mtree.c spec.c verify.c
+MAN8= mtree.8
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+.if !defined(WORLD)
+DPADD+= ${LIBMD}
+LDADD+= -lmd
+CFLAGS+= -DMD5 -DSHA1 -DRMD160
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c
new file mode 100644
index 0000000..2b28f21
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,341 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: compare.c,v 1.10 1998/08/02 14:41:34 bde Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#ifdef MD5
+#include <md5.h>
+#endif
+#ifdef SHA1
+#include <sha.h>
+#endif
+#ifdef RMD160
+#include <ripemd.h>
+#endif
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int uflag;
+extern int lineno;
+
+static char *ftype __P((u_int));
+
+#define INDENTNAMELEN 8
+#define LABEL \
+ if (!label++) { \
+ len = printf("%s: ", RP(p)); \
+ if (len > INDENTNAMELEN) { \
+ tab = "\t"; \
+ (void)printf("\n"); \
+ } else { \
+ tab = ""; \
+ (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
+ } \
+ }
+
+int
+compare(name, s, p)
+ char *name;
+ register NODE *s;
+ register FTSENT *p;
+{
+ extern int uflag;
+ u_long len, val;
+ int fd, label;
+ char *cp, *tab = "";
+
+ 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 (%s, %s)\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ }
+ 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 (%lu, %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 (%lu, %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->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions (%#o, %#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 (%u, %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) {
+ LABEL;
+ (void)printf("%ssize (%qd, %qd)\n",
+ tab, s->st_size, 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 (%.24s, ",
+ tab, ctime(&s->st_mtimespec.tv_sec));
+ (void)printf("%.24s)\n",
+ ctime(&p->fts_statp->st_mtimespec.tv_sec));
+ 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 (%lu, %lu)\n",
+ tab, s->cksum, val);
+ }
+ 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("%sMD5File: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->md5digest)) {
+ LABEL;
+ printf("%sMD5 (%s, %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("%sSHA1_File: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->sha1digest)) {
+ LABEL;
+ printf("%sSHA-1 (%s, %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_File: %s: %s\n", tab,
+ p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->rmd160digest)) {
+ LABEL;
+ printf("%sRIPEMD160 (%s, %s)\n", tab, s->rmd160digest,
+ new_digest);
+ tab = "\t";
+ }
+ }
+#endif /* RMD160 */
+
+ if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
+ LABEL;
+ (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
+ }
+ return (label);
+}
+
+char *
+inotype(type)
+ 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 */
+}
+
+static char *
+ftype(type)
+ 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(name)
+ char *name;
+{
+ static char lbuf[MAXPATHLEN];
+ register int len;
+
+ if ((len = readlink(name, lbuf, sizeof(lbuf) - 1)) == -1)
+ err(1, "line %d: %s", lineno, name);
+ lbuf[len] = '\0';
+ return (lbuf);
+}
diff --git a/usr.sbin/mtree/create.c b/usr.sbin/mtree/create.c
new file mode 100644
index 0000000..e591001
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,397 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: create.c,v 1.13 1999/01/18 06:58:25 jkoshy Exp $";
+#endif /* not lint */
+
+#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 <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <vis.h>
+#include "mtree.h"
+#include "extern.h"
+
+#define INDENTNAMELEN 15
+#define MAXLINELEN 80
+
+extern long int crc_total;
+extern int ftsoptions;
+extern int dflag, iflag, nflag, sflag;
+extern u_int keys;
+extern char fullpath[MAXPATHLEN];
+extern int lineno;
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+
+static int dsort __P((const FTSENT **, const FTSENT **));
+static void output __P((int, int *, const char *, ...));
+static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *));
+static void statf __P((int, FTSENT *));
+
+void
+cwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ time_t clock;
+ char *argv[2], host[MAXHOSTNAMELEN];
+ int indent = 0;
+
+ (void)time(&clock);
+ (void)gethostname(host, sizeof(host));
+ (void)printf(
+ "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s",
+ getlogin(), host, fullpath, ctime(&clock));
+
+ argv[0] = ".";
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+ err(1, "line %d: fts_open", lineno);
+ while ((p = fts_read(t))) {
+ if (iflag)
+ indent = p->fts_level * 4;
+ 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);
+ 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, crc_total);
+}
+
+static void
+statf(indent, p)
+ int indent;
+ FTSENT *p;
+{
+ struct group *gr;
+ struct passwd *pw;
+ u_long len, val;
+ int fd, offset;
+ 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);
+
+ 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) {
+ if ((pw = getpwuid(p->fts_statp->st_uid)) != NULL) {
+ output(indent, &offset, "uname=%s", pw->pw_name);
+ } else {
+ errx(1,
+ "line %d: could not get uname for uid=%u",
+ lineno, 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) {
+ if ((gr = getgrgid(p->fts_statp->st_gid)) != NULL) {
+ output(indent, &offset, "gname=%s", gr->gr_name);
+ } else {
+ errx(1,
+ "line %d: could not get gname for gid=%u",
+ lineno, 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=%qd", p->fts_statp->st_size);
+ if (keys & F_TIME)
+ output(indent, &offset, "time=%ld.%ld",
+ 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, "line %d: %s", lineno, p->fts_accpath);
+ (void)close(fd);
+ output(indent, &offset, "cksum=%lu", 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, "line %d: %s", lineno, p->fts_accpath);
+ } else {
+ 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, "line %d: %s", lineno, p->fts_accpath);
+ } else {
+ 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, "line %d: %s", lineno, p->fts_accpath);
+ } else {
+ 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));
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+
+static int
+statd(t, parent, puid, pgid, pmode)
+ FTS *t;
+ FTSENT *parent;
+ uid_t *puid;
+ gid_t *pgid;
+ mode_t *pmode;
+{
+ register FTSENT *p;
+ register gid_t sgid;
+ register uid_t suid;
+ register mode_t smode;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid = *pgid;
+ uid_t saveuid = *puid;
+ mode_t savemode = *pmode;
+ u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE];
+ static int first = 1;
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ err(1, "line %d: %s", lineno, RP(parent));
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+
+ maxuid = maxgid = maxmode = 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];
+ }
+ }
+ }
+ /*
+ * 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)) || (first)) {
+ first = 0;
+ if (dflag)
+ (void)printf("/set type=dir");
+ else
+ (void)printf("/set type=file");
+ if (keys & F_UNAME)
+ if ((pw = getpwuid(saveuid)) != NULL)
+ (void)printf(" uname=%s", pw->pw_name);
+ else
+ errx(1,
+ "line %d: could not get uname for uid=%u",
+ lineno, saveuid);
+ if (keys & F_UID)
+ (void)printf(" uid=%lu", (u_long)saveuid);
+ if (keys & F_GNAME)
+ if ((gr = getgrgid(savegid)) != NULL)
+ (void)printf(" gname=%s", gr->gr_name);
+ else
+ errx(1,
+ "line %d: could not get gname for gid=%u",
+ lineno, 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");
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ }
+ return (0);
+}
+
+static int
+dsort(a, b)
+ const FTSENT **a, **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));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+output(int indent, int *offset, const char *fmt, ...)
+#else
+output(indent, offset, fmt, va_alist)
+ int indent;
+ int *offset;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (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/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644
index 0000000..f3b6230
--- /dev/null
+++ b/usr.sbin/mtree/extern.h
@@ -0,0 +1,43 @@
+/*-
+ * 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.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+int compare __P((char *, NODE *, FTSENT *));
+int crc __P((int, u_long *, u_long *));
+void cwalk __P((void));
+char *inotype __P((u_int));
+u_int parsekey __P((char *, int *));
+char *rlink __P((char *));
+NODE *spec __P((void));
+int verify __P((void));
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644
index 0000000..339804a
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,110 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: misc.c,v 1.5 1998/06/05 14:43:40 peter Exp $";
+#endif /*not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int lineno;
+
+typedef struct _key {
+ 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},
+ {"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},
+};
+
+u_int
+parsekey(name, needvaluep)
+ char *name;
+ int *needvaluep;
+{
+ KEY *k, tmp;
+ int keycompare __P((const void *, const void *));
+
+ 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(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((KEY *)a)->name, ((KEY *)b)->name));
+}
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..51948f3
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,311 @@
+.\" 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
+.\" $Id: mtree.8,v 1.13 1998/06/10 06:45:08 peter Exp $
+.\"
+.Dd February 26, 1999
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm mtree
+.Op Fl cdeinrUux
+.Op Fl f Ar spec
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl p Ar path
+.Op Fl s Ar seed
+.Sh DESCRIPTION
+The utility
+.Nm mtree
+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 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 f Ar file
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.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 effect either the /set statements or the comment before each
+directory.
+It does however effect the comment before the close of each directory.
+.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 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 p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.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 U
+Modify the owner, group, and permissions of existing files to match
+the specification and create any missing directories.
+User, group, and permissions must all be specified for missing directories
+to be created.
+Exit with a status of 0 on success, 1 if any error occurred,
+a mismatch is not considered an error if it was corrected.
+.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 x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e. strings that
+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 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:
+.sp
+.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 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 Nm \&..
+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
+.Tn FreeBSD
+distribution.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh SEE ALSO
+.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 .
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644
index 0000000..76b945b
--- /dev/null
+++ b/usr.sbin/mtree/mtree.c
@@ -0,0 +1,163 @@
+/*-
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mtree.c,v 1.6 1997/10/01 06:30:02 charnier Exp $";
+#endif /* not lint */
+
+#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"
+
+extern long int crc_total;
+
+int ftsoptions = FTS_LOGICAL;
+int cflag, dflag, eflag, iflag, nflag, rflag, sflag, uflag, Uflag;
+u_int keys;
+char fullpath[MAXPATHLEN];
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *dir, *p;
+ int status;
+
+ dir = NULL;
+ keys = KEYDEFAULT;
+ while ((ch = getopt(argc, argv, "cdef:iK:k:np:rs:Uux")) != -1)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (!(freopen(optarg, "r", stdin)))
+ err(1, "%s", optarg);
+ 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 'n':
+ nflag = 1;
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~strtol(optarg, &p, 0);
+ if (*p)
+ errx(1, "illegal seed value -- %s", optarg);
+ case 'U':
+ Uflag = 1;
+ uflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ 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);
+ }
+ status = verify();
+ if (Uflag & (status == MISMATCHEXIT))
+ status = 0;
+ exit(status);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mtree [-cdeinrUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644
index 0000000..7ffb798
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,96 @@
+/*-
+ * 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.
+ *
+ * @(#)mtree.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID)
+
+#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 */
+ 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 */
+ 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..bf2df5a
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,317 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: spec.c,v 1.9 1999/01/12 02:58:23 jkoshy Exp $";
+#endif /* not lint */
+
+#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 __P((char *, NODE *));
+static void unset __P((char *, NODE *));
+
+NODE *
+spec()
+{
+ register NODE *centry, *last;
+ register 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), stdin);
+ ++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) {
+ warnx("filename %s is ill-encoded and literally used",
+ p);
+ strcpy(centry->name, 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(t, ip)
+ char *t;
+ register NODE *ip;
+{
+ register int type;
+ register 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_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:
+ if ((ip->slink = strdup(val)) == NULL)
+ errx(1, "strdup");
+ 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(t, ip)
+ char *t;
+ register NODE *ip;
+{
+ register char *p;
+
+ while ((p = strtok(t, "\n\t ")))
+ ip->flags &= ~parsekey(p, NULL);
+}
diff --git a/usr.sbin/mtree/verify.c b/usr.sbin/mtree/verify.c
new file mode 100644
index 0000000..a20fc0b
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,210 @@
+/*-
+ * 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
+#if 0
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: verify.c,v 1.6 1998/06/05 14:43:42 peter Exp $";
+#endif /* not lint */
+
+#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"
+
+extern long int crc_total;
+extern int ftsoptions;
+extern int dflag, eflag, rflag, sflag, uflag;
+extern char fullpath[MAXPATHLEN];
+extern int lineno;
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void miss __P((NODE *, char *));
+static int vwalk __P((void));
+
+int
+verify()
+{
+ int rval;
+
+ root = spec();
+ rval = vwalk();
+ miss(root, path);
+ return (rval);
+}
+
+static int
+vwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ register NODE *ep, *level;
+ int specdepth, rval;
+ char *argv[2];
+
+ argv[0] = ".";
+ 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))) {
+ switch(p->fts_info) {
+ case FTS_D:
+ 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("extra: %s", 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, crc_total);
+ return (rval);
+}
+
+static void
+miss(p, tail)
+ register NODE *p;
+ register char *tail;
+{
+ register int create;
+ register char *tp;
+
+ 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))
+ (void)printf("missing: %s", path);
+ if (p->type != F_DIR) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (!(p->flags & F_VISIT) && uflag)
+ if (!(p->flags & (F_UID | F_UNAME)))
+ (void)printf(" (directory not created: user not specified)");
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ (void)printf(" (directory not created: group not specified)");
+ 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));
+ continue;
+ }
+ if (chmod(path, p->st_mode))
+ (void)printf("%s: permissions 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..c05b422
--- /dev/null
+++ b/usr.sbin/named.reload/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:08:06 peter Exp $
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+MAN8= named.reload.8
+CLEANFILES+= named.reload
+
+all: named.reload
+
+realinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ named.reload ${DESTDIR}${BINDIR}
+
+named.reload: named.reload.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${.CURDIR}/named.reload.sh > named.reload
+
+.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..b838ea0
--- /dev/null
+++ b/usr.sbin/named.reload/named.reload.8
@@ -0,0 +1,69 @@
+.\" ++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
+.\"
+.TH @INDOT_U@NAMED.RELOAD @SYS_OPS_EXT_U@ "June 26, 1993"
+.UC 5
+.SH NAME
+@INDOT@named.reload \- cause the name server to synchronize its database
+.SH DESCRIPTION
+This command sends a \s-1SIGHUP\s+1 to the running name server. This
+signal is documented in
+.IR named (@SYS_OPS_EXT@).
+.SH BUGS
+Does not check to see if the name server is actually running, and could
+use a stale PID cache file which may result in the death of an unrelated
+process.
+.SH SEE ALSO
+@INDOT@named(@SYS_OPS_EXT@), @INDOT@named.restart(@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..0b6495a
--- /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
+# $Id: named.reload.sh,v 8.1 1994/12/15 06:24:14 vixie Exp $
+#
+
+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..43d92ab
--- /dev/null
+++ b/usr.sbin/named.restart/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:08:11 peter Exp $
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+MAN8= named.restart.8
+CLEANFILES+= named.restart
+
+all: named.restart
+
+realinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ named.restart ${DESTDIR}${BINDIR}
+
+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..034bebd
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.8
@@ -0,0 +1,73 @@
+.\" ++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
+.\"
+.TH @INDOT_U@NAMED.RESTART @SYS_OPS_EXT_U@ "June 26, 1993"
+.UC 5
+.SH NAME
+@INDOT@named.restart \- stop and restart the name server
+.SH DESCRIPTION
+This command sends a \s-1SIGKILL\s+1 to the running name server and then
+starts a new one.
+.SH BUGS
+Does not check to see if the name server is actually running, and could
+use a stale PID cache file which may result in the death of an unrelated
+process.
+.PP
+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
+@INDOT@named(@SYS_OPS_EXT@), @INDOT@named.reload(@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..4d073e6
--- /dev/null
+++ b/usr.sbin/named.restart/named.restart.sh
@@ -0,0 +1,7 @@
+#!/bin/sh -
+#
+# from named.restart 5.4 (Berkeley) 6/27/89
+# $Id: named.restart.sh,v 8.1 1994/12/15 06:24:14 vixie Exp $
+#
+
+exec %DESTSBIN%/%INDOT%ndc restart
diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile
new file mode 100644
index 0000000..fcb18d8
--- /dev/null
+++ b/usr.sbin/named/Makefile
@@ -0,0 +1,24 @@
+# $Id: Makefile,v 1.20 1998/06/04 07:25:52 bde Exp $
+
+USE_LIBBIND= true
+.include "${.CURDIR}/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/named
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= named
+SRCS= tmp_version.c pathnames.h \
+ db_dump.c db_load.c db_lookup.c db_save.c db_update.c \
+ db_glue.c \
+ ns_parser.y ns_lexer.c ns_parseutil.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
+MAN8= named.8
+
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${BIND_DIR}/bin/named/named-bootconf.pl \
+ ${DESTDIR}${DESTSBIN}/named-bootconf
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named/Makefile.inc b/usr.sbin/named/Makefile.inc
new file mode 100644
index 0000000..16a221f
--- /dev/null
+++ b/usr.sbin/named/Makefile.inc
@@ -0,0 +1,56 @@
+# From: Id: Makefile.inc,v 8.4 1996/03/03 17:42:43 vixie Exp
+# $Id: Makefile.inc,v 1.8 1998/06/11 09:16:28 peter Exp $
+
+.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
+
+# 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${DESTDIR}/usr/include -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_TIME=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: 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..26ede9e
--- /dev/null
+++ b/usr.sbin/named/Makefile.maninc
@@ -0,0 +1,57 @@
+# From: Id: Makefile.maninc,v 8.1 1994/12/15 06:23:43 vixie Exp
+# $Id: Makefile.maninc,v 1.3 1997/02/22 16:07:59 peter Exp $
+
+# (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}"|tr "[a-z]" "[A-Z]"`; \
+ export INDOT_U; \
+ XFER_INDOT_U=`echo "${XFER_INDOT}"|tr "[a-z]" "[A-Z]"`; \
+ export XFER_INDOT_U; \
+ CMD_EXT_U=`echo "${CMD_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export CMD_EXT_U; \
+ SYS_OPS_EXT_U=`echo "${SYS_OPS_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export SYS_OPS_EXT_U; \
+ LIB_NETWORK_EXT_U=`echo "${LIB_NETWORK_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export LIB_NETWORK_EXT_U; \
+ FORMAT_EXT_U=`echo "${FORMAT_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export FORMAT_EXT_U; \
+ DESC_EXT_U=`echo "${DESC_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export DESC_EXT_U; \
+ SYSCALL_EXT_U=`echo "${SYSCALL_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export SYSCALL_EXT_U; \
+ BSD_SYSCALL_EXT_U=`echo "${BSD_SYSCALL_EXT}"|tr "[a-z]" "[A-Z]"`; \
+ export BSD_SYSCALL_EXT_U; \
+ LIB_C_EXT_U=`echo "${LIB_C_EXT}"|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"
+
+MANFILTER= ${EXT_SED_CMD}
diff --git a/usr.sbin/ndc/Makefile b/usr.sbin/ndc/Makefile
new file mode 100644
index 0000000..a68671e
--- /dev/null
+++ b/usr.sbin/ndc/Makefile
@@ -0,0 +1,22 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:08:18 peter Exp $
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/ndc
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= ndc
+STRIP=
+MAN8= ndc.8
+
+CLEANFILES+= ndc
+
+all: ndc
+
+ndc: ndc.sh ndcedit.awk ${.CURDIR}/../../usr.sbin/named/Makefile.inc
+ sed -e "s|%PIDFILE%|${PIDDIR}/named.pid|" \
+ -e "s|%NAMED%|named|" \
+ -e "s|%PS%|${PS}|" \
+ < ${BIND_DIR}/bin/ndc/ndc.sh | awk -f ${.CURDIR}/ndcedit.awk > ndc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndc/ndcedit.awk b/usr.sbin/ndc/ndcedit.awk
new file mode 100644
index 0000000..eb375b0
--- /dev/null
+++ b/usr.sbin/ndc/ndcedit.awk
@@ -0,0 +1,41 @@
+# $Id: ndcedit.awk,v 1.8 1998/07/31 08:47:16 phk Exp $
+NR == 3 {
+ print "#"
+ print "# This file is generated automatically, do not edit it here!"
+ print "# Please change src/usr.sbin/ndc/ndcedit.awk instead"
+ print "#"
+ print ""
+
+ print "# If there is a global system configuration file, suck it in."
+ print "if [ -f /etc/defaults/rc.conf ]; then"
+ print "\t. /etc/defaults/rc.conf"
+ print "elif [ -f /etc/rc.conf ]; then"
+ print "\t. /etc/rc.conf"
+ print "fi\n"
+}
+{
+ if ($1 == "named") {
+ printf "\t\t# $named_flags is imported from /etc/rc.conf\n"
+ printf "\t\tif [ \"X${named_enable}\" = X\"YES\" ]; then\n"
+ printf "\t\t\t${named_program} ${named_flags} && {\n"
+ getline
+ printf "\t%s\n", $0
+ getline
+ printf "\t%s\n", $0
+ getline
+ printf "\t%s\n", $0
+ printf "\t\tfi\n"
+ } else if (/PS=`/) {
+ printf "\tif [ -f /proc/$PID/status ]; then\n"
+ printf "\t\tPS=`cat /proc/$PID/status 2>/dev/null | grep named`\n"
+ printf "\telse\n"
+ gsub("\t", "\t\t", $0);
+ print;
+ printf "\tfi\n"
+ } else {
+ if (/PATH=/) {
+ gsub(":/usr/ucb:", ":", $0);
+ }
+ print;
+ }
+}
diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile
new file mode 100644
index 0000000..ea2b61a
--- /dev/null
+++ b/usr.sbin/newsyslog/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.5 1998/09/19 22:42:11 obrien Exp $
+
+PROG= newsyslog
+
+MAN8= newsyslog.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8
new file mode 100644
index 0000000..c0e7925
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.8
@@ -0,0 +1,279 @@
+.\" This file contains changes from the Open Software Foundation.
+.\"
+.\" from: @(#)newsyslog.8
+.\" $Id: newsyslog.8,v 1.16 1999/01/27 04:27:49 jkoshy Exp $
+.\"
+.\" 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 January 27, 1999
+.Dt NEWSYSLOG 8
+.Os
+.Sh NAME
+.Nm newsyslog
+.Nd maintain system log files to manageable sizes
+.Sh SYNOPSIS
+.Nm newsyslog
+.Op Fl Fnrv
+.Op Fl f Ar config_file
+.Sh DESCRIPTION
+.Nm Newsyslog
+is a program that 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
+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.
+.Pp
+When starting up,
+.Nm
+reads in a configuration file to determine which logs may potentially
+be archived.
+By default, this configuration file is
+.Pa /etc/newsyslog.conf .
+Each line of the file contains information about a particular log file
+that should be handled by
+.Nm newsyslog .
+Each line has five mandatory fields and four optional fields, with a
+whitespace separating each field. Blank lines or lines beginning with
+``#'' are ignored. The fields of the configuration file are as
+follows:
+.Pp
+.Bl -tag -width indent
+.It Ar logfile_name
+Name of the system log file to be archived.
+.It Ar owner.group
+This optional field specifies the owner and group for the archive file.
+The "." is essential, even if the
+.Ar owner
+or
+.Ar group
+field is left blank. The field may be numeric, or a name which is
+present in
+.Pa /etc/passwd
+or
+.Pa /etc/group .
+.It Ar mode
+Specify the mode of the log file and archives.
+.It Ar count
+Specify the number of archive files to be kept
+besides the log file itself.
+.It Ar size
+When the size of the log file reaches
+.Ar size ,
+the log file will be trimmed as described above. If this field
+is replaced by an asterisk
+.Pq Ql \&* ,
+then the size of the log file is not taken into account
+when determining when to trim the log file.
+.It Ar when
+The
+.Ar when
+field can consist of an interval, a specific time, or both. If
+the
+.Ar when
+field is an asterisk
+.Pq Ql \&*
+log rotation will depend only on the contents of the
+.Ar size
+field.
+Otherwise, the
+.Ar when
+field consists of an optional interval in hours, optionally followed
+by an
+.So Li \&@ Sc Ns No -sign
+and a time in a restricted
+.Tn ISO 8601
+format. If a time is specified, the log file will only be trimmed
+if
+.Nm newsyslog
+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, both conditions must be satisfied for the rotation to take
+place.
+.Pp
+The particular format of the time is
+.Sm off
+.Oo
+.Oo
+.Oo
+.Oo
+.Oo
+.Va \&cc
+.Oc
+.Va \&yy
+.Oc
+.Va \&mm
+.Oc
+.Va \&dd
+.Oc
+.Oo
+.Li \&T
+.Oo
+.Va \&hh
+.Oo
+.Va \&mm
+.Oo
+.Va \&ss
+.Oc
+.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
+There is no provision for 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 `within the
+hour'.
+.It Ar flags
+This optional field specifies if the archive should have any
+special processing done to the archived log files.
+The
+.Ar Z
+flag will make the archive files compress to save space by
+using
+.Xr gzip 1 .
+The
+.Ar B
+flag means that the file is a binary file, and so the
+.Tn ASCII
+message which
+.Nm
+inserts to indicate the fact that the logs have been
+turned over should not be included. The
+.Ar -
+flag means nothing, but can be used as a placeholder when the
+.Ar path_to_pid_file
+field is specified.
+.It Ar path_to_pid_file
+This optional field specifies
+the file name to read to find the daemon process id. If this
+field is present, a
+.Ar signal_number
+is sent the process id contained in this
+file. This field must start with "/" in order to be recognized
+properly.
+.It Ar signal_number
+This optional field specifies
+the signal number will be sent to the daemon process. By default
+a SIGHUP will be sent.
+.El
+.Sh OPTIONS
+The following options can be used with newsyslog:
+.Bl -tag -width indent
+.It Fl f Ar config_file
+Instruct newsyslog to use
+.Ar config_file
+instead of
+.Pa /etc/newsyslog.conf
+for its configuration file.
+.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 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.
+.El
+.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 "SEE ALSO"
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr syslogd 8
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
new file mode 100644
index 0000000..832a8c3
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -0,0 +1,767 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: newsyslog.c,v 1.21 1998/12/23 12:03:33 peter Exp $";
+#endif /* not lint */
+
+#define OSF
+#ifndef COMPRESS_POSTFIX
+#define COMPRESS_POSTFIX ".gz"
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#include "pathnames.h"
+
+#define kbytes(size) (((size) + 1023) >> 10)
+#ifdef _IBMR2
+/* Calculates (db * DEV_BSIZE) */
+#define dbtob(db) ((unsigned)(db) << UBSHIFT)
+#endif
+
+#define CE_COMPACT 1 /* Compact the achived log files */
+#define CE_BINARY 2 /* Logfile is in binary, don't add */
+ /* status messages */
+#define CE_TRIMAT 4 /* trim at a specific time */
+
+#define NONE -1
+
+struct conf_entry {
+ char *log; /* Name of the log */
+ char *pid_file; /* PID file */
+ int uid; /* Owner of log */
+ int gid; /* Group of log */
+ int numlogs; /* Number of logs to keep */
+ int size; /* Size cutoff to trigger trimming the log */
+ int hours; /* Hours between log trimming */
+ time_t trim_at; /* Specific time to do trimming */
+ int permissions; /* File permissions on the log */
+ int flags; /* Flags (CE_COMPACT & CE_BINARY) */
+ int sig; /* Signal to send */
+ struct conf_entry *next; /* Linked list pointer */
+};
+
+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 force = 0; /* Force the trim no matter what*/
+char *conf = _PATH_CONF; /* Configuration file to use */
+time_t timenow;
+#define MIN_PID 5
+#define MAX_PID 99999 /* was lower, see /usr/include/sys/proc.h */
+char hostname[MAXHOSTNAMELEN+1]; /* hostname */
+char *daytime; /* timenow in human readable form */
+
+static struct conf_entry *parse_file();
+static char *sob(char *p);
+static char *son(char *p);
+static char *missing_field(char *p,char *errline);
+static void do_entry(struct conf_entry *ent);
+static void PRS(int argc,char **argv);
+static void usage();
+static void dotrim(char *log,char *pid_file,int numdays,int falgs,int perm,int owner_uid,int group_gid,int sig);
+static int log_trim(char *log);
+static void compress_log(char *log);
+static int sizefile(char *file);
+static int age_old_log(char *file);
+static pid_t get_pid(char *pid_file);
+static time_t parse8601(const char *s);
+
+int main(argc,argv)
+ int argc;
+ char **argv;
+{
+ struct conf_entry *p, *q;
+
+ PRS(argc,argv);
+ if (needroot && getuid() && geteuid())
+ errx(1, "must have root privs");
+ p = q = parse_file();
+
+ while (p) {
+ do_entry(p);
+ p=p->next;
+ free((char *) q);
+ q=p;
+ }
+ return(0);
+}
+
+static void do_entry(ent)
+ struct conf_entry *ent;
+
+{
+ int size, modtime;
+ char *pid_file;
+
+ if (verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: ",ent->log,ent->numlogs);
+ else
+ printf("%s <%d>: ",ent->log,ent->numlogs);
+ }
+ size = sizefile(ent->log);
+ modtime = age_old_log(ent->log);
+ if (size < 0) {
+ if (verbose)
+ printf("does not exist.\n");
+ } else {
+ if (ent->flags & CE_TRIMAT) {
+ if (timenow < ent->trim_at
+ || difftime(timenow, ent->trim_at) >= 60*60) {
+ if (verbose)
+ printf("--> will trim at %s",
+ ctime(&ent->trim_at));
+ return;
+ } else if (verbose && ent->hours <= 0) {
+ printf("--> time is up\n");
+ }
+ }
+ if (verbose && (ent->size > 0))
+ printf("size (Kb): %d [%d] ", size, ent->size);
+ if (verbose && (ent->hours > 0))
+ printf(" age (hr): %d [%d] ", modtime, ent->hours);
+ if (force || ((ent->size > 0) && (size >= ent->size)) ||
+ (ent->hours <= 0 && (ent->flags & CE_TRIMAT)) ||
+ ((ent->hours > 0) && ((modtime >= ent->hours)
+ || (modtime < 0)))) {
+ if (verbose)
+ printf("--> trimming log....\n");
+ if (noaction && !verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: trimming\n",
+ ent->log,ent->numlogs);
+ else
+ printf("%s <%d>: trimming\n",
+ ent->log,ent->numlogs);
+ }
+ if (ent->pid_file) {
+ pid_file = ent->pid_file;
+ } else {
+ /* Only try to notify syslog if we are root */
+ if (needroot)
+ pid_file = _PATH_SYSLOGPID;
+ else
+ pid_file = NULL;
+ }
+ dotrim(ent->log, pid_file, ent->numlogs,
+ ent->flags, ent->permissions, ent->uid, ent->gid, ent->sig);
+ } else {
+ if (verbose)
+ printf("--> skipping\n");
+ }
+ }
+}
+
+static void PRS(argc,argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ char *p;
+
+ timenow = time((time_t *) 0);
+ daytime = ctime(&timenow) + 4;
+ daytime[15] = '\0';
+
+ /* Let's get our hostname */
+ (void) gethostname(hostname, sizeof(hostname));
+
+ /* Truncate domain */
+ if ((p = strchr(hostname, '.'))) {
+ *p = '\0';
+ }
+
+ optind = 1; /* Start options parsing */
+ while ((c=getopt(argc,argv,"nrvFf:t:")) != -1)
+ switch (c) {
+ case 'n':
+ noaction++;
+ break;
+ case 'r':
+ needroot = 0;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ conf = optarg;
+ break;
+ case 'F':
+ force++;
+ break;
+ default:
+ usage();
+ }
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: newsyslog [-Fnrv] [-f config-file]\n");
+ exit(1);
+}
+
+/* Parse a configuration file and return a linked list of all the logs
+ * to process
+ */
+static struct conf_entry *parse_file()
+{
+ FILE *f;
+ char line[BUFSIZ], *parse, *q;
+ char *errline, *group;
+ struct conf_entry *first = NULL;
+ struct conf_entry *working = NULL;
+ struct passwd *pass;
+ struct group *grp;
+ int eol;
+
+ if (strcmp(conf,"-"))
+ f = fopen(conf,"r");
+ else
+ f = stdin;
+ if (!f)
+ err(1, "%s", conf);
+ while (fgets(line,BUFSIZ,f)) {
+ if ((line[0]== '\n') || (line[0] == '#'))
+ continue;
+ errline = strdup(line);
+ if (!first) {
+ working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
+ first = working;
+ } else {
+ working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
+ working = working->next;
+ }
+
+ q = parse = missing_field(sob(line),errline);
+ parse = son(line);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ working->log = strdup(q);
+
+ 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++ = '\0';
+ if (*q) {
+ if (!(isnumber(*q))) {
+ if ((pass = getpwnam(q)) == NULL)
+ errx(1,
+ "error in config file; unknown user:\n%s",
+ errline);
+ working->uid = pass->pw_uid;
+ } else
+ working->uid = atoi(q);
+ } else
+ working->uid = NONE;
+
+ q = group;
+ if (*q) {
+ if (!(isnumber(*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 = NONE;
+
+ 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 = working->gid = NONE;
+
+ 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))
+ errx(1, "error in config file; bad number:\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 (isdigit(*q))
+ working->size = atoi(q);
+ else
+ working->size = -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' && *ep != '@' && *ep != '*')
+ errx(1, "malformed interval/at:\n%s", errline);
+ if (*ep == '@') {
+ if ((working->trim_at = parse8601(ep + 1))
+ == (time_t)-1)
+ errx(1, "malformed at:\n%s", errline);
+ working->flags |= CE_TRIMAT;
+ }
+ }
+
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ parse = son(parse);
+ if (!*parse)
+ eol = 1;
+ *parse = '\0';
+ }
+
+ while (q && *q && !isspace(*q)) {
+ if ((*q == 'Z') || (*q == 'z'))
+ working->flags |= CE_COMPACT;
+ else if ((*q == 'B') || (*q == 'b'))
+ working->flags |= CE_BINARY;
+ else if (*q != '-')
+ errx(1, "illegal flag in config file -- %c", *q);
+ 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;
+ }
+
+ free(errline);
+ }
+ if (working)
+ working->next = (struct conf_entry *) NULL;
+ (void) fclose(f);
+ return(first);
+}
+
+static char *missing_field(p,errline)
+ char *p,*errline;
+{
+ if (!p || !*p)
+ errx(1, "missing field in config file:\n%s", errline);
+ return(p);
+}
+
+static void dotrim(log,pid_file,numdays,flags,perm,owner_uid,group_gid,sig)
+ char *log;
+ char *pid_file;
+ int numdays;
+ int flags;
+ int perm;
+ int owner_uid;
+ int group_gid;
+ int sig;
+{
+ char file1 [MAXPATHLEN+1], file2 [MAXPATHLEN+1];
+ char zfile1[MAXPATHLEN+1], zfile2[MAXPATHLEN+1];
+ int notified, need_notification, fd, _numdays;
+ struct stat st;
+ pid_t pid;
+
+#ifdef _IBMR2
+/* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */
+/* change it to be owned by uid -1, instead of leaving it as is, as it is */
+/* supposed to. */
+ if (owner_uid == -1)
+ owner_uid = geteuid();
+#endif
+
+ /* Remove oldest log */
+ (void) sprintf(file1,"%s.%d",log,numdays);
+ (void) strcpy(zfile1, file1);
+ (void) strcat(zfile1, COMPRESS_POSTFIX);
+
+ if (noaction) {
+ printf("rm -f %s\n", file1);
+ printf("rm -f %s\n", zfile1);
+ } else {
+ (void) unlink(file1);
+ (void) unlink(zfile1);
+ }
+
+ /* Move down log files */
+ _numdays = numdays; /* preserve */
+ while (numdays--) {
+ (void) strcpy(file2,file1);
+ (void) sprintf(file1,"%s.%d",log,numdays);
+ (void) strcpy(zfile1, file1);
+ (void) strcpy(zfile2, file2);
+ if (lstat(file1, &st)) {
+ (void) strcat(zfile1, COMPRESS_POSTFIX);
+ (void) strcat(zfile2, COMPRESS_POSTFIX);
+ if (lstat(zfile1, &st)) continue;
+ }
+ if (noaction) {
+ printf("mv %s %s\n",zfile1,zfile2);
+ printf("chmod %o %s\n", perm, zfile2);
+ printf("chown %d.%d %s\n",
+ owner_uid, group_gid, zfile2);
+ } else {
+ (void) rename(zfile1, zfile2);
+ (void) chmod(zfile2, perm);
+ (void) chown(zfile2, owner_uid, group_gid);
+ }
+ }
+ if (!noaction && !(flags & CE_BINARY))
+ (void) log_trim(log); /* Report the trimming to the old log */
+
+ if (!_numdays) {
+ if (noaction)
+ printf("rm %s\n",log);
+ else
+ (void)unlink(log);
+ }
+ else {
+ if (noaction)
+ printf("mv %s to %s\n",log,file1);
+ else
+ (void)rename(log, file1);
+ }
+
+ if (noaction)
+ printf("Start new log...");
+ else {
+ fd = creat(log,perm);
+ if (fd < 0)
+ err(1, "can't start new log");
+ if (fchown(fd, owner_uid, group_gid))
+ err(1, "can't chmod new log file");
+ (void) close(fd);
+ if (!(flags & CE_BINARY))
+ if (log_trim(log)) /* Add status message */
+ err(1, "can't add status message to log");
+ }
+ if (noaction)
+ printf("chmod %o %s...\n", perm, log);
+ else
+ (void) chmod(log,perm);
+
+ pid = 0;
+ need_notification = notified = 0;
+ if (pid_file != NULL) {
+ need_notification = 1;
+ pid = get_pid(pid_file);
+ }
+
+ if (pid) {
+ if (noaction) {
+ notified = 1;
+ printf("kill -%d %d\n", sig, (int)pid);
+ } else if (kill(pid,sig))
+ warn("can't notify daemon, pid %d", (int)pid);
+ else {
+ notified = 1;
+ if (verbose)
+ printf("daemon pid %d notified\n", (int)pid);
+ }
+ }
+
+ if ((flags & CE_COMPACT)) {
+ if (need_notification && !notified)
+ warnx("log not compressed because daemon not notified");
+ else if (noaction)
+ printf("Compress %s.0\n",log);
+ else {
+ if (notified) {
+ if (verbose)
+ printf("small pause to allow daemon to close log\n");
+ sleep(10);
+ }
+ compress_log(log);
+ }
+ }
+}
+
+/* Log the fact that the logs were turned over */
+static int log_trim(log)
+ char *log;
+{
+ FILE *f;
+ if ((f = fopen(log,"a")) == NULL)
+ return(-1);
+ fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
+ daytime, hostname, (int)getpid());
+ if (fclose(f) == EOF)
+ err(1, "log_trim: fclose:");
+ return(0);
+}
+
+/* Fork of /usr/ucb/compress to compress the old log file */
+static void compress_log(log)
+ char *log;
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN+1];
+
+ (void) sprintf(tmp,"%s.0",log);
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ else if (!pid) {
+ (void) execl(_PATH_GZIP, _PATH_GZIP, "-f", tmp, 0);
+ err(1, _PATH_GZIP);
+ }
+}
+
+/* Return size in kilobytes of a file */
+static int sizefile(file)
+ 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(file)
+ char *file;
+{
+ struct stat sb;
+ char tmp[MAXPATHLEN+sizeof(".0")+sizeof(COMPRESS_POSTFIX)+1];
+
+ (void) strcpy(tmp,file);
+ if (stat(strcat(tmp,".0"),&sb) < 0)
+ if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
+ return(-1);
+ return( (int) (timenow - sb.st_mtime + 1800) / 3600);
+}
+
+static pid_t get_pid(pid_file)
+ char *pid_file;
+{
+ FILE *f;
+ char line[BUFSIZ];
+ pid_t pid = 0;
+
+ if ((f = fopen(pid_file,"r")) == NULL)
+ warn("can't open %s pid file to restart a daemon",
+ pid_file);
+ else {
+ if (fgets(line,BUFSIZ,f)) {
+ pid = atol(line);
+ if (pid < MIN_PID || pid > MAX_PID) {
+ warnx("preposterous process number: %d", (int)pid);
+ pid = 0;
+ }
+ } else
+ warn("can't read %s pid file to restart a daemon",
+ pid_file);
+ (void)fclose(f);
+ }
+ return pid;
+}
+
+/* Skip Over Blanks */
+char *sob(p)
+ register char *p;
+{
+ while (p && *p && isspace(*p))
+ p++;
+ return(p);
+}
+
+/* Skip Over Non-Blanks */
+char *son(p)
+ register char *p;
+{
+ while (p && *p && !isspace(*p))
+ p++;
+ return(p);
+}
+
+/*
+ * 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 time_t
+parse8601(const char *s)
+{
+ char *t;
+ struct tm tm, *tmp;
+ u_long ul;
+
+ tmp = localtime(&timenow);
+ tm = *tmp;
+
+ tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
+
+ ul = strtoul(s, &t, 10);
+ if (*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.
+ */
+ switch (t - s) {
+ case 8:
+ tm.tm_year = ((ul / 1000000) - 19) * 100;
+ ul = ul % 1000000;
+ case 6:
+ tm.tm_year = tm.tm_year - (tm.tm_year % 100);
+ tm.tm_year += ul / 10000;
+ ul = ul % 10000;
+ case 4:
+ tm.tm_mon = (ul / 100) - 1;
+ ul = ul % 100;
+ case 2:
+ tm.tm_mday = ul;
+ 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;
+ ul = strtoul(s, &t, 10);
+ if (*t != '\0' && !isspace(*t))
+ return -1;
+
+ switch (t - s) {
+ case 6:
+ tm.tm_sec = ul % 100;
+ ul /= 100;
+ case 4:
+ tm.tm_min = ul % 100;
+ ul /= 100;
+ case 2:
+ tm.tm_hour = ul;
+ 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;
+ }
+
+ return mktime(&tm);
+}
+
+
diff --git a/usr.sbin/newsyslog/pathnames.h b/usr.sbin/newsyslog/pathnames.h
new file mode 100644
index 0000000..2eb271d
--- /dev/null
+++ b/usr.sbin/newsyslog/pathnames.h
@@ -0,0 +1,27 @@
+/*
+ * 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.
+
+ $Id$
+
+*/
+
+#define _PATH_CONF "/etc/newsyslog.conf"
+#define _PATH_SYSLOGPID _PATH_VARRUN "syslog.pid"
+#define _PATH_GZIP "/usr/bin/gzip"
diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile
new file mode 100644
index 0000000..12ce92e
--- /dev/null
+++ b/usr.sbin/nfsd/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+=-DNFS
+MAN8= nfsd.8
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..4f7bb90
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.8
@@ -0,0 +1,134 @@
+.\" 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
+.\"
+.Dd March 29, 1995
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm nfsd
+.Op Fl rut
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+.Nm Nfsd
+runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm nfsd
+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 portmap 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 portmap server is restarted.
+.It Fl n
+Specifies how many servers to create.
+.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
+.Nm Nfsd
+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 nfsd
+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 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+KLD is available,
+.Nm nfsd
+will exit with an error.
+.Pp
+The
+.Nm nfsd
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr kldload 8 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsd
+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..e1a1ffe
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,673 @@
+/*
+ * 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[] =
+ "$Id$";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/wait.h>
+#include <sys/mount.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+
+#ifdef NFSKERB
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <libutil.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s) fprintf(stderr,(s))
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+#ifdef OLD_SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArg = NULL; /* end of argv */
+#endif
+
+#ifdef NFSKERB
+char lnam[ANAME_SZ];
+KTEXT_ST kt;
+AUTH_DAT kauth;
+char inst[INST_SZ];
+struct nfsrpc_fullblock kin, kout;
+struct nfsrpc_fullverf kverf;
+NFSKERBKEY_T kivec;
+struct timeval ktv;
+NFSKERBKEYSCHED_T kerb_keysched;
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void setproctitle __P((char *));
+#endif
+#endif
+void usage __P((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 portmap
+ *
+ * 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:
+ * -c - support iso cltp clients
+ * -r - reregister with portmapper
+ * -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 sockaddr_in inetaddr, inetpeer;
+#ifdef ISO
+ struct sockaddr_iso isoaddr, isopeer;
+ char *cp;
+#endif
+ fd_set ready, sockbits;
+ int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock;
+ int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock;
+ int tp4cnt, tp4flag, tpipcnt, tpipflag, udpflag;
+#ifdef notyet
+ int tp4sock, tpipsock;
+#endif
+#ifdef NFSKERB
+ struct group *grp;
+ struct passwd *pwd;
+ struct ucred *cr;
+ struct timeval ktv;
+ char **cpp;
+#endif
+#ifdef __FreeBSD__
+ struct vfsconf vfc;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if (vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS is not available in the running kernel");
+#endif
+
+#ifdef OLD_SETPROCTITLE
+ /* Save start and extent of argv for setproctitle. */
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 4
+ nfsdcnt = DEFNFSDCNT;
+ cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0;
+ tpipflag = udpflag = 0;
+#ifdef ISO
+#define GETOPT "cn:rtu"
+#define USAGE "[-crtu] [-n num_servers]"
+#else
+#define GETOPT "n:rtu"
+#define USAGE "[-rtu] [-n num_servers]"
+#endif
+ while ((ch = getopt(argc, argv, GETOPT)) != -1)
+ switch (ch) {
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", nfsdcnt,
+ DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+#ifdef ISO
+ case 'c':
+ cltpflag = 1;
+ break;
+#ifdef notyet
+ case 'i':
+ tp4cnt = 1;
+ break;
+ case 'p':
+ tpipcnt = 1;
+ break;
+#endif /* notyet */
+#endif /* ISO */
+ 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;
+ }
+ }
+
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGTERM, SIG_IGN);
+ }
+ (void)signal(SIGCHLD, reapchild);
+
+ if (reregister) {
+ if (udpflag &&
+ (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)))
+ err(1, "can't register with portmap for UDP");
+ if (tcpflag &&
+ (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)))
+ err(1, "can't register with portmap for TCP");
+ exit(0);
+ }
+ openlog("nfsd:", LOG_PID, LOG_DAEMON);
+
+ for (i = 0; i < nfsdcnt; i++) {
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit (1);
+ case 0:
+ break;
+ default:
+ continue;
+ }
+
+ setproctitle("server");
+ nfssvc_flag = NFSSVC_NFSD;
+ nsd.nsd_nfsd = NULL;
+#ifdef NFSKERB
+ if (sizeof (struct nfsrpc_fullverf) != RPCX_FULLVERF ||
+ sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK)
+ syslog(LOG_ERR, "Yikes NFSKERB structs not packed!");
+ nsd.nsd_authstr = (u_char *)&kt;
+ nsd.nsd_authlen = sizeof (kt);
+ nsd.nsd_verfstr = (u_char *)&kverf;
+ nsd.nsd_verflen = sizeof (kverf);
+#endif
+ while (nfssvc(nfssvc_flag, &nsd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit(1);
+ }
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
+#ifdef NFSKERB
+ /*
+ * Get the Kerberos ticket out of the authenticator
+ * verify it and convert the principal name to a user
+ * name. The user name is then converted to a set of
+ * user credentials via the password and group file.
+ * Finally, decrypt the timestamp and validate it.
+ * For more info see the IETF Draft "Authentication
+ * in ONC RPC".
+ */
+ kt.length = ntohl(kt.length);
+ if (gettimeofday(&ktv, (struct timezone *)0) == 0 &&
+ kt.length > 0 && kt.length <=
+ (RPCAUTH_MAXSIZ - 3 * NFSX_UNSIGNED)) {
+ kin.w1 = NFS_KERBW1(kt);
+ kt.mbz = 0;
+ (void)strcpy(inst, "*");
+ if (krb_rd_req(&kt, NFS_KERBSRV,
+ inst, nsd.nsd_haddr, &kauth, "") == RD_AP_OK &&
+ krb_kntoln(&kauth, lnam) == KSUCCESS &&
+ (pwd = getpwnam(lnam)) != NULL) {
+ cr = &nsd.nsd_cr;
+ cr->cr_uid = pwd->pw_uid;
+ cr->cr_groups[0] = pwd->pw_gid;
+ cr->cr_ngroups = 1;
+ setgrent();
+ while ((grp = getgrent()) != NULL) {
+ if (grp->gr_gid == cr->cr_groups[0])
+ continue;
+ for (cpp = grp->gr_mem;
+ *cpp != NULL; ++cpp)
+ if (!strcmp(*cpp, lnam))
+ break;
+ if (*cpp == NULL)
+ continue;
+ cr->cr_groups[cr->cr_ngroups++]
+ = grp->gr_gid;
+ if (cr->cr_ngroups == NGROUPS)
+ break;
+ }
+ endgrent();
+
+ /*
+ * Get the timestamp verifier out of the
+ * authenticator and verifier strings.
+ */
+ kin.t1 = kverf.t1;
+ kin.t2 = kverf.t2;
+ kin.w2 = kverf.w2;
+ bzero((caddr_t)kivec, sizeof (kivec));
+ bcopy((caddr_t)kauth.session,
+ (caddr_t)nsd.nsd_key,sizeof(kauth.session));
+
+ /*
+ * Decrypt the timestamp verifier in CBC mode.
+ */
+ XXX
+
+ /*
+ * Validate the timestamp verifier, to
+ * check that the session key is ok.
+ */
+ nsd.nsd_timestamp.tv_sec = ntohl(kout.t1);
+ nsd.nsd_timestamp.tv_usec = ntohl(kout.t2);
+ nsd.nsd_ttl = ntohl(kout.w1);
+ if ((nsd.nsd_ttl - 1) == ntohl(kout.w2))
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
+ }
+#endif /* NFSKERB */
+ }
+ exit(0);
+ }
+
+ /* If we are serving udp, set up the socket. */
+ if (udpflag) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create udp socket");
+ exit(1);
+ }
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(sock,
+ (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind udp addr");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ exit(1);
+ }
+ (void)close(sock);
+ }
+
+#ifdef ISO
+ /* If we are serving cltp, set up the socket. */
+ if (cltpflag) {
+ if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create cltp socket");
+ exit(1);
+ }
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(sock,
+ (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind cltp addr");
+ exit(1);
+ }
+#ifdef notyet
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+#endif /* notyet */
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't add UDP socket");
+ exit(1);
+ }
+ close(sock);
+ }
+#endif /* ISO */
+
+ /* Now set up the master server socket waiting for tcp connections. */
+ on = 1;
+ FD_ZERO(&sockbits);
+ connect_type_cnt = 0;
+ if (tcpflag) {
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tcp socket");
+ exit(1);
+ }
+ if (setsockopt(tcpsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tcpsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tcpsock, &sockbits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+
+#ifdef notyet
+ /* Now set up the master server socket waiting for tp4 connections. */
+ if (tp4flag) {
+ if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tp4 socket");
+ exit(1);
+ }
+ if (setsockopt(tp4sock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(tp4sock,
+ (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tp4 addr");
+ exit(1);
+ }
+ if (listen(tp4sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tp4sock, &sockbits);
+ maxsock = tp4sock;
+ connect_type_cnt++;
+ }
+
+ /* Now set up the master server socket waiting for tpip connections. */
+ if (tpipflag) {
+ if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tpip socket");
+ exit(1);
+ }
+ if (setsockopt(tpipsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tpipsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tpipsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tpipsock, &sockbits);
+ maxsock = tpipsock;
+ connect_type_cnt++;
+ }
+#endif /* notyet */
+
+ if (connect_type_cnt == 0)
+ exit(0);
+
+ setproctitle("master");
+
+ /*
+ * 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");
+ exit(1);
+ }
+ }
+ if (tcpflag && FD_ISSET(tcpsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ 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 = sizeof(inetpeer);
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#ifdef notyet
+ if (tp4flag && FD_ISSET(tp4sock, &ready)) {
+ len = sizeof(isopeer);
+ if ((msgsock = accept(tp4sock,
+ (struct sockaddr *)&isopeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ 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)&isopeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+ if (tpipflag && FD_ISSET(tpipsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tpipsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ 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)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#endif /* notyet */
+ }
+}
+
+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;
+{
+
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void
+setproctitle(a)
+ char *a;
+{
+ register char *cp;
+ char buf[80];
+
+ cp = Argv[0];
+ (void)snprintf(buf, sizeof(buf), "nfsd-%s", a);
+ (void)strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = '\0';
+ Argv[1] = NULL;
+}
+#endif /* __FreeBSD__ */
+#endif
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 0000000..e30ea08
--- /dev/null
+++ b/usr.sbin/nologin/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+MAN8= nologin.8
+MAN5= nologin.5
+
+nologin depend lint tags:
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/nologin.sh ${DESTDIR}${BINDIR}/nologin
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nologin/nologin.5 b/usr.sbin/nologin/nologin.5
new file mode 100644
index 0000000..37d311a
--- /dev/null
+++ b/usr.sbin/nologin/nologin.5
@@ -0,0 +1,66 @@
+.\" 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
+.\" $Id: nologin.5,v 1.5 1999/01/12 14:09:23 asami Exp $
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 5
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+.Sh DESCRIPTION
+.Nm Nologin
+disallows logins if the file
+.Pa /var/run/nologin
+exists.
+Programs display the contents of
+.Pa /var/run/nologin
+to the user and exit.
+.Sh SECURITY
+Ignored by
+.Xr login 1
+for user root.
+.Sh FILES
+.Bl -tag -width /var/run/nologinxxx -compact
+.It Pa /var/run/nologin
+The
+.Nm
+file resides in
+.Pa /var/run .
+.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..4e7bf61
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,56 @@
+.\" 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
+.\" $Id: nologin.8,v 1.2 1998/07/15 06:37:07 charnier Exp $
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os BSD 4.4
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm nologin
+.Sh DESCRIPTION
+.Nm Nologin
+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.
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr nologin 5 .
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nologin/nologin.sh b/usr.sbin/nologin/nologin.sh
new file mode 100644
index 0000000..346836f
--- /dev/null
+++ b/usr.sbin/nologin/nologin.sh
@@ -0,0 +1,39 @@
+#!/bin/sh -p
+#
+# 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.
+#
+# @(#)nologin.sh 8.1 (Berkeley) 6/5/93
+# $Id$
+#
+
+echo 'This account is currently not available.'
+exit 1
diff --git a/usr.sbin/nslookup/Makefile b/usr.sbin/nslookup/Makefile
new file mode 100644
index 0000000..13f7a41
--- /dev/null
+++ b/usr.sbin/nslookup/Makefile
@@ -0,0 +1,21 @@
+# $Id: Makefile,v 1.6 1998/05/03 05:14:56 peter Exp $
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nslookup
+.PATH: ${BIND_DIR}/doc/man
+
+PROG= nslookup
+SRCS= main.c getinfo.c debug.c send.c skip.c list.c subr.c commands.l
+MAN8= nslookup.8
+
+CFLAGS+=-D_PATH_HELPFILE=\"${DESTHELP}/nslookup.help\"
+LDADD+= -ll
+DPADD+= ${LIBL}
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${BIND_DIR}/bin/nslookup/nslookup.help \
+ ${DESTDIR}${DESTHELP}/nslookup.help
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nsupdate/Makefile b/usr.sbin/nsupdate/Makefile
new file mode 100644
index 0000000..c8675b5
--- /dev/null
+++ b/usr.sbin/nsupdate/Makefile
@@ -0,0 +1,10 @@
+# $FreeBSD$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/bin/nsupdate
+
+PROG= nsupdate
+NOMAN= hmm..
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pccard/Makefile b/usr.sbin/pccard/Makefile
new file mode 100644
index 0000000..7b8a0a6
--- /dev/null
+++ b/usr.sbin/pccard/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for pccardc/pccardd.
+# $Id: Makefile,v 1.3 1997/02/22 16:08:32 peter Exp $
+#
+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..3d73c9e
--- /dev/null
+++ b/usr.sbin/pccard/Makefile.inc
@@ -0,0 +1,2 @@
+CFLAGS+= -Wall -g -static
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/pccard/pccardc/Makefile b/usr.sbin/pccard/pccardc/Makefile
new file mode 100644
index 0000000..8a4ac9a
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/Makefile
@@ -0,0 +1,14 @@
+#
+# pccardc Makefile
+#
+# $Id: Makefile,v 1.7 1998/02/26 14:36:00 hosokawa Exp $
+#
+PROG= pccardc
+NOMAN= noman
+SRCS= beep.c dumpcis.c enabler.c pccardc.c pccardmem.c printcis.c \
+ rdattr.c rdmap.c rdreg.c readcis.c wrattr.c wrreg.c
+
+CFLAGS+= -I${.CURDIR}/../pccardd
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/../pccardd
diff --git a/usr.sbin/pccard/pccardc/beep.c b/usr.sbin/pccard/pccardc/beep.c
new file mode 100644
index 0000000..b2dba5a
--- /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[] =
+ "$Id$";
+#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..d991f6f
--- /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[] =
+ "$Id: dumpcis.c,v 1.10 1999/02/05 16:00:15 kuriyama Exp $";
+#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/enabler.c b/usr.sbin/pccard/pccardc/enabler.c
new file mode 100644
index 0000000..ea0779e
--- /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[] =
+ "$Id: enabler.c,v 1.11 1999/02/05 16:00:15 kuriyama Exp $";
+#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 %d, 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.c b/usr.sbin/pccard/pccardc/pccardc.c
new file mode 100644
index 0000000..2a42b08
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.c
@@ -0,0 +1,95 @@
+/*
+ * 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[] =
+ "$Id: pccardc.c,v 1.7 1998/02/26 14:36:01 hosokawa Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+typedef int (*main_t)(int, char **);
+
+#define DECL(foo) int foo(int, char**);
+DECL(beep_main);
+DECL(dumpcis_main);
+DECL(enabler_main);
+DECL(help_main);
+DECL(pccardmem_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" },
+ { "enabler", enabler_main, "Device driver enabler" },
+ { "help", help_main, "Prints command summary" },
+ { "pccardmem", pccardmem_main, "Allocate memory for pccard driver" },
+ { "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..240cdd6
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardmem.c
@@ -0,0 +1,71 @@
+/*
+ * 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[] =
+ "$Id: pccardmem.c,v 1.10 1999/02/05 16:00:15 kuriyama Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.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/printcis.c b/usr.sbin/pccard/pccardc/printcis.c
new file mode 100644
index 0000000..ff5a157
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/printcis.c
@@ -0,0 +1,709 @@
+/*
+ * 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[] =
+ "$Id: printcis.c,v 1.9 1997/11/18 21:08:07 nate Exp $";
+#endif /* not lint */
+
+#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 dump_pwr_desc(unsigned char *);
+void print_ext_speed(unsigned char, int);
+void dump_device_desc(unsigned char *p, int len, char *type);
+void dump_info_v1(unsigned char *p, int len);
+void dump_config_map(struct tuple *tp);
+void dump_cis_config(struct tuple *tp);
+void dump_other_cond(unsigned char *p);
+void dump_func_ext(unsigned char *p, int len);
+
+void
+dumpcis(struct cis *cp)
+{
+ struct tuple *tp;
+ struct tuple_list *tl;
+ int count = 0, sz, ad, i;
+ unsigned char *p;
+
+ 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_CHECKSUM: /* 0x10 */
+ if (tp->length == 5) {
+ printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
+ (short)((tp->data[1] << 8) | tp->data[0]),
+ (tp->data[3] << 8) | tp->data[2],
+ tp->data[4]);
+ } else
+ printf("\tIllegal length for checksum!\n");
+ break;
+ case CIS_LONGLINK_A: /* 0x11 */
+ printf("\tLong link to attribute memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ case CIS_LONGLINK_C: /* 0x12 */
+ printf("\tLong link to common memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ 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 */
+ break;
+ 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 */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICE_OA: /* 0x1D */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICEGEO: /* 0x1E */
+ break;
+ case CIS_DEVICEGEO_A: /* 0x1F */
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
+ (tp->data[1] << 8) | tp->data[0],
+ (tp->data[3] << 8) | tp->data[2]);
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ switch (tp->data[0]) {
+ default:
+ printf("\tUnknown function");
+ break;
+ case 0:
+ printf("\tMultifunction card");
+ break;
+ case 1:
+ printf("\tMemory card");
+ break;
+ case 2:
+ printf("\tSerial port/modem");
+ break;
+ case 3:
+ printf("\tParallel port");
+ break;
+ case 4:
+ printf("\tFixed disk card");
+ break;
+ case 5:
+ printf("\tVideo adapter");
+ break;
+ case 6:
+ printf("\tNetwork/LAN adapter");
+ break;
+ case 7:
+ printf("\tAIMS");
+ break;
+ }
+ printf("%s%s\n", (tp->data[1] & 1) ? " - POST initialize" : "",
+ (tp->data[1] & 2) ? " - Card has ROM" : "");
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ dump_func_ext(tp->data, tp->length);
+ break;
+ case CIS_VERS_2: /* 0x40 */
+ break;
+ }
+ }
+}
+
+/*
+ * Dump configuration map tuple.
+ */
+void
+dump_config_map(struct tuple *tp)
+{
+ unsigned char *p, x;
+ int rlen, mlen;
+ int i;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+
+ rlen = (tp->data[0] & 3) + 1;
+ mlen = ((tp->data[0] >> 2) & 3) + 1;
+ u.l = 0;
+ p = tp->data + 2;
+ for (i = 0; i < rlen; i++)
+ u.b[i] = *p++;
+ printf("\tReg len = %d, config register addr = 0x%lx, last config = 0x%x\n",
+ rlen, u.l, tp->data[1]);
+ if (mlen)
+ printf("\tRegisters: ");
+ for (i = 0; i < mlen; i++, p++) {
+ for (x = 0x1; x; x <<= 1)
+ printf("%c", x & *p ? 'X' : '-');
+ printf(" ");
+ }
+ printf("\n");
+}
+
+/*
+ * Dump a config entry.
+ */
+void
+dump_cis_config(struct tuple *tp)
+{
+ unsigned char *p, feat;
+ int i, j;
+ char c;
+
+ p = tp->data;
+ printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
+ *p & 0x40 ? "(default)" : "");
+ if (*p & 0x80) {
+ p++;
+ printf("\tInterface byte = 0x%x ", *p);
+ switch (*p & 0xF) {
+ 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) {
+ printf(" BVD1/2 active");
+ c = ',';
+ }
+ if (*p & 0x20) {
+ printf("%c card WP active", c); /* Write protect */
+ c = ',';
+ }
+ if (*p & 0x40) {
+ printf("%c +RDY/-BSY active", c);
+ c = ',';
+ }
+ if (*p & 0x80)
+ printf("%c wait signal supported", c);
+ printf("\n");
+ }
+ p++;
+ feat = *p++;
+ switch (CIS_FEAT_POWER(feat)) {
+ case 0:
+ break;
+ case 1:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 2:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 3:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp1 pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp2 pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ }
+ if (feat & CIS_FEAT_TIMING) {
+ i = CIS_WAIT_SCALE(*p);
+ j = CIS_READY_SCALE(*p);
+ p++;
+ if (i != 3) {
+ printf("\tWait scale ");
+ print_ext_speed(*p, i);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ if (j != 7) {
+ printf("\tRDY/BSY scale ");
+ print_ext_speed(*p, j);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ }
+ if (feat & CIS_FEAT_I_O) {
+ if (CIS_IO_ADDR(*p))
+ printf("\tCard decodes %d address lines",
+ CIS_IO_ADDR(*p));
+ else
+ printf("\tCard provides address decode");
+ switch (CIS_MEM_ADDRSZ(*p)) {
+ case 0:
+ break;
+ case 1:
+ printf(", 8 Bit I/O only");
+ break;
+ case 2:
+ printf(", limited 8/16 Bit I/O");
+ break;
+ case 3:
+ printf(", full 8/16 Bit I/O");
+ break;
+ }
+ printf("\n");
+ if (*p & CIS_IO_RANGE) {
+ p++;
+ c = *p++;
+ for (i = 0; i <= CIS_IO_BLKS(c); i++) {
+ printf("\t\tI/O address # %d: ", i + 1);
+ switch (CIS_IO_ADSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf("block start = 0x%x", *p++);
+ break;
+ case 2:
+ printf("block start = 0x%x", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf("block start = 0x%x",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch (CIS_IO_BLKSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" block length = 0x%x", *p++ + 1);
+ break;
+ case 2:
+ printf(" block length = 0x%x", ((p[1] << 8) | *p) + 1);
+ p += 2;
+ break;
+ case 3:
+ printf(" block length = 0x%x",
+ ((p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p) + 1);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ }
+ }
+
+ /* IRQ descriptor */
+ if (feat & CIS_FEAT_IRQ) {
+ printf("\t\tIRQ modes:");
+ c = ' ';
+ if (*p & CIS_IRQ_LEVEL) {
+ printf(" Level");
+ c = ',';
+ }
+ if (*p & CIS_IRQ_PULSE) {
+ printf("%c Pulse", c);
+ c = ',';
+ }
+ if (*p & CIS_IRQ_SHARING)
+ printf("%c Shared", c);
+ printf("\n");
+ if (*p & CIS_IRQ_MASK) {
+ i = p[0] | (p[1] << 8);
+ 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);
+ printf("\n");
+ p += 3;
+ } else {
+ printf("\t\tIRQ level = %d\n", CIS_IRQ_IRQN(*p));
+ p++;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case 0:
+ break;
+ case 1:
+ printf("\tMemory space length = 0x%x\n", (p[1] << 8) | p[0]);
+ p += 2;
+ break;
+ case 2:
+ printf("\tMemory space address = 0x%x, length = 0x%x\n",
+ (p[3] << 8) | p[2],
+ (p[1] << 8) | p[0]);
+ p += 4;
+ break;
+
+ /* Memory descriptors. */
+ case 3:
+ c = *p++;
+ for (i = 0; i <= (c & 7); i++) {
+ printf("\tMemory descriptor %d\n\t\t", i + 1);
+ switch (CIS_MEM_LENSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" blk length = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" blk length = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" blk length = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch (CIS_MEM_ADDRSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" card addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" card addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" card addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ if (c & CIS_MEM_HOST)
+ switch ((c >> 5) & 3) {
+ case 0:
+ break;
+ case 1:
+ printf(" host addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" host addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" host addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ break;
+ }
+ if (feat & CIS_FEAT_MISC) {
+ printf("\tMax twin cards = %d\n", *p & 7);
+ printf("\tMisc attr:");
+ if (*p & 0x8)
+ printf(" (Audio-BVD2)");
+ if (*p & 0x10)
+ printf(" (Read-only)");
+ if (*p & 0x20)
+ printf(" (Power down supported)");
+ if (*p & 0x80) {
+ printf(" (Ext byte = 0x%x)", p[1]);
+ p++;
+ }
+ printf("\n");
+ p++;
+ }
+}
+
+/*
+ * dump_other_cond - Dump other conditions.
+ */
+void
+dump_other_cond(unsigned char *p)
+{
+ if (p[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");
+ }
+}
+
+/*
+ * Dump power descriptor.
+ */
+int
+dump_pwr_desc(unsigned char *p)
+{
+ int len = 1, i;
+ unsigned 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);
+}
+
+void
+dump_device_desc(unsigned 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) {
+ unsigned 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++;
+ }
+}
+
+/*
+ * Print version info
+ */
+void
+dump_info_v1(unsigned char *p, int len)
+{
+ printf("\tVersion = %d.%d", p[0], p[1]);
+ p += 2;
+ printf(", Manuf = [%s],", p);
+ while (*p++);
+ printf("card vers = [%s]\n", p);
+ while (*p++);
+ if (*p == 0xff)
+ return;
+ printf("\tAddit. info = [%s]", p);
+ while (*p++);
+ while (*p != 0xff) {
+ printf(",[%s]", p);
+ while (*p++);
+ }
+ printf("\n");
+}
+
+/*
+ * dump functional extension tuple.
+ */
+void
+dump_func_ext(unsigned char *p, int len)
+{
+ if (len == 0)
+ return;
+ switch (p[0]) {
+ case 0:
+ case 8:
+ case 10:
+ if (len != 4) {
+ printf("\tWrong length for serial extension\n");
+ return;
+ }
+ printf("\tSerial interface extension:\n");
+ 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",
+ (p[2] & 1) ? "Space," : "",
+ (p[2] & 2) ? "Mark," : "",
+ (p[2] & 4) ? "Odd," : "",
+ (p[2] & 8) ? "Even," : "");
+ printf("\n");
+ break;
+ case 1:
+ case 5:
+ case 6:
+ case 7:
+ printf("\tModem interface capabilities:\n");
+ break;
+ case 2:
+ printf("\tData modem services available:\n");
+ break;
+ case 9:
+ printf("\tFax/modem services available:\n");
+ break;
+ case 4:
+ printf("\tVoice services available:\n");
+ break;
+ }
+}
+
+/*
+ * print_ext_speed - Print extended speed.
+ */
+void
+print_ext_speed(unsigned 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]);
+}
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..41038ae
--- /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[] =
+ "$Id: rdmap.c,v 1.10 1999/02/05 16:00:16 kuriyama Exp $";
+#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..da28f52
--- /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[] =
+ "$Id: rdreg.c,v 1.10 1999/02/05 16:00:16 kuriyama Exp $";
+#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..51b57bc
--- /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[] =
+ "$Id: wrattr.c,v 1.11 1999/02/05 16:00:16 kuriyama Exp $";
+#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..b86ee11
--- /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[] =
+ "$Id: wrreg.c,v 1.10 1999/02/05 16:00:16 kuriyama Exp $";
+#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..af70e31
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/Makefile
@@ -0,0 +1,12 @@
+# Makefile for pccardd
+
+PROG= pccardd
+SRCS= pccardd.c cardd.c file.c util.c readcis.c printcis.c
+MAN8= pccardd.8
+MAN5= pccard.conf.5
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../pccardc
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/../pccardc
diff --git a/usr.sbin/pccard/pccardd/cardd.c b/usr.sbin/pccard/pccardd/cardd.c
new file mode 100644
index 0000000..2e5a529
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.c
@@ -0,0 +1,563 @@
+/*
+ * 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[] =
+ "$Id: cardd.c,v 1.33 1999/01/10 13:00:09 guido Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include "cardd.h"
+
+static struct card_config *assign_driver(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 *);
+
+/*
+ * 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 = 0x%x, driver name = %s\n",
+ confp->index, 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);
+ }
+ }
+}
+
+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 *slots, *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 noslot:
+ /* Debounce potentially incorrectly reported removals */
+ if (state.laststate == filled || state.laststate == suspend)
+ card_removed(sp);
+ break;
+ case filled:
+ card_inserted(sp);
+ break;
+ case suspend:
+ /* ignored */
+ break;
+ }
+}
+
+/*
+ * 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;
+
+ if (sp->cis)
+ freecis(sp->cis);
+ if (sp->config) {
+ sp->config->inuse = 0;
+ sp->config->driver->inuse = 0;
+ }
+ if ((cp = sp->card) != 0)
+ execute(cp->remove, sp);
+ sp->cis = 0;
+ sp->config = 0;
+ /* release io */
+ bit_nset(io_avail, sp->io.addr, sp->io.addr + sp->io.size - 1);
+}
+
+/*
+ * 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;
+
+ sleep(5);
+ 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)
+ if (strncmp(cp->manuf, sp->cis->manuf, CIS_MAXSTR) == 0 &&
+ strncmp(cp->version, sp->cis->vers, CIS_MAXSTR) == 0)
+ break;
+ 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;
+ }
+ if (cp->ether)
+ read_ether(sp);
+ if ((sp->config = assign_driver(cp)) == NULL)
+ return;
+ if (assign_io(sp)) {
+ logmsg("Resource allocation failure for %s", sp->cis->manuf);
+ 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];
+
+ lseek(sp->fd, (off_t)sp->card->ether, 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]);
+}
+
+/*
+ * assign_driver - Assign driver to card.
+ * First, see if an existing driver is already setup.
+ */
+static struct card_config *
+assign_driver(struct card *cp)
+{
+ struct driver *drvp;
+ struct card_config *conf;
+
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == cp &&
+ conf->driver->config == conf &&
+ conf->driver->inuse == 0) {
+#ifdef DEBUG
+ logmsg("Found existing driver (%s) for %s\n",
+ conf->driver->name, cp->manuf);
+#endif
+ conf->driver->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+ }
+ /*
+ * New driver must be allocated. Find one that matches the
+ * any configurations not in use.
+ */
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->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 */
+ if (conf->irq == 0) {
+ int i;
+ for (i = 1; i < 16; i++)
+ if (pool_irq[i]) {
+ conf->irq = i;
+ pool_irq[i] = 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);
+}
+
+/*
+ * 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;
+ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
+ if (cisconf->id == sp->config->index)
+ break;
+ if (cisconf == 0)
+ 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.
+ */
+ if (cisconf->memspace || (defconf && defconf->memspace)) {
+ struct cis_memblk *mp;
+
+ mp = cisconf->mem;
+ 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;
+ }
+ sp->mem.cardaddr = 0x4000;
+#ifdef DEBUG
+ logmsg("Using mem addr 0x%x, size %d, card addr 0x%x\n",
+ sp->mem.addr, sp->mem.size, sp->mem.cardaddr);
+#endif
+ }
+
+ /* Now look at I/O. */
+ bzero(&sp->io, sizeof(sp->io));
+ if (cisconf->iospace || (defconf && defconf->iospace)) {
+ struct cis_config *cp;
+
+ cp = cisconf;
+ if (!cisconf->iospace)
+ cp = defconf;
+ /*
+ * 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.
+ */
+ if (cp->io) {
+ sp->io.addr = cp->io->addr;
+ sp->io.size = cp->io->size;
+ } else
+ /*
+ * No I/O block, assume the address lines
+ * decode gives the size.
+ */
+ sp->io.size = 1 << cp->io_addr;
+
+ if (sp->io.addr == 0) {
+ int i = bit_fns(io_avail, IOPORTS, sp->io.size);
+
+ if (i < 0)
+ return (-1);
+ sp->io.addr = i;
+ }
+ bit_nclear(io_avail, sp->io.addr,
+ sp->io.addr + sp->io.size - 1);
+
+ /* Set up the size to take into account the decode lines. */
+ sp->io.cardaddr = cp->io_addr;
+ switch (cp->io_bus) {
+ case 0:
+ break;
+ case 1:
+ sp->io.flags = IODF_WS;
+ break;
+ case 2:
+ sp->io.flags = IODF_WS | IODF_CS16;
+ break;
+ case 3:
+ sp->io.flags = IODF_WS | IODF_CS16 | IODF_16BIT;
+ break;
+ }
+#ifdef DEBUG
+ logmsg("Using I/O addr 0x%x, size %d\n",
+ sp->io.addr, sp->io.size);
+#endif
+ }
+ sp->irq = sp->config->irq;
+ 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;
+ char c;
+ off_t offs;
+ int rw_flags;
+
+ 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);
+ 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);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = sp->config->index;
+ c |= 0x40;
+ write(sp->fd, &c, sizeof(c));
+#ifdef DEBUG
+ logmsg("Setting config reg at offs 0x%lx to 0x%x, Reset time = %d ms\n",
+ (unsigned long)offs, c, sp->card->reset_time);
+#endif
+ sleep(5);
+ 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));
+ }
+ mem.window = 0;
+ if (sp->mem.addr) {
+ mem.window = 0;
+ mem.flags = sp->mem.flags | MDF_ACTIVE | MDF_16BITS;
+ 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);
+ }
+ }
+ io.window = 0;
+ if (sp->io.size) {
+ io.flags = sp->io.flags;
+ io.start = sp->io.addr;
+ io.size = sp->io.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
+#ifdef DEBUG
+ logmsg("Assigning I/O window %d, start 0x%x, size 0x%x flags 0x%x\n",
+ io.window, io.start, io.size, io.flags);
+#endif
+ io.flags |= IODF_ACTIVE;
+ if (ioctl(sp->fd, PIOCSIO, &io)) {
+ logerr("ioctl (PIOCSIO)");
+ return (0);
+ }
+ }
+ strcpy(drv.name, drvp->kernel);
+ drv.unit = drvp->unit;
+ drv.irqmask = 1 << sp->irq;
+ drv.flags = 0x80;
+ if (sp->mem.size) {
+ drv.mem = sp->mem.addr;
+ drv.memsize = sp->mem.size;
+ } else {
+ drv.mem = 0;
+ drv.memsize = 0;
+ }
+ if (sp->io.size)
+ drv.iobase = sp->io.addr;
+ else
+ drv.iobase = 0;
+#ifdef DEBUG
+ logmsg("Assign %s%d, io 0x%x, mem 0x%lx, %d bytes, irq %d, flags %x\n",
+ drv.name, drv.unit, drv.iobase, drv.mem, drv.memsize, sp->irq, drv.flags);
+#endif
+
+ /*
+ * 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", sp->card->manuf);
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.sbin/pccard/pccardd/cardd.h b/usr.sbin/pccard/pccardd/cardd.h
new file mode 100644
index 0000000..ec2ed02
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.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.
+ *
+ * $Id: cardd.h,v 1.11 1998/02/27 08:19:24 hosokawa Exp $
+ *
+ * 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;
+ struct driver *driver;
+ int irq;
+ int flags;
+ char inuse;
+};
+
+struct card {
+ struct card *next;
+ char *manuf;
+ char *version;
+ int ether; /* For net cards, ether at offset */
+ int reset_time; /* Reset time */
+ struct card_config *config; /* List of configs */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+};
+
+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];
+ unsigned char eaddr[6]; /* If any */
+ struct allocblk io; /* I/O block spec */
+ struct allocblk mem; /* Memory block spec */
+ int irq; /* Irq value */
+};
+
+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 struct driver *drivers; /* List of drivers */
+EXTERN struct card *cards;
+EXTERN bitstr_t *mem_avail;
+EXTERN bitstr_t *io_avail;
+
+/* 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);
+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 */
+void readfile(char *);
+
+#define IOPORTS 0x400
+#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)
diff --git a/usr.sbin/pccard/pccardd/file.c b/usr.sbin/pccard/pccardd/file.c
new file mode 100644
index 0000000..e638a4e
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/file.c
@@ -0,0 +1,635 @@
+/*
+ * 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[] =
+ "$Id: file.c,v 1.15 1998/04/25 17:52:15 hosokawa Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cardd.h"
+
+static FILE *in;
+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 */
+ 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
+
+struct flags {
+ char *name;
+ int mask;
+};
+
+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 struct allocblk *ioblk_tok(int);
+static struct allocblk *memblk_tok(int);
+static struct driver *new_driver(char *);
+
+static void addcmd(struct cmd **);
+static void parse_card(void);
+
+/*
+ * Read a file and parse the pcmcia configuration data.
+ * After parsing, verify the links.
+ */
+void
+readfile(char *name)
+{
+ struct card *cp;
+
+ in = fopen(name, "r");
+ if (in == 0) {
+ logerr(name);
+ die("readfile");
+ }
+ 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);
+ }
+}
+
+static void
+parsefile(void)
+{
+ int i;
+ struct allocblk *bp;
+
+ pushc = 0;
+ lineno = 1;
+ for (;;)
+ switch (keyword(next_tok())) {
+ case KWD_EOF:
+ /* EOF */
+ return;
+ case KWD_IO:
+ /* reserved I/O blocks */
+ 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:
+ /* reserved irqs */
+ while ((i = irq_tok(0)) > 0)
+ pool_irq[i] = 1;
+ pusht = 1;
+ break;
+ case KWD_MEMORY:
+ /* reserved memory blocks. */
+ 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();
+ break;
+ default:
+ error("syntax error");
+ pusht = 0;
+ break;
+ }
+}
+
+/*
+ * Parse a card definition.
+ */
+static void
+parse_card(void)
+{
+ char *man, *vers;
+ struct card *cp;
+ int i;
+ struct card_config *confp, *lastp;
+
+ man = newstr(next_tok());
+ vers = newstr(next_tok());
+ cp = xmalloc(sizeof(*cp));
+ cp->manuf = man;
+ cp->version = vers;
+ cp->reset_time = 50;
+ cp->next = cards;
+ cards = cp;
+ for (;;) {
+ switch (keyword(next_tok())) {
+ case KWD_CONFIG:
+ /* config */
+ i = num_tok();
+ 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;
+
+ /*
+ * 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 */
+ cp->ether = num_tok();
+ if (cp->ether == -1) {
+ error("illegal ether address offset");
+ cp->ether = 0;
+ }
+ break;
+ case KWD_INSERT:
+ /* insert */
+ addcmd(&cp->insert);
+ break;
+ case KWD_REMOVE:
+ /* remove */
+ addcmd(&cp->remove);
+ 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);
+ 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;
+
+ 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;
+
+ 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 '?'.
+ */
+static int
+irq_tok(int force)
+{
+ int i;
+
+ if (strcmp("?", 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);
+}
+
+/*
+ * 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);
+ 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 (get() != '\n');
+ return (last_char = '\n');
+ }
+ 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 (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));
+}
diff --git a/usr.sbin/pccard/pccardd/pccard.conf.5 b/usr.sbin/pccard/pccardd/pccard.conf.5
new file mode 100644
index 0000000..68a8421
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccard.conf.5
@@ -0,0 +1,202 @@
+.\"
+.\" 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.
+.\"
+.Dd November 2, 1994
+.Dt PCCARD.CONF 5
+.Os FreeBSD
+.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
+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
+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
+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
+.Dl config Ar index driver interrupt [ flags ]
+.Dl ether Ar offset
+.Dl insert Ar command
+.Dl remove Ar command
+.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 and card version that
+is used to match the values from the card's CIS memory. 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.
+.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
+.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.
+.Sh EXAMPLE
+A typical configuration file may appear thus:
+.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/pccard.conf -compact
+.It Pa /etc/pccard.conf
+The
+.Xr pccardd 8
+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..5ed9f56
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.8
@@ -0,0 +1,158 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: pccardd.8,v 1.10 1998/03/23 08:25:40 charnier Exp $
+.\"
+.Dd November 1, 1994
+.Dt PCCARD 8
+.Os FreeBSD
+.Sh NAME
+.Nm pccardd
+.Nd PC-CARD (PCMCIA) management daemon
+.Sh SYNOPSIS
+.Nm pccardd
+.Op Fl d
+.Op Fl v
+.Op Fl f Ar configfile
+.Sh DESCRIPTION
+.Nm Pccardd
+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/pccard.conf )
+and scans the available PC-CARD slots for cards.
+.Nm Pccardd
+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
+.Nm Pccardd
+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 primary reason
+is that once a driver is associated with a card, the
+driver's
+.Fn probe
+routine has been called, and this usually causes driver specific
+data areas to be initialized with the I/O ports or memory resources
+allocated to the card. Most drivers are not designed to be
+disassociated from the hardware and then reassociated with different
+parameters. This will change significantly when loadable kernel
+modules are supported.
+.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 f Ar configfile
+Specifies a different configuration file to be used
+in placed of the default file
+.Pa /etc/pccard.conf .
+The file format is detailed in
+.Xr pccard.conf 5 ,
+and lists the PC-CARD cards recognized by
+.Nm pccardd ,
+and the kernel drivers and devices that are used to
+interface to the card.
+.Pp
+.Sh FILES
+.Bl -tag -width /etc/pccard.conf -compact
+.It Pa /etc/pccard.conf
+.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
+.Nm Pccardd
+can set up card parameters, but cannot guarantee that
+particular drivers can work with the card.
+.Pp
+Since
+.Nm FreeBSD
+does not currently support loadable kernel modules, any
+.Em irq
+specifications in the configuration file must match the
+.Nm config
+entry for the kernel.
+.Pp
+Removing cards may cause problems if system resources
+have been associated with the card, such as network
+mounted filesystems.
diff --git a/usr.sbin/pccard/pccardd/pccardd.c b/usr.sbin/pccard/pccardd/pccardd.c
new file mode 100644
index 0000000..3525fac
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.c
@@ -0,0 +1,106 @@
+/*
+ * 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[] =
+ "$Id: pccardd.c,v 1.1 1998/02/27 08:19:25 hosokawa Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+#define EXTERN
+#include "cardd.h"
+
+char *config_file = "/etc/pccard.conf";
+
+/*
+ * mainline code for cardd
+ */
+int
+main(int argc, char *argv[])
+{
+ struct slot *slots, *sp;
+ int count, dodebug = 0;
+ int doverbose = 0;
+
+ while ((count = getopt(argc, argv, ":dvf:")) != -1) {
+ switch (count) {
+ case 'd':
+ setbuf(stdout, 0);
+ setbuf(stderr, 0);
+ dodebug = 1;
+ break;
+ case 'v':
+ doverbose = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ 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 */
+
+ /* Mem allocation done in MEMUNIT units. */
+ mem_avail = bit_alloc(MEMBLKS);
+ readfile(config_file);
+ if (doverbose)
+ dump_config_file();
+ log_setup();
+ if (!dodebug)
+ if (daemon(0, 0))
+ die("fork failed");
+ slots = readslots();
+ if (slots == 0)
+ die("no PC-CARD slots");
+ logmsg("pccardd started", NULL);
+ for (;;) {
+ fd_set mask;
+ FD_ZERO(&mask);
+ for (sp = slots; sp; sp = sp->next)
+ FD_SET(sp->fd, &mask);
+ count = select(32, 0, 0, &mask, 0);
+ if (count == -1) {
+ logerr("select");
+ continue;
+ }
+ if (count)
+ for (sp = slots; sp; sp = sp->next)
+ if (FD_ISSET(sp->fd, &mask))
+ slot_change(sp);
+ }
+}
diff --git a/usr.sbin/pccard/pccardd/readcis.c b/usr.sbin/pccard/pccardd/readcis.c
new file mode 100644
index 0000000..5b9dbb5
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.c
@@ -0,0 +1,617 @@
+/*
+ * 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[] =
+ "$Id: readcis.c,v 1.6.2.5 1998/04/20 05:41:30 nate Exp $";
+#endif /* not lint */
+
+#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 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 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},
+ {"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, 255},
+ {"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},
+ {"Organisation", 0x46, 255},
+ {"Terminator", 0xFF, 255},
+ {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;
+ }
+ }
+ 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->manuf = *cp->vers = *cp->add_info1 = *cp->add_info2 = '\0';
+ cp->maj_v = *p++;
+ cp->min_v = *p++;
+ strncpy(cp->manuf, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->vers, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->add_info1, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->add_info2, p, CIS_MAXSTR - 1);
+}
+
+/*
+ * 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;
+ 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 i;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+
+ p1 = p + 1;
+ cp->last_config = *p1++ & 0x3F;
+ u.l = 0;
+ for (i = 0; i <= (*p & 3); i++)
+ u.b[i] = *p1++;
+ cp->reg_addr = u.l;
+ cp->ccrs = *p1;
+}
+
+/*
+ * 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;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+ struct cis_config *conf, *last;
+ struct cis_memblk *mem;
+ unsigned char feat;
+ struct cis_memblk *lastmem = 0;
+
+ 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;
+ if (*p & 0x40)
+ cp->def_config = conf;
+ if (*p++ & 0x80)
+ p++;
+ feat = *p++;
+ 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;
+ if (*p++ & CIS_IO_RANGE) {
+ struct cis_ioblk *io, *last = 0;
+ i = CIS_IO_ADSZ(*p);
+ j = CIS_IO_BLKSZ(*p++);
+ for (x = 0; x < conf->io_blks; x++) {
+ io = xmalloc(sizeof(*io));
+ if (last)
+ last->next = io;
+ else
+ conf->io = io;
+ last = io;
+ u.l = 0;
+ switch (i) {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ break;
+ }
+ io->addr = u.l;
+ u.l = 0;
+ switch (j) {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ u.l++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.l++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ u.l++;
+ break;
+ }
+ io->size = u.l;
+ }
+ }
+ }
+ 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 = (p[1] << 8) | p[0];
+ p += 2;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case 0:
+ break;
+ case 1:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0]) << 8;
+ break;
+ case 2:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0]) << 8;
+ conf->mem->address = ((p[3] << 8) | p[2]) << 8;
+ break;
+ case 3:
+ conf->memspace = 1;
+ x = *p++;
+ conf->memwins = CIS_MEM_WINS(x);
+ for (i = 0; i < conf->memwins; i++) {
+ mem = xmalloc(sizeof(*mem));
+ if (i == 0)
+ conf->mem = mem;
+ else
+ lastmem->next = mem;
+ lastmem = mem;
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_LENSZ(x); j++)
+ u.b[j] = *p++;
+ mem->length = u.l << 8;
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->address = u.l << 8;
+ if (x & CIS_MEM_HOST) {
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->host_address = u.l << 8;
+ }
+ }
+ break;
+ }
+ if (feat & 0x80) {
+ 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 = tp->data[0] |
+ (tp->data[1] << 8) |
+ (tp->data[2] << 16) |
+ (tp->data[3] << 24);
+#ifdef DEBUG
+ printf("Checking long link at %ld (%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 %ld (%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;
+
+ /* 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 (read_attr(fd, &length, 1) != 1) {
+ warn("CIS len read");
+ break;
+ }
+ total++;
+ tp->length = length;
+#ifdef DEBUG
+ printf("Tuple code = 0x%x, len = %d\n", code, length);
+#endif
+ if (length == 0xFF) {
+ length = tp->length = 0;
+ code = CIS_END;
+ }
+ if (length != 0) {
+ total += length;
+ tp->data = xmalloc(length);
+ if (read_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 (tinfo == 0 || (tinfo->length != 255 && tinfo->length != length)) {
+ printf("code %s ignored\n", tuple_name(code));
+ tp->code = CIS_NULL;
+ }
+ if (tl->tuples == 0)
+ 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 (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);
+ printf("Code %d not found\n", code);
+ 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..024b9dc
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.h
@@ -0,0 +1,133 @@
+/*
+ * 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.
+ *
+ * $Id: readcis.h,v 1.8 1997/02/22 16:09:00 peter Exp $
+ */
+
+#define CIS_MAXSTR 30
+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[CIS_MAXSTR];
+ char vers[CIS_MAXSTR];
+ char add_info1[CIS_MAXSTR];
+ char add_info2[CIS_MAXSTR];
+ unsigned char maj_v, min_v;
+ unsigned char last_config;
+ unsigned char ccrs;
+ unsigned long reg_addr;
+ struct dev_mem attr_mem;
+ struct dev_mem common_mem;
+ struct cis_config *def_config;
+ struct cis_config *conf;
+};
+
+void *xmalloc(int);
+void dumpcis(struct cis *);
+void freecis(struct cis *);
+struct cis *readcis(int);
+
+char *tuple_name(unsigned char);
diff --git a/usr.sbin/pccard/pccardd/util.c b/usr.sbin/pccard/pccardd/util.c
new file mode 100644
index 0000000..a9aa48b
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/util.c
@@ -0,0 +1,272 @@
+/*
+ * 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[] =
+ "$Id: util.c,v 1.11 1997/11/19 02:31:41 nate Exp $";
+#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);
+ vsprintf(s, fmt, ap);
+
+ if (do_log)
+ syslog(LOG_ERR, 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 count)
+{
+ int i;
+ int found = 0;
+
+ for (i = 0; i < nbits; i++)
+ if (bit_test(nm, i)) {
+ if (++found == count)
+ return (i - count + 1);
+ } else
+ found = 0;
+ 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, size / MEMUNIT);
+ if (i < 0)
+ return (0);
+ bit_nclear(mem_avail, i, size / MEMUNIT);
+ 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..b608668
--- /dev/null
+++ b/usr.sbin/pciconf/Makefile
@@ -0,0 +1,7 @@
+# $ANA: Makefile,v 1.1.1.1 1996/09/25 21:12:57 wollman Exp $
+
+PROG= pciconf
+MAN8= 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..34fc83a
--- /dev/null
+++ b/usr.sbin/pciconf/pathnames.h
@@ -0,0 +1 @@
+#define _PATH_DEVPCI "/dev/pci"
diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
new file mode 100644
index 0000000..2fef29e
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.8
@@ -0,0 +1,193 @@
+.\" $Id: pciconf.8,v 1.4 1998/11/12 00:22:30 ken Exp $
+.\" 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.
+.\"
+.Dd February 7, 1997
+.Dt PCICONF 8
+.Os FreeBSD
+.Sh NAME
+.Nm pciconf
+.Nd diagnostic utility for the PCI bus
+.Sh SYNOPSIS
+.Nm pciconf Fl l
+.Nm pciconf Fl a Ar selector
+.Nm pciconf Fl r Ar selector
+.Op Fl b | Fl h
+.Ar reg
+.Nm pciconf Fl w Ar selector
+.Op Fl b | Fl h
+.Ar reg value
+.Sh DESCRIPTION
+The
+.Nm
+command provides a command line interface to the functionality provided by
+.Pa /dev/pci Ns 's
+.Xr ioctl 2
+interface.
+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
+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
+The
+.Fl l
+option is the only one available to non-root users.
+All other invocations of
+.Nm
+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 reg
+of device
+.Ar selector
+and prints out its value in hexadecimal.
+The
+.Fl w
+option writes the
+.Ar value
+into a configuration space register at byte offset
+.Ar reg
+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 SEE ALSO
+.Xr ioctl 2 ,
+.\" .Xr pci 4 ,
+.Xr kldload 8
+.Sh HISTORY
+The
+.Nm
+command appeared first in
+.Fx 2.2 .
+The
+.Fl a
+option was added for
+.Tn PCI
+KLD support in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+facility was written by
+.An Stefan Esser
+and
+.An Garrett Wollman .
+.Sh BUGS
+The
+.Fl b
+and
+.Fl h
+options are implemented in
+.Nm pciconf ,
+but not in the underlying
+.Fn ioctl .
+.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..1d7954d
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.c
@@ -0,0 +1,275 @@
+/*
+ * 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[] =
+ "$Id: pciconf.c,v 1.6 1998/09/15 08:21:13 gibbs Exp $";
+#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 <pci/pcivar.h>
+#include <pci/pci_ioctl.h>
+
+#include "pathnames.h"
+
+static void list_devs(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 exitstatus = 0;
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pciconf -l",
+ " pciconf -a sel",
+ " pciconf -r [-b | -h] sel addr",
+ " pciconf -w [-b | -h] sel addr [value]");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int listmode, readmode, writemode, attachedmode;
+ int byte, isshort;
+
+ listmode = readmode = writemode = attachedmode = byte = isshort = 0;
+
+ while ((c = getopt(argc, argv, "alrwbh")) != -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;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((listmode && optind != argc)
+ || (writemode && optind + 3 != argc)
+ || (readmode && optind + 2 != argc)
+ || (attachedmode && optind + 1 != argc))
+ usage();
+
+ if (listmode) {
+ list_devs();
+ } 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(void)
+{
+ int fd;
+ struct pci_conf_io pc;
+ struct pci_conf conf[255], *p;
+ int none_count = 0;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 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%08lx "
+ "chip=0x%08lx rev=0x%02x hdr=0x%02x\n",
+ (p->pd_name && *p->pd_name) ? p->pd_name :
+ "none",
+ (p->pd_name && *p->pd_name) ? 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);
+ }
+ } while (pc.status == PCI_GETCONF_MORE_DEVS);
+
+ close(fd);
+}
+
+static struct pcisel
+getsel(const char *str)
+{
+ char *ep = (char*) str;
+ struct pcisel sel;
+
+ 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 || ep == str)
+ errx(1, "cannot parse selector %s", str);
+ return sel;
+}
+
+static void
+readit(const char *name, const char *reg, 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;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCREAD, &pi) < 0)
+ err(1, "ioctl(PCIOCREAD)");
+
+ printf("0x%08x\n", pi.pi_data);
+}
+
+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..1a3be34
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile
@@ -0,0 +1,7 @@
+SUBDIR= keycap cursor fontedit fonts kcon loadfont scon \
+ userkeys vttest ispcvt mcon
+SUBDIR+= vgaio kbdio set2061
+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..8ed9829
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile.inc
@@ -0,0 +1,9 @@
+# $Id: Makefile.inc,v 1.4 1998/05/31 11:41:53 bde Exp $
+
+FONTDIR = /usr/share/misc/pcvtfonts
+
+MAN3EXT= 3
+MAN5EXT= 5
+MAN8EXT= 8
+
+.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/BugList b/usr.sbin/pcvt/Misc/Doc/BugList
new file mode 100644
index 0000000..9c8feef
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/BugList
@@ -0,0 +1,62 @@
+
+List of known Bugs Last Edit-Date: [Sun Mar 5 13:03:51 1995]
+================================================================================
+
+
+Description: Fixed/done by/date
+------------------------------------------- --------------------------------
+Util/keycap/man5/keycap.5 does STILL not
+build correctly when doing a make obj.
+Will there ever be a solution ... :-)
+------------------------------------------- --------------------------------
+NetBSD PR #404: Meta-Control-Space broken
+with PCVT_META_ESC and PCVT_NULLCHARS set
+Meta-Control-Space should (theoretically)
+send ESC NUL if PCVT_NULLCHARS and
+PCVT_META_ESC are defined; in reality, it
+just sends ESC. Because of the grody
+encoding hack used by sgets() to implement
+PCVT_NULLCHARS (if the first character is
+a null, send it), there isn't a trivial
+one-line fix.
+How-To-Repeat: in a kernel with
+PCVT_NULLCHARS and PCVT_META_ESC, type a
+M-C-SPC to emacs.
+------------------------------------------- --------------------------------
+when auto switching to vt0 is enabled by
+PCVT_SW0CNOUTP and the screen is switched
+from an X-vt to screen 0, the video mem
+is irrecoverably destroyed - no data loss
+------------------------------------------- --------------------------------
+Altgr+Shift+key is not separately They way the keyboard mapping
+mappable. is done is subject to a sub-
+ stantial rewrite in a future
+ release
+------------------------------------------- --------------------------------
+132 column mode not working with #9 GXE
+(S3-based) (sorry, i don't have one -hm)
+(see discussion of 132 column mode in the
+ NotesAndHints file!)
+------------------------------------------- --------------------------------
+WD90Cxx chip detection fails to detect
+chips other than C00,C10 and C11. C30
+chips are detected as C11s ....
+------------------------------------------- --------------------------------
+Xfree86 2.0 locks the console when started
+under NetBSD 0.9. SuperProbe and X both
+show process status "DE+" in ps. Work-
+around is to disable PCVT_USL_VT_COMPAT.
+------------------------------------------- --------------------------------
+On one keyboard, if a "Lock" key is pressed
+the leds do not get updated and the key-
+board hangs.
+------------------------------------------- --------------------------------
+HP function key labels code needs to set
+the user defined fkey string somehow!
+------------------------------------------- --------------------------------
+Video 7 1024i not fully supported yet, has
+sometimes problems with some chars to displ
+------------------------------------------- --------------------------------
+The HP-Escape sequences need to be FULLY 28.12.93, -hm did a bit to supp-
+implemented port elm. Needs MORE work !
+------------------------------------------- --------------------------------
diff --git a/usr.sbin/pcvt/Misc/Doc/ChangeLog b/usr.sbin/pcvt/Misc/Doc/ChangeLog
new file mode 100644
index 0000000..2e8547e
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/ChangeLog
@@ -0,0 +1,899 @@
+ChangeLog last edit-date: [Thu Apr 6 10:52:50 1995]
+================================================================================
+
+Changes 3.10 -> 3.20 April 1995
+--------------------------------------------------------------------------------
+
+- bugfix from Thomas Eberhardt: the force 24 lines enable function key
+ label was not updated when changing force 24 lines mode with
+ 'scon -f on|off', this is now fixed.
+
+- bugfix from John Kohl fixing divide by zero problem in pcvt_ext.c when
+ ringing the bell and pitch is 0: this avoids an integer divide
+ trap in supervisor mode.
+
+- changed the default behaviour of keyboard controller delay from using
+ delay()/DELAY() to using dummy reads to port 0x84 because i got
+ keyboard hangs in X.
+
+- bugfix: in pcvt_ext.c, MDA state restore when leaving X was lost, should
+ function now again.
+
+- renamed PCVT_XSERVER to XSERVER in ispcvt.c
+
+- bugfix in pcvt_vtf.c: renamed PCVT_USL_COMPAT to PCVT_USL_VT_COMPAT
+
+- added '-d <device>' option to ispcvt and loadfont
+
+- removed PCVT_NEEDPG
+
+- almost completely removed support for 386BSD 0.1 with patchkit 0.2.4
+
+- applied a patch from Joerg providing missing FreeBSD 2.1 functionality and
+ a some minor bugfixes and checks.
+
+- provide a single place PCVT_KBD_DELAY in pcvt_hdr.h. made it configurable
+ via PCVT_PORTIO_DELAY to use either 6 dummy reads from port 0x84
+ or the delay/DELAY function of the operating system. Changed ispcvt
+ to report status of compile time switch.
+
+- release beta 23 --------------------------------------------------------------
+
+- removed some files for 386BSD as there is virtually noone outside
+ using it anymore
+
+- make pcvt_ioctl.h accept "KERNEL" or "_KERNEL" for NetBSD-current
+
+- configuration fix: patch from Rafal Boni for pcvt_vtf.c, he writes:
+ This patch lets one compile pcvt on a system that is lacking XSERVER
+ and UCONSOLE [or either?] in the config file. I found this while
+ trying to build a minimal floppy-based test kernel. The problem is
+ that roll_up and roll_down assume that PCVT_USL_COMPAT is
+ unconditionally set, and hence muck with parts of the video structure
+ that are only there if PCVT_USL_COMPAT is set.
+ (generalized the patch a bit so that fastscroll functionality is
+ not lost in case of PCVT_USL_COMPAT not being defined. -hm)
+
+- bugfix: pcvt_ext.c switch_screen() - when switching to a 25 line charset
+ pure VT emulation screen which has force 24 lines set, then we have
+ to clear the last line on screen (bugreport from Joerg).
+
+- added define PCVT_NONRESP_KEYB_TRY for how many times to try to detect
+ a non-connected keyboard. This was previously set to 100 and
+ Joerg reports a 5+ minutes delay for a 386 booting without keyboard.
+ Set to 25 now in the definition. Also made shure the messages in
+ the corresponding loop display only once. [doreset() in pcvt_kbd.c]
+
+- updated kbdio utility to include the 7 us delay and added a 'what' command
+ detect type of MCA motherboard keyboard controller according to
+ Frank van Gilluwe, "The Undocumented PC", Addison Wesley, pp 273
+
+- "Gateway 2000" problem: after some discussion on the NetBSD port-i386
+ mailing list about hanging keyboards, Martin Husemann sent in a
+ patch where each inb(CONTROLLER_DATA) is now preceeded by a
+ delay(6). This fixed his keyboard problem, the same was reported
+ from Roland McGrath for his Gateway 2000 keyboard/machine.
+
+- patch from John Kohl for usl_vt_ioctl() in file pcvt_ext.c, he writes:
+ This is probably a long standing bug. tsleep() returns ERESTART
+ if it's interrupted. ERESTART is -1, which means that the
+ VT_WAITACTIVE call returns from usl_vt_ioctl() with -1,
+ meaning "not one of my ioctl's".
+
+- patch from Matthieu Herrb for NetBSD-current (post 1.0) support
+
+- patch from Joerg for FreeBSD pre-2.1 support
+
+- added file ToDo in directory Doc
+
+- fix for Util/Makefile.inc.NetBSD-c from Onno
+
+- removing unnecessary Trident support code from pcvt_out.c
+
+- fix from Onno for IST_EDGE in NetBSD-current
+
+- patch from Thomas Eberhardt to fix the bell frequency and duration setting
+ for NetBSD
+
+- some fixes from Onno van der Linden for NetBSD
+
+- added patch for NetBSD-current from Rafal Boni and Lon Willett
+
+- added another mega patch from Lon Willett fixing several bugs:
+
+ 1 -- Misc porting changes to deal with NetBSD-current, including
+ a new Util/Makefile.inc.NetBSD-current.
+
+ 2 -- The Control_R scancodes were being mapped to the Control_L
+ pcvt keynum.
+
+ 3 -- FASTSCROLL/graphics-mode problem: there was a problem where
+ switching from a graphics screen to a text screen would corrupt
+ the text screen's content if it had been scrolling using the
+ FASTSCROLL code. (The problem is the bcopy() from kernel to
+ video memory was using the vs[i].Memory pointer as the source,
+ instead of the vs[i].Crtat pointer). This is fixed in the beta
+ 21 release by just disabling the fast scroll code for screens
+ which aren't active.
+
+ The patch I'm sending you re-enables the fast scroll code on
+ inactive (i.e. in kernel memory) screens, saving a few cpu
+ cycles.
+
+ 4 -- This is a big one: running multiple X servers was not
+ really working. The USL handshaking code was a mess (not very
+ well designed to begin with, but there's not much that can be
+ done about that). I did manage to get it fairly functional, but
+ there's a lot of changes.
+
+- changing example rc.local to take care of good old EGA's
+
+- fixing support for EGA boards in vt_coldinit()
+
+- removing vt100 font files from Util/fonts, updated Manifest
+
+- fixing an obviously longstanding bug in roll_up() / roll_down() which was
+ triggered by Joergs "Crtat" cleanup mega patch.
+
+- removing PCVT_FAKE_SYSCONS10
+
+- removing duplicate names in termcap entries
+
+- fixing vt_coldinit: setting variable "color" for mda/hercules and cga
+ initializing cursor position
+
+- fixing roll_up() and roll_down() for mda/hercules: mda's have ALWAYS
+ just one page of memory, original hercules boards too so they
+ can NEVER use the fastscroll option. SOME hercules are somehow
+ enhanced in that they support 2 pages. pcvt now just supports
+ one page hercules/mda!
+
+- made cleanups to 8x14 high and 8x8 high fonts: adjust the 5 scanline
+ characters to adjust with the corner characters from the low fonts
+
+- applying patch from Joerg which fixes some bugs:
+
+ writing to a tty containing an X server resulted in some stange
+ behaviour depending on the opsys and opsys version used
+
+ the init code preserving the screen contents and cursor shape
+ can only be done on boards allowing the reading of
+ several crtc registers
+
+- removed bcopyb declaration from pcvt_hdr.h for FreeBSD 2.1
+
+- another patch from Lon Willett (willett@math.utah.edu), he writes:
+
+ 1 -- Very minor: One of my previous changes didn't get merged
+ into "pcvt_out.c"; the caclulation of ws_row doesn't account for
+ "force24" at one spot. It's easier to just set it from
+ screen_rows anyway, unless there's something that I didn't
+ understand happening here.
+
+ 2 -- Debugging stuff: I don't know if you care to add it, but I
+ found it useful, so I left it in. I rearranged the
+ PCVT_SHOWKEYS code a little, and added a few lines to show
+ keyboard commands and responses, with special delimiters.
+ [file: pcvt_kbd.c]
+
+ 3 -- "kcon" utility enhancement: I added a "-R" switch to kcon
+ to do a KBDRESET ioctl. [file: Util/kcon/kcon.c]
+
+ 4 -- KBDRESET-ioctl bug: the code was trying to read kbd
+ responses without an spltty(), so naturally it would fail, since
+ pcrint() was grabbing the ACKs. [file: pcvt_kbd.c]
+
+ 5 -- update_led() makes kbd hang: the problem here is that
+ update_led() makes the keyboard generate two KEYB_R_ACK
+ responses, and one of the interrupts sometimes gets lost when
+ there is a lot of other I/O happening. See the comment in the
+ code. My fix seems to work for me, but you may want to handle
+ it some other way. It would probably be quite reasonable to
+ check for lost interrupts periodically anyway, whether
+ update_led() has been called or not. [file: pcvt_kbd.c]
+
+ (NOTE: item 5 has been disabled because it causes older
+ FreeBSD's to panic because of the timer queue not being
+ initialized at the time called. -hm)
+
+- partly rewrote doreset() in pcvt_kbd.c to enable boot procedure to proceed
+ if no (PC) keyboard is found. The current behaviour and implementation
+ is subject to change.
+
+- patch from Thomas Gellekum to Util/keycap/keycap.src
+
+- minor modification to main.c in Util/set2061
+
+- patch from Onno and John Kohl to make pcvt beta 16 work on NetBSD current:
+ 1. pcvt_conf.h
+ _real_ 1.0 ===> NetBSD1_0 == 1
+ current 1.0 (1.0A) ===> NetBSD1_0 == 2
+ 2. pcvt_hdr.h
+ Put pcvt_conf.h before _all_ the #ifdef PCVT_*. This way
+ the #ifdef NetBSDx_y stuff can do its work.
+
+- size of PCVT_BURST was still reported as 1 for FreeBSD 2.0, fixed
+
+- made some adjustments for FreeBSD 2.0 in Util/vgaio
+
+- Another patch from Joerg for FreeBSD 2.0
+
+- included vgaio, kbdio and set2061 into the outer Makefile, this programs
+ are build and cleaned but don't install anything.
+ All program's in the Util dir should now cleanly make <anything> ...
+
+- enable 132 column support for Trident TVGA8900CL, some NetBSDisms
+
+- addend patch from Joerg for pcvt-320b7 to fix FreeBSD 2.0's ttymalloc's
+ encapsulation into #if's. Also new keycap.src entry.
+
+- fixed keyboard status/LED not updated on soft reset emulator
+
+- on 23rd of December i had some spare time, so i added some demos to the
+ Util/demo directory as well a a time-killer program (playvt) to view
+ some of the VT animations and a christmas animation i got from Joerg.
+
+- added a new utility set2061 to program the clock generator on my S3 based
+ ELSA Winner VGA board. This eventually lets me use 132 columns on this
+ board, because i'm now able to program the clock to generate 40MHz
+ on clock output #2.
+
+- debugged winsize patch on FreeBSD 1.1, line discipline open init's the
+ queues, winsize initialization must happen after line disc. open!
+
+- got a bunch of NetBSD PR's from J.T. Conklin:
+
+ PR #214: PCVT treats ctrl-shift exactly like shift for most characters.
+ (fixed by Lon Willet's Mega Patch described below -hm)
+
+ PR #399: If you define PCVT_META_ESC when builting PCVT, Meta-Return
+ sends 0x8d instead of ESC RET.
+ (fixed by applying the patch from Bill Sommerfeld. -hm)
+
+ PR #400: pcvt sometimes gets confused about window size
+ (fixed by applying the patch from Bill Sommerfeld. -hm)
+
+ PR #404: Meta-Control-Space broken with PCVT_META_ESC and PCVT_NULLCHARS
+ (put into "BugList" file, no solution yet .. -hm)
+
+ PR #488: pcvt can loose keyboard control if you start an xserver from
+ an xterm
+ (already fixed by a patch from John Kohl, see below -hm)
+
+ PR #580: NetBSD i386/pcvt bugs/enhancements; fixes included
+ (already integrated the patch from Lon Willet, see below -hm)
+
+- on coldinit, if FAT_CURSOR is defined, the old large blockcursor is used
+
+- enhanced the vgaio output in an attempt to make 132 column mode work
+ for my S3 board ....
+
+- applied patch from Thomas Gellekum: install instructions for FreeBSD 2.0,
+ patch to fontedit to compile under FreeBSD 2.0, an addition to
+ keycap.src and a fix for kbdio
+
+- changed installation instructions to reflect recent changes, removed
+ instructions for FreeBSD 1.0 and added instructions for FreeBSD 2.0
+
+- upgraded all utilities version strings to 3.20
+
+- new example of rc.local
+
+- changing timeout()/untimeout() function parameter type to TIMEOUT_FUNC_T
+ definition in pcvt_hdr.h
+
+- changed install instructions to reference Etc dir instead of Doc dir for
+ several files which were moved from Doc to Etc.
+
+- new parameter -d for the cursor utility, updated cursor manpage
+
+- applying a patch from Lon Willett, willett@math.utah.edu which fixes
+ several bugs and provides some enhancements. Lon writes:
+
+ 1 -- The displayed cursor is not updated during kernel I/O,
+ because async_update() doesn't get called. This is merely ugly
+ while the system startup messages are being displayed, but it is
+ positively annoying when using the kernel debugger.
+
+ 2 -- CONTROL-SHIFT-<KEY> is taken to be the same as SHIFT-<KEY>.
+ It should be interpretted as CONTROL-<KEY>, or even have its own
+ binding. One of the lines below fixes it in the former way.
+ This is especially bad (i.e. clearly buggy) when I type
+ CONTROL-SHIFT-<6/^> to get a "Control-^", and instead get a "^".
+
+ 3 -- The "special" region at the bottom of the screen, i.e. the
+ function-key-labels/status-line in HPVT mode, and the blank line
+ in FORCE24LINES mode, do not get properly cleared/updated.
+
+ 4 -- When switching screen sizes/modes, the FORCE24LINES setting
+ is not always handled.
+
+ 5 -- The PCVT_VT220KEYB functions are missing some features.
+ The control key settings are bug fixes; they apply only if
+ system function key labels are on, and match the labels. The
+ shift key definitions are enhancements.
+
+ The new keys (previously undefined) are:
+
+ Control-F1 -- toggle 80/132 columns
+ Control-F2 -- soft reset emulator
+ Control-F3 -- toggle force 24 lines mode
+ Control-F4 -- toggle keyboard debugging
+
+ 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)
+
+ 6 -- [Enhancement] The initial startup screen is cleared, and
+ the screen gets cleared when changing modes/sizes. I don't like
+ this. I want a chance to see bootstrap loader messages, and I
+ also want the command "scon -d/dev/ttyv0 -H -s28" in my
+ rc.local, but then I lose various messages from daemons, etc
+ (which aren't always logged to syslog). So I have fixed the
+ code so that it tries to preserve the contents of the screen
+ when starting up, and when changing modes/sizes.
+
+ 7 -- [Enhancement?] I added code to preserve the initial cursor
+ shape at startup. This isn't really necessary, since the
+ "cursor" command can be used to set it to your preference; but
+ it seems like this is the preferable policy to use for the
+ default.
+
+- applying patches from Onno van der Linden and Martin Husemann: adding
+ support for post 1.0 NetBSD-current and adding PCVT_NO_LED_UPDATE
+
+- patch from Joerg Wunsch: support for FreeBSD 2.0 and better support
+ for serial console coexistence
+
+- bugreport from Michael Havemester regarding PCVT_NOFASTSCROLL: it was
+ not enabled in pcvt_vtf.c, fixed.
+
+- renamed pcvt_disable_intr and pcvt_enable_intr to PCVT_DISABLE_INTR and
+ PCVT_ENABLE_INTR (Joerg)
+
+- attempt to automatically compile time configure in pcvt_conf.h, how
+ does FreeBSD do this ? should work for NetBSD (from Onno v.d. Linden)
+
+- split off pcvt_kbd.h from pcvt_kbd.c, file > 100k
+
+- split off pcvt_config.h from pcvt_hdr.h. All compile time configurations
+ should be put into pcvt_config.h now.
+
+- moved version definitions to begin of file in pcvt_ioctl.h and pcvt_hdr.h
+
+- fixing NetBSD #ifdef in pcvt_drv.c
+
+- fixed bug in scon. when doing "scon -c <num>" the destination screen was
+ cleared, this is fixed now by properly setting "force_24lines"
+
+- added kbdio utility from Joerg into Util directory
+
+- changed font dir from /usr/share/misc/vgafonts to /usr/share/misc/pcvtfonts
+
+- Fix for Gateway 2000 keyboard problems from Brian Moore
+
+- Starting to implement more keymoard mapping layers in pcvt_kbd.c. This
+ section is a building site, enclosed in NOT_YET_DEF ifdef's
+
+- VT_SETMODE ioctl patch from John Kohl <jtk@kolvir.blrc.ma.us>, see text
+ from John in the source file pcvt_ext.c
+
+- patch from Joerg for kbd_emulate_pc(), intro of timeout
+
+- fixed bug in soft/hard reset and real system fkey labels
+
+- added patch from Joerg for FreeBSD 1.1.5.1R
+
+- fixed both SR and SF entries in termcap database file
+
+- removed SR entry in termcap database file (bugreport from John Perry)
+
+- bugfix in cirrus 132 column switching got from Onno/Charles
+
+- french keycap database entry from Matthieu Herrb
+
+- cleanup patch from Joerg for Util/demo/Makefile und Util/fonts/Makefile
+
+
+Changes 3.00 -> 3.10 June 1994
+--------------------------------------------------------------------------------
+
+- patch from Joerg correcting my assumed timeout/untimeout FreeBSD casts
+
+- adjusted some #if's while validating NetBSD 0.9 and NetBSD-current
+
+- rolled in Joerg's changes for a pre-1.1.5 (?) FreeBSD-current
+
+- Util/fonts: install only the necessary fontfiles, vt100 not used anymore
+
+- updated and commented Etc/rc.local and updated to more recent /etc/ttys files
+
+- updated all installation instructions and changed their names
+
+- documenting more PCVT_ options in pcvt_hdr.h
+
+- adding CONF_ options to driver config ioctl and upgrading ispcvt
+
+- commenting the source, housekeeping, step up to 3.10 beta 2
+
+- casting timeout()/untimeout() for FreeBSD 1.1R :-(
+
+- unprotecting vgapage() routine with #if !PCVT_KBD_FIFO
+
+- adding Michael Havemester's keyboard fifo diffs to source tree
+
+- pcvt_drv.c: made declaration of Crtat global for this file for NetBSD
+
+- changing Util/fonts/Makefile and Util/demo/Makefile to run in all
+ supported environments
+
+- moving inline from pcvt_vtf.h into pcvt_hdr.h, i still feel that this is
+ not the best solution, but i got annoyed by the 10 line pcvt_vtf.h
+
+- bugfix, in hpmode, clear last 4 lines when 28 column mode and force
+ 24 lines are true
+
+- applying patch from Thomas Gellekum <thomas@ghpc8.ihf.rwth-aachen.de>:
+ - discard escape sequences ESC space F and ESC space G
+ - discard escape sequences DECELR = enable locator report
+ and DECSLE = select type of locator event
+ - disable scrolling when writing outside the scrolling region
+ at an absolute position.
+
+ [ pcvt now seems to be "VAX resistant" :-) ]
+
+- optional switch to screen 0 can be done on kernel/console output
+
+- fixed kernel output cursor positioning
+
+- removed PCVT_FORCE8BIT
+
+- pcvt_ext.c, changed screen switching bcopyb's to bcopy's
+
+- new patch from Onno to support all BIOS versions of the Cirrus chipset.
+
+- pcvt_x_hook() has to care about fkey labels now
+
+- updated Doc/NotesAndHints and Doc/BugList
+
+- applied patch from Onno van der Linden for updated Cirrus chipset support
+
+- updated Doc/Bibliography
+
+- removed paranoid delay()/DELAY() from vga_test()
+
+- added prototype ttrstrt() into pcvt_drv.c for NetBSD 0.9
+
+- made INSTALLATION.NetBSD and INSTALLATION.NetBSD.bundled from mycrofts base
+
+- Charles Hannum took Michael Havemesters speedup modifications, made some
+ further enhancements and after adding support for pcvt, put it into
+ the NetBSD-current tree. THANK YOU, Charles !
+ The NetBSD version of May 20th '94 was taken as the base for 3.10.
+
+
+Changes 2.20 -> 3.00 March 1994
+--------------------------------------------------------------------------------
+
+- Release 3.00
+
+- last minute patch from Joerg (pcvt_hdr.h, BugList, NotesAndHints)
+
+- included speedup patch from Michael Havemester as Etc/LAST-MINUTE
+
+- updated Doc/pcvt.4
+
+- removed bug in Util/ispcvt/Makefile which caused ispcvt to be installed
+ into /usr/sbin and /usr/local/bin
+
+- split ioctl VGAPCVTID into two: intro of ioctl VGAPCVTINFO for compile
+ time options only.
+ VGAPCVTID is now frozen for identification purposes (XFree86 3.0)
+ updated Util/ispcvt for the above mentioned changes
+
+- issued patch to upgrade beta14 to beta16
+
+- bugfix: when scrolling up (bcopy) is interrupted by a keystroke requesting
+ a change of the current screen, the "new" screen is scrolled up. Fixed
+ in pcvt_kbd.c and pcvt_out.c (check_scroll)
+
+- fixed bug "Jumping through vt's with ALT-F12 does jump over vt0"
+
+- PCVT_PCBURST intro, update of ioctl and Util/ispcvt
+
+- NetBSD speedup patch from Michael Havemester (factor 6-10)
+
+- issued patch to upgrade beta14 to beta15 (never officially announced)
+
+- added patch for ttioctl parameters NetBSD-current 12 Feb 94 from Michael
+ Havemester in pcvt_drv.c
+
+- INSTALLATION.xxx(x)BSD upgraded to reflect changes in Util and NetBSD-current
+
+- struct pcvtid changed to hold the value of PCVT_xxx(x)BSD, ispcvt upgraded
+
+- applied patch from Szabolcs Szigeti for 132 column operation for Trident
+ TVGA 8900B and TVGA8900C based boards
+
+- PCVT_NETBSD can/must now be 1 or 09 for Release 0.9 and > 09 for current
+
+- applied patches from John Brezak and Szabolcs Szigeti for recent
+ NetBSD-current changes (syscframe -> trapframe)
+
+- in Util, removed Makefile.inc. Make Makefile.inc.FreeBSD and
+ Makefile.inc.NetBSD and added a check to the toplevel and every
+ other Makefile in this part of the tree. sigh ...
+
+- patch from Joerg for pcvt.4 Makefile and Debugger in FreeBSD
+
+- issued pcvt-beta14, code freeze for 3.00 release, just bugfixes now
+
+- INSTALLATION.FreeBSD and a small cleanup patch from Joerg
+
+- large patch from Joerg to get pcvt FreeBSD-current compliant
+
+- permission/owner cleanup, files:664, dirs:775, user:root, group:wheel
+
+- got rid of the verbose error message when installing in Util/fonts
+
+- new make-method in Util/kcon to workaround make portability problems
+
+- NetBSD-current detection workaroundaroundaround for NEW_AVERRUNNABLE in
+ pcvt_header.h
+
+- MONO_BUF and COLOR_BUF now ifndef'ed
+
+- fixed typo in pcvt_kbd.h in cfkey11() and cfkey12()
+
+- machine/pio.h must be included in pcvt_hdr.h for recent NetBSD-current
+
+- protected every tsleep call with an "if(curproc)" otherwise there is
+ chance to panic the system (Joerg has an idea why ...)
+
+- more fixes from Joerg: keyboard scansets fixed, X server is now aware
+ of redefined keys (ioctl implemented)
+
+- patch from Heiko Rupp, configuration with XSERVER not defined didn't compile
+
+- new version of vgaio from Joerg installed
+
+- large keyboard cleanup patch from Joerg merged in
+
+- bugfixes from Joerg: fix crash on not-open vt, remove pcxint, add option
+ PCVT_INHIBIT_NUMLOCK (for notebook owners :-), support for EGA/VGA
+ fonts with up to 32 scanlines.
+
+- included vgaio, a program to read/write vga register values from Joerg.
+
+- included mcon, the keyboard mouse emulator control program from Joerg.
+
+- applied averrunnable patch to satisfy the most recent NetBSD-current.
+
+- fixing cursor not updated bug if usl/vt server is running on vt0
+
+- polished some chars in Util/fonts/vt220l.810, fixed all permissions in
+ the uuencoded fontfiles.
+
+- debugged the EGA/VGA curses based font editor 'fed' in Util/fed. It seems
+ it's working ok now now.
+
+- updated copyright header files
+
+- synchronize asynchronous cursor position update with having a valid
+ (new) cursor (row) position from sputc(). (Otherwise a cursor
+ would appear temporarily in the first position of the first
+ function key label in the HP mode)
+
+- updated screeninfo ioctl and scon to report the monitor type
+
+- added file Doc/Notes for random notes and hints for pcvt-users.
+
+- enhanced the pcvtid-ioctl and the ispcvt(8) utility to print out the
+ values of all "PCVT_XXXXXX" compile time options.
+
+- included work from Joerg to convert all ifdef's to if's, to be able
+ to compile various configurations of pcvt without changing
+ options in the header file.
+
+- included (currently untested !) patch for a keyboard mouse emulator
+ from Joerg. (he got problems after he bought a notebook with
+ just one serial port, which he wanted to use for slip ...)
+
+- screensaver reset is now also done asynchronously to get more speed. the
+ function average() was renamed to async_update().
+
+- cursor position update and cursor position display in HP mode is now done
+ asynchronously in function average() in pcvt_sup.c. the function
+ update_cursor does no longer exist. this gave about 10..30% increase
+ in speed depending on the data cat'ed (termcap, kernel, 1Mb nulls)
+
+- HP function key emulation processing debugged, this has to be rewritten
+ to use a stack and a new parser. elm -K now works a bit more, the
+ display is not garbled anymore, but fkey strings do not work.
+
+- pcvt_vtf.c split off from pcvt_out.c, file got > 100k. Checked all
+ forward declarations in header and source files
+
+- 132 column support for Cirrus Logic CL-GD542X chipsets written by
+ Onno van der Linden, c/o vdlinden@fwi.uva.nl
+
+- keyboard scancode display (#define PCVT_SHOWKEYS)
+
+- printscreen keycode fix form Onno van der Linden
+
+- Util/Makefile.inc added .depend dependency for make depend
+
+- pcvt_ioctl.h is now installed into /usr/include/machine.
+
+- keyboard scancode 1 is now used by default, perhaps it cures some problems
+
+- support for keyboard scancodes sets 1 and 2 (compile time selectable via
+ PCVT_SCANSET), patch from Onno van der Linden, c/o vdlinden@fwi.uva.nl
+
+- Util/fontedit.c updated to "#if defined (__386BSD__) || defined (__NetBSD__)"
+ (suggested by Mark Weaver, Mark_Weaver@brown.edu)
+
+- 132 column support for S3 86c928 chipsets
+
+- split off pcvt_ext.c from pcvt_sup.c, it was more than 100k ....
+
+- intro of Doc/Manifest and Doc/TestedHardware, removed README.X-PATCH because
+ it was now really outdated
+
+- superprobe compatibility patches from Joerg
+
+- intro of file Doc/BugList
+
+- another powerpatch from Joerg:
+ - some vgaioctl's are now available if in X mode (i.e. to scon to
+ another screen from within an xterm)
+ - removed bug in keyboardhandling, numlock'ed numkeys did send an
+ additional null (0x00) char, this has been fixed now.
+ - SysRq key made functional
+
+- made force 24 lines (see below) the default configuration to have a well
+ behaving vt220 emulator at startup.
+
+- every vt now has a separate caps-lock, num-lock and scroll-lock flag and
+ a separate handling of these lock-keys.
+
+- made sleeping in case of scroll lock working from an earlier patch from Joerg.
+
+- large patch from Joerg:
+ - Doc/pcvt.4 updated to reflect recent changes
+ - struct winsize set ok when switching between HP/VT
+ - struct winsize pixels reflect real values now
+ - ioctl for switching between 80 and 132 cols
+ - 132 columns for generic VGA's
+ - updated scon to provide access to ioctl 80/132 col switching
+
+- removed pcconcoftc and kbdsoftc structures from header files, removed
+ pcconsintr variable and introduced kbd polling synchronization
+ variable kbd_polling.
+
+- merging Joerg's patch to support 132 columns on Tseng Labs ET3000
+
+- moved Util/uemacs/* --> Etc/uemacs.tar.Z.uu
+
+- added patches from Joerg for new Makefiles in Util
+
+- added keyboard security define's to the new X server code
+
+- adding bugfixes from Joerg Wunsch for "old" (= non vt switching) X server
+
+- Control-Alt-Functionkey(1...12) switches now virtual screens/terminals to
+ behave consistently with xfree 2.0, also the pages are now checked
+ against the real no. of terminals available ALL the time ...
+
+- pcvt_hdr.h: changed "int pcstart();" to "void pcstart();" to avoid warning
+ message when compiling under NetBSD-current
+
+- Doc dir split into Doc and Etc, Support renamed to Util
+
+- adding NetBSD-current support for new X server support (syscframe changed
+ to trapframe in NetBSD-current as of 11/11/93)
+
+- adding Joergs changes for XFree86 2.0 multiple X server and/or terminal
+ session support
+
+- adding entries from patchkit 0.2.4 codrv keymap to Support/keycap/keycap.src
+
+- Keyboard security introduced into the XSERVER dependent part in pcvt_drv.c
+
+- renamed device files from /dev/ttycXX to /dev/ttyvXX
+
+- added file Doc/pcvt.el from Joerg Wunsch to distribution
+
+- added HP-mode function key map from Gordon L. Burditt to description
+ in Doc/Keyboard.HP
+
+- fixed bug in scon which prevents it from showing the correct status of
+ 132 column support of chipset
+
+- 132 column mode for Trident TVGA9000 works now, after 2 1/5 months of
+ calling everybody i eventually got a tech ref manual from Trident ....
+
+- it is now possible to "force" pcvt into a 24 line mode when operating
+ in pure VT mode with 25 lines or in HP mode with 28 lines. This
+ is sometimes necessary when running software which assumes it runs
+ on a "real" VT220 which has just 24 lines.
+
+- updated scon to support the 24 lines force mode (scon -f [ on | off ])
+
+- soft reset fkey now positions cursor into left upper corner, update_cursor()
+ made global function
+
+- updated scon (-l) to print out additional info about the vga chipset,
+ family and 132 column support if VGA detected.
+
+- added additional fields to screeninfo structure in pcvt_ioctl.h to be
+ able to return information about current vga chipset.
+
+- moved VGA type/family definitions from pcvt_hdr.h to pcvt_ioctl.h
+
+- Terminfo and Termcap updated to support 132 columns
+
+- fixed bug in kcon which outputs garbage for remapped keys in kcon -l.
+ (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- modified Makefile for Support/ispcvt to copy it for installation instead
+ of moving it ..
+
+- renamed /usr/share/misc/keycap -> /usr/share/misc/keycap.pcvt to
+ avoid nameclash with same file for codrv (Gordon Burditt)
+
+- security bit no longer ignored when initializing keyboard (see #define
+ PCVT_USEKBDSEC in pcvt_hdr.h, suggested by Terry Lambert)
+
+- cpufunc.h include made only for NetBSD (Gordon Burditt)
+
+- switch statement in pcvt_drv.c removed for 386BSD (Gordon Burditt)
+
+- Trident cursor size bug removed
+
+- keyboard initialization for ddb
+
+- added support for cursor on/of switching, screensaver and DECTCEM
+
+- removed explicit Hercules support, MDA = Hercules in this context now
+
+- patchkit from Joerg Wunsch (kbd-overlay malloc, scroll_sleep, clip fix)
+
+- ispcvt now installed into /usr/sbin
+
+- removed PCVT_NETBSD08 and PCVT_NETBSDCU, intro of PCVT_NETBSD
+
+- implemented 132 column operation for wd90c11 chipsets
+
+- PCVT_PREPATCH022 renamed to PCVT_NEEDPG
+
+- INSTALLATION.NetBSD written
+
+- DEVICE in kcon makefile changed from /dev/console to /dev/ttyc0
+
+- new keyboard code in pcvt_kbd.c from NetBSD current
+
+- new /etc/rc.local script in INSTALLATION
+
+- implemented 132 column operation for et4000 chipsets
+
+- detection of super vga chipsets as a prerequisite for 132 col mode
+
+- switched to memory mapped virtual screen operation, configurable no. of
+ virtual screens, virtual screens now also on MDA and Hercules boards
+
+
+Changes 2.10 -> 2.20 June 1993
+--------------------------------------------------------------------------------
+
+- added new option -a to scon to get the video adaptor in scripts
+
+- support for NetBSD-current, define PCVT_NETBSDCU to enable it
+
+- Support for NetBSD 0.8, define PCVT_NETBSD08 in pcvt_hdr.h to enable it.
+
+- Change Support/Makefile to use <bsd.subdir.mk> instead of <bsd.prog.mk>
+
+- Font editor for the EGA/VGA font-files added to support the design of new
+ fonts. One will need Zeyd M. Ben-Halim's ncurses library to compile it, see
+ file README.FIRST for information where to get it
+
+- Doc/INSTALLATION upgraded
+
+- Terminfo entry added to support Zeyd M. Ben-Halim's ncurses port
+
+- ispcvt is now installed in /sbin to have it at boottime if /usr is not
+ yet mounted.
+
+- applied a patch which prevents CAPS LOCK, SHIFT LOCK, and SCROLL LOCK
+ from being repeated (causing i.e a flashing CAPS LOCK led while
+ holding CAPS LOCK key down).
+ (diff from Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- merging patches to support the pccons-model of X11 server support
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- file pcvt_drv.c routine pg() enclosed in "#ifdef PCVT_PREPATCH022" to solve
+ multiple defined symbols beginning with patchkit 0.2.2
+ (reported by Marko Karppinen, dreamer@purkki.apu.fi)
+
+- added british keycap entry into keycap source file
+ (from Andy Duplain, duplain@rtf.bt.co.uk)
+
+
+Changes 2.00 -> 2.10 March 1993
+--------------------------------------------------------------------------------
+
+- detecting the presence of video boards has been changed to ask the
+ BIOS "equipment byte" in the RTC-CMOS ram what's installed. this caused
+ many discussions but solved also many problems ....
+
+- driver name changed from "pc" to "vt" for multiple driver coexistence
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- new devicenames recommended for showup in utils like "ps"
+ (many people suggested that ..)
+
+- new location for manual pcvt.0
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- new demo file "sgr.vt" to show available graphic renditions
+
+- intro of Doc/ChangeLog (this file)
+
+- fixed bug in Support/keycap/Makefile
+ (diff from Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- make vttest (main.c) compile after applying patchkit-beta2
+
+- screensaver fixed by Joerg Wunsch
+
+- screensaver now compiled in by default
+
+- made recognition of CONTROL-ALT-DELETE an optional #ifdef'ed feature
+
+- added sgr-conversion table for MDA adaptors
+
+- fixed sgr-conversion table for VGA monochrome environments
+
+- added support for using the kernel debugger
+ (diff from Bruce Evans, bde@runx.oz.au)
+
+- fixed several bugs regarding monochrome environments in pcvt_sup.c
+
+- fixed bug in scon preventing one from piping output though more
+ (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- display current screen number in HP-mode in the bottom right of screen
+
+- changed names of all #define-able compile time options to start with
+ "PCVT_" for easy identification and installation into the kernel config file
+
+- applied another pcvt_kbd.c patchkit from Bruce Evans, bde@runx.oz.au. he
+ writes:
+
+ These fixes are mainly related to ddb. sgetc has a weird interface that
+ has caused some bugs, and it was too easy for ddb to reenter itself.
+
+ 1. Don't use char for keypad2num, char might be unsigned. Space is not
+ important since the array is small.
+
+ 2. Don't use u_short for n.
+
+ 3. Change some 0's to NULLs.
+
+ 4. sgetc must not return NULL for the !noblock case. Only callers with
+ noblock set check for the null pointer. When the kernel follows a
+ null pointer, I think page 0 is sometimes mapped in so nothing bad
+ happens. The kernel panics if the page is not mapped in.
+
+ 5. Reentrancy fix. The debugger really ought to check for reentrancy
+ itself, but the driver still needs to return early after the
+ debugger returns, so that it doesn't return a junk ESC from
+ ctrl-alt-ESC.
+
+ 6. xlatkey2ascii may return NULL too.
+
+--------------------------------------------------------------------------------
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..8999896
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Makefile
@@ -0,0 +1,15 @@
+FILES= Acknowledgements Bibliography BugList ChangeLog CharGen \
+ Charsets EscapeSequences Keyboard.HP Keyboard.VT \
+ Manifest NotesAndHints TestedHardware ToDo
+
+beforeinstall:
+ for file in ${FILES}; \
+ do \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$$file ${DESTDIR}${BINDIR}/Doc/$$file ; \
+ done
+
+afterdistribute: beforeinstall
+
+.include "../Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Misc/Doc/Manifest b/usr.sbin/pcvt/Misc/Doc/Manifest
new file mode 100644
index 0000000..cd75e15
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Manifest
@@ -0,0 +1,162 @@
+
+FILES AND DIRECTORIES - Base Directory [pcvt Release 3.20]
+--------------------------------------------------------------------------------
+
+Doc Documentation, see below
+Etc Additional things, see below
+README.FIRST guess what
+Util Additional utilities and support, see below
+
+Driversource Description
+------------ --------------------------------------------------------
+pcvt_conf.h Driver, compile-time configuration file
+pcvt_drv.c Driver, os-interface
+pcvt_ext.c Driver, USL-VT Xserver ioctl's and 132 column support
+pcvt_hdr.h Driver, global include file
+pcvt_ioctl.h Driver, ioctl's available for the driver
+pcvt_kbd.c Driver, keyboard handling
+pcvt_kbd.h Driver, keyboard handling header file
+pcvt_out.c Driver, VT220 emulator state machine and misc
+pcvt_sup.c Driver, support code for ega's/vga's
+pcvt_tbl.h Driver, character set to vga charset mapping tables
+pcvt_vtf.c Driver, VT220 emulator support functions
+
+FILES AND DIRECTORIES - Doc
+--------------------------------------------------------------------------------
+
+Acknowledgements Everyone and everything who/what helped
+Bibliography Useful Books and documentation
+BugList Things to do
+ChangeLog Driver development history
+CharGen Description of the character set files
+Charsets VT220 and mda/cga/hcs/ega/vga charactersets
+EscapeSequences List of supported control codes & escape sequences
+INSTALL.FreeBSD-1.1 Install instructions for FreeBSD Release 1.1 or 1.1.5.1
+INSTALL.FreeBSD-2.0 Install instructions for FreeBSD Release 2.0
+INSTALL.NetBSD-0.9 Install instructions for NetBSD Release 0.9
+INSTALL.NetBSD-1.0 Install instructions for NetBSD Release 1.0
+Keyboard.HP Special keys and emulated VT220 keys, one style
+Keyboard.VT Special keys and emulated VT220 keys, other style
+Makefile Makefile for the pcvt.4 manualpage
+Manifest This file
+NotesAndHints Random notes and hints
+TestedHardware A list of tested Hardware, this is just what we got ...
+ToDo A list of things to do
+pcvt.4 Manual page for the driver and ioctl's
+
+
+FILES AND DIRECTORIES - Etc
+--------------------------------------------------------------------------------
+
+MAKEDEV.pcvt A shell script to mknod the device files
+Terminfo A terminfo entry for the emulator, this IS in
+ fact a VT220 terminfo entry !
+Termcap A termcap entry for the emulator, this IS in fact
+ a series of VT220 termcap entries which
+ are extended by 24/25/40/50 lines, 80/132
+ columns and HP-function key labels
+pcvt.el GNU emacs configuration
+rc.local sample script for driver initialization
+ttys.pcvt.netbsd sample /etc/ttys.pcvt for NetBSD-current
+ttys.pcvt.freebsd sample /etc/ttys.pcvt for FreeBSD 1.1R
+ttys.pccons.netbsd sample /etc/ttys.pccons for NetBSD-current
+ttys.pccons.freebsd sample /etc/ttys.pccons for FreeBSD 1.1R
+uemacs.tar.Z.uu an example of how to use the function key labels.
+ It consists of some files from/for MicroEmacs 3.11a:
+ - dot-emacsrc, to be moved to /usr/local/.emacsrc,
+ startup file for micro emacs, contains setup of
+ the function key labels
+ - emacs.hlp, interactive uemacs help system
+ - unix.c-3.11a the source for the terminal handling
+xmodmap-german X-server german keyboard mapping sample
+
+
+FILES AND DIRECTORIES - Util
+--------------------------------------------------------------------------------
+
+directory description
+--------- ---------------------------------------------------------------
+
+loadfont Program to download charactersets into EGA/VGA character
+ generator memory.
+
+cursor Very simple program to set the cursor shape.
+
+scon Program to control various aspects of terminal emulation,
+ such as: emulation mode, screen switching etc.
+
+kcon Program to control various aspects of the keyboard such as
+ key remapping for national keyboards etc.
+
+keycap A library for accessing the keycap database
+
+ispcvt A short program usable in shell scripts to verify that the
+ current running kernel has pcvt compiled in and that the
+ the utility version and the driver version are the same.
+
+fontedit A program to edit VT220 downloadable character sets.
+
+userkeys A program to edit the VT220 programmable function keys.
+
+fonts Contains uuencoded binary fontfiles following the naming rule:
+
+ vt220<X>.<YYY>.uu
+
+ <X> can be 'l' or 'h', where 'l' stands for a standard
+ IBM II charset and is to be loaded first for the
+ base characterset to support a desired resolution,
+ and 'h' is the extended characterset needed for proper
+ VT220 emulation and is to be loaded as the second set
+ in addition to the above mentioned base characterset.
+
+ <YYY> is the identifier for the character cell size, currently
+ we have:
+ 808 = 8x8 - 50 lines on VGA, 43 lines on EGA
+ 810 = 8x10 - 40 lines on VGA
+ 814 = 8x14 - 28 lines on VGA, 25 lines on EGA
+ 816 = 8x16 - 25 lines on VGA
+
+ Files distributed:
+
+ vt220l.808.uu, vt220h.808.uu
+ vt220l.810.uu, vt220h.810.uu
+ vt220l.814.uu, vt220h.814.uu
+ vt220l.816.uu, vt220h.816.uu
+
+vttest A VT100 compatibility tester. This is a test tool for
+ VT100 emulation writers and terminal buyers.
+
+demo - chardemo.vt and colors.vt: These two files are from the
+ MSDOS-Kermit distribution from the Columbia University.
+ They can be "cat"-ed to the terminal screen and display
+ all supported VT220 charactersets and all possible colors
+ respectively.
+ - sgr.vt: A demonstration of the various display enhancement
+ combinations for the DECSGR escape sequence.
+ - some other VT animations i collected over the time and a
+ program to play them on pcvt with adjustable delay.
+
+fed - a simple, System V curses based font-editor for the EGA/VGA
+ fonts in the above mentioned font - directory.
+ Fed was developed using Zeyd M. Ben-Halim's ncurses library,
+ which is available by ftp from netcom.com:pub/zmbenhal/.
+
+mcon - a program to control the mouse emulation via the keyboard
+
+kbdio - keyboard controller debugging utility
+
+vgaio - a program to read and write VGA registers.
+ ---------------------- CAUTION ------------------------------
+ Because you are able to change the timings without check
+ or warning, this program may permanently damage your monitor.
+ If you don't know what you are doing, DO NOT USE IT !!!!!!!!!
+ -------------------------------------------------------------
+
+set2061 - a program to set the programmable clock generator on my S3-
+ based ELSA Winner 1000 VGA board.
+ ---------------------- CAUTION ------------------------------
+ Because you are able to change the timings without check
+ or warning, this program may permanently damage your monitor.
+ If you don't know what you are doing, DO NOT USE IT !!!!!!!!!
+ -------------------------------------------------------------
+
diff --git a/usr.sbin/pcvt/Misc/Doc/NotesAndHints b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
new file mode 100644
index 0000000..725831a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
@@ -0,0 +1,321 @@
+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 a 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/Doc/TestedHardware b/usr.sbin/pcvt/Misc/Doc/TestedHardware
new file mode 100644
index 0000000..5d37681
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/TestedHardware
@@ -0,0 +1,79 @@
+Tested Hardware List last edit-date: [Mon Feb 20 19:36:20 1995]
+
+This is by no means a complete list of hardware pcvt runs on, it is just
+compiled from reports people sent in and from hardware owned/loaned !
+
+
+TESTED VIDEO BOARDS (80 column operation)
+--------------------------------------------------------------------------------
+
+Manufacturer Chipset Monitor Notes
+------------------------------ -------------- ---------------------- --------
+2theMax (?) ET4000 VGA Color
+Video7 Inc. VEGA VGA VGA Color/Mono (2)
+Diamond Stealth VRAM S3 NEC 3FGx
+Trident TVGA 8800CS NEC 3D
+Trident TVGA 9000B VGA Color/Mono (2)
+Data General C&T P82C604 VGA Color
+NoName Hercules W86855AF Mono
+Tandon Monochrome (Hercules) TD3088A Tandon Mono
+Kyocera ML III25 (Mainboard) WD90C00 JVC VGA Color (1)
+Kyocera ML III25 (Mainboard) WD90C00 Nokia CED1 VGA Mono (1)
+Kyocera ML IIII33 (Mainboard) WD90C11 Nokia CED1 VGA Mono (1)
+NoName VGA TVGA9000B JVC VGA Color (1,2)
+Tseng Labs ET3000AX JVC VGA Color (2)
+Video7 Inc. VEGA VGA VGA Mono (2)
+Video7 Inc. 1024i VGA Mono (2)
+ELSA GmbH S3 928 VGA Mono/Color
+IBM EGA 6845 Monochrome (2,3)
+IBM EGA 6845 Tandon EGA Color (2,3)
+Trident TVGA 8900CL VGA Mono (2)
+
+Notes:
+(1) - slight display distortion when switching between screens
+(2) - remarkable display distortion and/or loss of sync while loading fonts
+(3) - snow while scrolling with HP function key labels turned on
+
+TESTED VIDEO BOARDS (132 column operation)
+--------------------------------------------------------------------------------
+
+Manufacturer Chipset Monitor Notes
+------------------------------ -------------- ---------------------- --------
+2theMax (?) ET4000 VGA Color
+2theMax (?) ET4000 Tandon VGA Mono
+Kyocera ML III33 (Mainboard) WD90C11 Tandon VGA Mono (1,2)
+Kyocera ML IIII33 (Mainboard) WD90C11 Tandon VGA Mono (1,2)
+Kyocera ML IIII33 (Mainboard) WD90C11 VGA Color (1,2)
+Trident (?) TVGA9000B VGA Mono (1,3)
+Tseng Labs (?) ET3000 NEC 3D
+ELSA GmbH S3 928 VGA Mono/Color
+Trident TVGA 8900CL VGA Mono
+
+Notes:
+(1) - slight display distortion when switching between screens
+(2) - all fonts must be loaded in ascending order prior to switching to 132 cols
+(3) - remarkable display distortion and/or loss of sync while loading fonts
+
+
+TESTED KEYBOARDS
+--------------------------------------------------------------------------------
+
+Manufacturer Type Layout
+------------------------------ ---------------------- ------------------------
+Cherry MF II US
+Cherry/Tandon MF II German
+Hewlett-Packard MF II US
+Hewlett-Packard MF II German
+Tatung AT German
+Kyocera MF II German
+
+There is absolutely NO support for the ancient PC-keyboards (they had 83 keys).
+
+There is only limited support for AT-keyboards (they have 84 keys, and a
+separate numeric keypad, they don't have F9-F12 keys) because the emulator
+needs F9-F12 for control functions, and due to the current design of the
+keyboard driver there is no (full) support for national keyboards because
+of the lack of a ALTGR key.
+
+MF-keyboards are fully supported, 101- and 102-key versions.
+
diff --git a/usr.sbin/pcvt/Misc/Doc/ToDo b/usr.sbin/pcvt/Misc/Doc/ToDo
new file mode 100644
index 0000000..463e005
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/ToDo
@@ -0,0 +1,13 @@
+
+Things to do Last Edit-Date: [Sun Apr 2 18:29:16 1995]
+================================================================================
+
+- implement secondary DA request: ESC [ > c and/or ESC [ > 0 c
+
+- implement user settable primary DA response and secondary DA response
+
+- retrying for a non-connected keyboard in doreset() must be made cpu and
+ speed independent. in case of a not connected keyboard, a fast machine
+ runs doreset() fast and a slow machine runs doreset() slow - bad !!
+
+- remove single screen X server support and make PCVT_USL_VT the default
diff --git a/usr.sbin/pcvt/Misc/Etc/Makefile b/usr.sbin/pcvt/Misc/Etc/Makefile
new file mode 100644
index 0000000..914d39a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Makefile
@@ -0,0 +1,13 @@
+# $Id: Makefile,v 1.2 1998/03/16 11:46:09 bde Exp $
+
+FILES= Termcap Terminfo pcvt.el rc.local uemacs.tar.Z.uu xmodmap-german
+
+NOOBJ=
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} ${FILES} \
+ ${DESTDIR}${BINDIR}/Etc
+
+afterdistribute: beforeinstall
+
+.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/rc.local b/usr.sbin/pcvt/Misc/Etc/rc.local
new file mode 100644
index 0000000..32d89c2
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/rc.local
@@ -0,0 +1,264 @@
+#---------------------------------------------------------------------------
+#
+# sample rc.local
+# ---------------
+#
+# last edit-date: [Sun Feb 19 19:20:42 1995]
+#
+#---------------------------------------------------------------------------
+#
+# NOTE:
+# assumptions: - 8 screens configured
+# - /dev/ttyv0 ... /dev/ttyv7 exist
+#
+#---------------------------------------------------------------------------
+
+# start xdm on screen 8
+#xdm_start=YES
+xdm_start=NO
+
+# setup german keyboard layout
+#set_keybd=YES
+set_keybd=NO
+
+# setup keyboard delay & rate
+set_keydr=YES
+# very fast settings
+set_keydr_rate=0
+set_keydr_delay=0
+#set_keydr=NO
+
+# constuct a new /etc/motd file
+construct_motd=NO
+#construct_motd=YES
+
+# setup cursor start and end scanline
+set_cursor=YES
+#set_cursor=NO
+# block cursor
+set_cur_start=0
+set_cur_end=15
+
+#-----------------------------------------------------
+# construct /etc/motd file
+#-----------------------------------------------------
+
+if [ X${construct_motd} = X"YES" ]
+then
+ if [ -f /netbsd ]
+ then
+ if [ ! -f /etc/motd ]; then
+ install -c -o root -g wheel -m 664 /dev/null /etc/motd
+ fi
+ T=/tmp/_motd
+ rm -f $T
+ sysctl -n kern.version | sed 1q > $T
+ echo "" >> $T
+ sed '1,/^$/d' < /etc/motd >> $T
+ cmp -s $T /etc/motd || cp $T /etc/motd
+ rm -f $T
+ echo 'runtime link editor directory cache'
+ ldconfig
+ else
+ T=/tmp/_motd
+ rm -f $T
+ uname -a > $T
+ echo "" >> $T
+ sed '1,/^$/d' < /etc/motd >> $T
+ cp $T /etc/motd
+ chmod 644 /etc/motd
+ rm -f $T
+
+ fi
+fi
+
+#-----------------------------------------------------
+# local daemons
+#-----------------------------------------------------
+
+echo -n 'starting local daemons:'
+
+# Kerberos runs ONLY on the Kerberos server machine
+if [ X${kerberos_server} = X"YES" ]; then
+ echo -n ' kerberos'; kerberos >> /var/log/kerberos.log &
+fi
+
+echo '.'
+
+#-----------------------------------------------------
+# check for correct driver and driver version matching
+#-----------------------------------------------------
+
+if [ -x /usr/sbin/ispcvt ]
+then
+ if /usr/sbin/ispcvt
+ then
+
+#--------------------------------------------------
+# loading fonts into vga
+#--------------------------------------------------
+ echo 'console driver type: pcvt'
+ if [ -x /usr/sbin/loadfont -a -x /usr/sbin/scon ]
+ then
+ adaptor=`/usr/sbin/scon -a`
+ if [ $adaptor = VGA ]
+ then
+ echo 'loading 25 lines base font into character set 0'
+ /usr/sbin/loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.816
+ echo 'loading 25 lines extension font into character set 1'
+ /usr/sbin/loadfont -c1 -f/usr/share/misc/pcvtfonts/vt220h.816
+ echo 'loading 28 lines base font into character set 2'
+ /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.814
+ echo 'loading 28 lines extension font into character set 3'
+ /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.814
+ echo 'loading 40 lines base font into character set 4'
+ /usr/sbin/loadfont -c4 -f/usr/share/misc/pcvtfonts/vt220l.810
+ echo 'loading 40 lines extension font into character set 5'
+ /usr/sbin/loadfont -c5 -f/usr/share/misc/pcvtfonts/vt220h.810
+ echo 'loading 50 lines base font into character set 6'
+ /usr/sbin/loadfont -c6 -f/usr/share/misc/pcvtfonts/vt220l.808
+ echo 'loading 50 lines extension font into character set 7'
+ /usr/sbin/loadfont -c7 -f/usr/share/misc/pcvtfonts/vt220h.808
+ elif [ $adaptor = EGA ]
+ then
+ echo 'loading 25 lines base font into character set 0'
+ /usr/sbin/loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.814
+ echo 'loading 25 lines extension font into character set 1'
+ /usr/sbin/loadfont -c1 -f/usr/share/misc/pcvtfonts/vt220h.814
+ echo 'loading 35 lines base font into character set 2'
+ /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.810
+ echo 'loading 35 lines extension font into character set 3'
+ /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.810
+
+# echo 'loading 43 lines base font into character set 2'
+# /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.808
+# echo 'loading 43 lines extension font into character set 3'
+# /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.808
+
+ fi
+ fi
+
+#--------------------------------------------------
+# setting screen sizes and emulation
+#--------------------------------------------------
+ if [ -x /usr/sbin/scon ]
+ then
+ if [ $adaptor = VGA ]
+ then
+ size=-s28
+ echo 'switching to 28 lines and VT/HP-emulation'
+ elif [ $adaptor = EGA ]
+ then
+ size=-s25
+ echo 'switching to 25 lines and VT/HP-emulation'
+ else
+ size=
+ echo 'switching to VT/HP-emulation'
+ fi
+
+# get monitor type (mono/color)
+
+ monitor=`/usr/sbin/scon -m`
+
+# for all screens do
+
+ for device in /dev/ttyv*
+ do
+
+# setup HP mode
+
+ /usr/sbin/scon -d$device $size -H
+
+# setup cursor size
+
+ if [ X${set_cursor} = X"YES" -a -x /usr/sbin/cursor ]
+ then
+ /usr/sbin/cursor -d$device -s$set_cur_start -e$set_cur_end
+ fi
+
+# if monochrome monitor, set color palette to use a higher intensity
+
+ if [ $monitor = MONO ]
+ then
+ if [ $adaptor = VGA ]
+ then
+ /usr/sbin/scon -d$device -p8,60,60,60
+ fi
+ fi
+ done
+
+# switch to screen 0
+
+ /usr/sbin/scon -c0
+
+# set screensaver timeout to one minute
+
+ /usr/sbin/scon -t360
+ fi
+
+#------------------------------------------------------
+# if desired, setup keyboard for german keyboard layout
+#------------------------------------------------------
+
+ if [ X${set_keybd} = X"YES" -a -x /usr/sbin/kcon ]
+ then
+ echo 'switching to german keyboard layout'
+ /usr/sbin/kcon -m de
+ fi
+
+#------------------------------------------------------
+# if desired, setup rate and delay keyboard values
+#------------------------------------------------------
+
+ if [ X${set_keydr} = X"YES" -a -x /usr/sbin/kcon ]
+ then
+ echo setting keyboard typematic rate = $set_keydr_rate and delay = $set_keydr_delay
+ /usr/sbin/kcon -r $set_keydr_rate -d $set_keydr_delay
+ fi
+
+#--------------------------------------------------
+# if desired, start xdm on screen 8
+#--------------------------------------------------
+
+ if [ X${xdm_start} = X"YES" -a -x /usr/X386/bin/xdm ]
+ then
+ /usr/sbin/scon -c 7
+ /usr/X386/bin/xdm
+ sleep 5
+ /usr/sbin/scon -c 0
+ fi
+
+#--------------------------------------------------
+# cp /etc/ttys corresponding to console driver
+#--------------------------------------------------
+
+ if [ -f /etc/ttys.pcvt ]
+ then
+ echo 'copying /etc/ttys.pcvt -> /etc/ttys'
+ cp /etc/ttys.pcvt /etc/ttys
+ fi
+
+ else
+ echo 'console driver type: not pcvt or pcvt utility/driver mismatch:'
+ echo '--------------------------------------------------------------'
+ /usr/sbin/ispcvt -v
+ echo '--------------------------------------------------------------'
+ if [ -f /etc/ttys.pccons ]
+ then
+ echo 'copying /etc/ttys.pccons -> /etc/ttys'
+ cp /etc/ttys.pccons /etc/ttys
+ fi
+ fi
+else
+ echo 'console driver type: not pcvt'
+ if [ -f /etc/ttys.pccons ]
+ then
+ echo 'copying /etc/ttys.pccons -> /etc/ttys'
+ cp /etc/ttys.pccons /etc/ttys
+ fi
+fi
+
+echo
+
+# EOF ----------------------------------------------------------------------
+
diff --git a/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu b/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu
new file mode 100644
index 0000000..25fba07
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu
@@ -0,0 +1,594 @@
+begin 666 uemacs.tar.Z
+M'YV09-[0:5&F39@Q<^2,`<"PH<.'$"-*G$BQHL6*(#+:V`@"0,:/,#I^S!C2
+MXT@0,F3,N%$C8XT:,&+`H#$CA@T:&6/$F%&#AD<8%X,*'4JT:,0Z<^B$D9,1
+M@)PW`HU.O(.F3!DV4K-JW<JUJ]>O6G>T&$NVK-FS:-.J7<M6K8(=;Q-,42J'
+M3ATX(,RD85,F[QNF3=*,>5JD29`A4T#,<(%##`O%+F0^7JPS3-RVF#.??1OW
+MCIPT=.B4<0-"3!X02*ZR:5.'#AH0@<>@"7,US1P0,7+@R,$B=PX8O7/DB!$\
+MAXRX"4`4.7.FC)RD9<C022K'!4H<R%'"V(XZ3!LQ=>2<`0$S`0HT;8"@08@&
+MC8OU<UR0*9."\XX$=\F$$4U&!VX<+<`@0PN[@=`">LB9\1<(;\S10@XOV(##
+M&B#<`=IK2$#1`A7.M9&&&V&P<9L)(+`!U1HAIK%&&7,@)QIT9%1X(0AHP"&#
+M#3+0\$*-,<"THXTS&/?C#=OE@,<+8]3!&AM(O:#':**-@4.0,H`05W[[1><?
+M#`#&,&"!![:1X((9ME`%%B`4=-!ME,'@(HO\R>@:"$DNB10(HLGA(8@B@H`"
+M"#WT0"->1,+P0@XXU7<?EOSY)T,.`<;P8$AA(L=&&$D11`9H+>@G&@@&#?;&
+ME7!XJB4(,-S0@DP/QF`@@O?AIR9"D.GTV'S@,??A&8_!\08;MJ&Q*P@NN&!?
+M`M%Q:JI_6TQ1!VE!U#&>3;C5H$,--EP;DF\R=!%7$9L.9(5S<S`;PPW>PC5'
+M&72`0,*F<XS11HQ&!,'$%$4HX!EH9;30!HMSA-$<""(TRRX=P]X%@A'/CH'P
+M&VZTL$09IS4Q6AT@S)$'=&T06ZP+78A@7Z`D"XI$$4Q``<(464Q!16&`EBSS
+MS#3/'!>4SM%X%5X:<ZR`JTG]U6^H3RF0P+KMOFN;O/3:BZ_18GQ(1@MTO-'"
+MBJ>5@4<928KF[T%/M5"E$4[$8+2KEXIQ50MF-/QPQ%@3G%$16"1!Q4@GIYR1
+MR`E$[<;455]-<9I;=STTV%;/L+`3QR509=IKM^V&PVE`+/AI(IRD^4E\^PVX
+MU7%KS75KAXO:@N)DSV"TXI"SP;;;E<,]>.:;U]ZYU%2#/KCHAG]M.NI.T&`T
+M3JV_/OG;E\M=^^:W_YU[\KR3[GO8P-=@=$O%2TZYY7'3OCSG4.,>>.B%2T]T
+MXHO;8+0-)8:AMNO:(]_]]YHW_SGTY7M]?@LXD7V#T3=HW_N,MSW982XC<)!#
+M&>PPDGC)@30$"Y_SQK>[_)4N;"TA&W82@`,!1@YVW)M=1MR@M79]Q($0Y)NK
+M2(B'@5CH;V^X@]&JP@8X&*V#"IQ#&I[4@A<&1(8)"`,9IM:&-\R'8`HD0^?*
+M<(8/N6%7+7B#&=BVES(8K4H*DL,=EC(UV2SE('DRVACXLA1_`4Q@_0(6"8V&
+M-'?!BVD@H((4JI"O$(R&#&*Z&1Y`H[,:*J!*05/@]$:%+`L.L@4V2(#1YL,7
+MK_DPAF(DHQS,.(>`-:<%:K2B'?^6Q_N<3S&`RDA`W'""=H7(1'?`4U761:<W
+MM,$@?[L-R4!PA"(XH0A2L!<(H%`%*4#A"?B*60\4,(.C54V0YS/:OO1WQH$5
+M#`1+*$(64!.$Q#CA"0NK@A.&0(4D/,$)(`A!"$`0,@5L$H\*B$L"%UBY.]$0
+M+W!`HP)P$L@+$E)M373#$]UPABA.,9-L+,-29-,"!=J!7'T100]\H$(0)%$.
+M8;A#IVP#ATOEX8I^T2(76^!%B#K,.>:\8R<3P,)VO1,$\6R.`EI2ST%"C8E.
+MA*(4,?DA*SHNHUN40Q=GX]$P'DV@"D$#V_Z2TQ@IE*%G<VAT("I1>%4T#!<]
+MIYAV4+-9&B8)X&R"+:N0S6UV\YO0E&9B2-2$)$QA",*465S::)`/Y06$I(E;
+MZUK$OI8F\Z=)>^.\%N:T?/5-?+K+FB'WYRJRF2T!(<CB&/J"-O=]\'BQ2Q[M
+M\J:RCQ@A"4PHPMXD>#_RC4Y_B&M!`,G6N,3^9;$H\2#\X"K9C%SA"5(@PD>:
+M\`0KO(`(*+/?\SS;N_UU,'5&,ZU"^L(ZQZX6LB$\(&IXB::/3`$*14!K!/\Z
+MP<`2[K/V?-#BA(=8Q?:%>,8EH/Q$>`6L$N$)5_C($)I`A,1,UW.[K2!V#RF3
+MQ5FONZ?M"_;"&[_(S@]?09#"$)"0$2D4`0I,.$P1=$M!P<Z7L(5U@OKP.UP0
+ML"][K)T?;,PJW8RLM[V;I6YGY=O;T'II<?^C,&H#B&'D&E!N!CZ"-\'I8?:Z
+ME\'6U1JG9G4;#0;7NR#H8(L+V%KEU.UN'RG,81+#MS$"E9*63&--`YK7I>U5
+MCG0,*2??4E40O#:V7>7FC!-3U3*3+"[@V4N,[O"7J6'M-O]R0QT4$$"[(H[*
+M;K1RT^[E5_@V&*4&;:>#V*S3Q1VVL0/L;W*5]V79@NH-!P4!#\:"X^25M(=M
+M7ESC'L??#(O0R[!U=!$C/1:DBCB^IV$DN_JU3CL(&M.%!FX"BIMH3RNWT:*\
+M2D8FW8)*QTW57KLTH>D5O.&I5KS^_32N0<#(C)3:UX,;`Z;Z-6R.A@$.H$G1
+MD^Q[O6,K^L6T6[:TL:T4-N@!VJ>1]KI@/354YHQL$[YPIUU<Y&6C4B'31C>=
+MILWN%MP%#N]V0HI9/&\B:WC9_\;WNO0=/=":CGT^YJ"W;:T\NMDM(U3`)FR"
+M@-4F2Q+*:*3I&O&:YWA=>8YU%"F7N_P$#;$L,8<90A&F,%80#.$)33",$V1+
+MM^BFM:H*Z*"=185GI9E\ST\[]9_G0$/7C<&5L"3VH2=.;PUGZ$P9D=??,JGO
+M-+2`Z:K1-$:'/%[E7KVY&0$[&]CP7L`F3XA$-&)?9$WKQQK\TT$@@J-G^P3<
+MAMC/U@7VT.2^76.3/=D'Q&UF7\9WO[>]NOA[L(G[YX3[[K?651>A888@!8UG
+M!`I!J.7?W<Y;Z8'H7UV$>AC^EK[U4?WNRNUYA^G4!M8_?L0.[IU>^()B`+Z^
+M[!7'@L\_LON^W![5URWQPQ>W02$7'/BTLSB2,YZ1JSK!XT_^5R5##E"2&QV.
+M6$[YEJG:Y?+N'+T;=T+H"V/+NYGY_2530`XR=DQ[%EVO2.\SZ0<G;*G%T-!)
+M=7B+1CNWA`5()B,PE$KZUFJO]DBI1%IC]WR(ISQ08&!6\!$.>'Q_)G@]Y'\/
+MZ`2J,VN_-X&THW@?D21,X4/ZQH$"4163Y(`]5FP)`%Z8!WO*8X(9T8((J$2<
+MA7QS4%&<@H(*Y`8NY('<E@"79W?0EQ'/Q007MX/_IV],]QENL`8=F("MEP#R
+M5H-+.`5((`58M009D8'Z=@9/(5$9Z#^^)X#@IAQ.D&!2('IDV(-_UG#9!7%.
+MT'PC.(!S<V08IW'6AWUEI'U1)G(VU4;?=W)9)E4K5S,K4P0!-F`O8&`(IF`_
+M!W]F]C,A,71%XWWXQU=\1H?6M2X#)5195%0`F`"(IH0D^(B12&`XQ452"%0$
+M95`()78WQ8:M!6`"1F!B<!!K4%3ZID!/M5A?1P=4.!YTMX=MR(L#!@(F0(R7
+MLECZ%@=UX!QY4%!E4(S]0AW#0C;<18.L.(!3T(N^Q`1H=0<O8(W8J&]H\"P#
+M<8I<=(1)>%PVF#F4"(E(EA!CD!=WP(-*9UWO2(0M\(MC$(SS"&^NIXOSDX]!
+M<#?\6!K`R'"#%5K`,W#,6&3T\Q$4*7G+%W'.QX6M:&1/2'T;UW&1E'W-)&4C
+MAXB?&'Y:AD[DYXAE=58V9V.7B(DZ^3-`4W\NY8EZ!HI)!WC)<P8"834&00>R
+M`45F,!JH95@!*(%\2$M/8))#P#9.F5"B6)1':8BXR&DB.95'4)6>5R(U-7J0
+M%S=MM(P,^6GX<H!VL!1IX#Y\H6_@809-.4F^HD-O4W@SF)$:YH7HAU(-`AJQ
+M8Y<P%3&?1#:6!Y@BY#(!=H"?I($YYCR+*6$+*95MZ(9[!RJ(@Y:XEWSF\YEJ
+MF``$%Y:;*7L?,9D=J7QA@X=ZV):QYX=Q!(@<=WTI.8@K:8CW%Y0PR8@S23,@
+M$&,S=I,@II/(&7\GQHF$Y))!62^A&)#)@S1?(P<4`I6JZ)C*]98G81#6"9K(
+MIS5>Q$^L]@8?,A"K1T1+<9V,$X&H6618,&!'@$!$:`+>N0;ZM@9[X3H*U$00
+MLS@A6'?VN(30A%E,\!']&3N4F3Q/!P?9F*#_"8Z&IYE%=G-0,$T9`:&DH6_J
+MUB\:BDDQ%'"-*9L$PP3H)6#5I%D:NJ!QTZ':Z)\1\V\!%V_:23!5``45.`0I
+MJE0PRJ(D-IJ_TWNF6:/>LY$^FGM`^IK,=T-$VH<E:9LHF0!.IIO;=TG=YYQ'
+M%T<H%Y.=E)Q>FIQQ,07L`@(*$P:M\08WV7?Y$IQ?VJ9`1P,P8$Q"\Y-M5`(*
+M00=M@!<FH$.D00)C8`:GEU`NP#<AD`9F`(UE8(T@8*<.DZ>H8C2(I4!V\4#!
+M=4>&BF>,BJ=ZZB$QXJ>`ZAU]80)PMZAWZJBN<E^%>J@FL"![FJBDVJAX(0)C
+MD#FM&@>OJJD$@P9\$T1#Y"^$)ZM%-!^$:JEFP*4*@(@*)$1H<"*BZ7!A`Z?I
+MQ*9NVJ9QP01O($0@$`8@0$*IE%)6)*W3ZJ4_0P-RBDQWAEB&"HWK8JLFH!>L
+MQZW>Z@)P)`*[NDQ#LY($LP5#L'K;*A!Y(34@<$M7`'JUY`(?5DZ(=17K`JEV
+M2$6\!Z]H)*_S4JE_<ZF,V(CA&JX8RYD@\`1&\(A4<*./`9E2<#=*AA@?DY,U
+M@[$9ZZ8*T+#[HSYP`7>^>D1T<QA4H``T>P8F\HNN$ZQ]<;/<Q!E8"GY;"A<[
+MX`-*JQQ$8#=$\)!%8+#L92#92@9J@!3MXAI]D2SM8BK9RGI<NZU*HC9R<"Q4
+M4!5I$B[,EB4@8!NJU!=>ZQK[\;;_RGM;=!N7DA1I"QIQ`C'6<;;?BBQJ*V??
+MD3-N>[5ZRZ\?,A@%`27#0KADJTISVQS341I*`G!K5A4D=%!E>Q]:6[=]X;9<
+MRQ_&@K0)8`1_D92W(45T&[9>FYY[VRZ0FS-Z<14QLA1EH`/'D@#@PBE$D"7,
+M<@71`0+1,AXQ@1LPH`,P8"T[@1O"D2X)P+OAT@+C\AS,`@/I$A=9\`88PW3<
+MRP:W*R)H"@>M0;=TP&;9*@:0UA>9=!MS^[E5@Q>LFP?<V[G($BKQH1"@FS%H
+M*K?M0A6CL4`Y0[\8$[9\I+6G<0=KQW:42RJF0@8A<"R,PB*Z&RLMD#%A$&E"
+M""6$R9>Q@QP7'#`:'!Y#V"ZD&%3TEXP@K%1MI#5@]&A'1!55E#&T*"S\E%$$
+MT[L#\;NBP2SE!!<I4:[VEP#S1YWWV1F?P4Q5FE#Z6A4'.2SI>I=YV;:WX596
+MD`1%D%Y`B["IBJC6&"+0^(NLYZ=`BQOLXR;16Z>TA5M3$`16H%ED+'>06J<)
+MYC+E^(PD<,*R,<=C&L='9`)BW*GR0GB`[`;^ND$)8*\@YTQ;8*UOH)\WG$7*
+M,;T\G+L@L`5=@+!]DYC[U$\S57R0*ERHI<>F2%3S:`+2UBXBH,,M4,G^D3D%
+MLZM=7*ONDA3[<2<P&;T)L(GL4IUK`*D_58I#I5&%)@*:G)T&B9`ZQ5$\!48@
+MI<OZN78O^L&Z_"'K4A?'F(SN@C#_$LK$"JF*3(AHE*^.#,GC(<FL7+WE<LF9
+M+,M`1LK#C(JH/+>K/+WJ_,KY*LOI2LMYK!1V<1NY'+V\/!!'K,OP+(_%?,RN
+MDLQ%Q<Q?]%%E"\W[.<T0`\S6[!P#X8TW+*I#Y"[1S':'E;`56ZS1BT\QQ4_^
+MY+`V):4?)\Y6.F5IW,>#?$0EL,8SY\9^Y7VDO*AU3`5WC`04NRG%.G_B.1OD
+MV0*^<IXMD)Z^;*SJTL?VNJR/W*S9E1(*`!98G=5:O=5<W=5>'1$\]AXU]-5D
+M710?L1'L8Q(@(1(C41*:(P,"\A(N`1,R01,VT1*X(1,R$`,_4=9^O158NQ1-
+M\111\14`?!5_G=B*O=B,O5#1&QN$L7F)01FNDAHUE#&,.QIS$+TH``,S\`*Y
+M\0*[41]&TP07#"H"Y0:W435DRDJ?RP,S-P0^``)QDT"5PQ2LC13#LGHMF#-8
+M8S1>$`09\2^KM]IHBA1]\;E/1X1/P79Q\[YH&S#_@B=I,-V80K?"_=L*X`47
+MN"!;``7C001NT`7)L3*BLG;,%D-N\-@MP`,,PA0\@`0X5P0^D`!"D)C#PKJ@
+M[`5:\-Z7#-X@4`5P0-Y,>-YL=Q?L/=L+`ML[5]]%P'KZ747II!D47N$6;A8*
+MX-CF$0,I`!NUA57S204G8W.]-`6PE4Y>\+$+8\J%UE'.+`<)8-HJGA&H2\QK
+MUF;1.S%Y$$^W*P=GV")>(`09(03`B(HN#M$QW@)"_A%$?I"H.&S1B[O92CD'
+M%<'!3>-/080,<JB9E.3S^1%'<)39:I9KY`6:I1P0SN4U%;W;36-.4$)D7@;L
+M36-8#C'MPKKQ!%%F>&UHL-V5!06!QKUXN^:F75ESD^8H]45[#@=]?N&._NAL
+MD>'U;1XRT.&*5P3=Y`3S22)8A2\E"^+IQ&O12P17L6J`QDZ"3B?-#-';+5L+
+M?NDO4]^DWDA]45*J_M!AY`5HM>"=CDM44-])H-H8/>8_>!!69-JBG@"S;NH,
+MF.K#I@"F302C7NJ?8NO/[@5+$+U#8"*LA`*"U^&L?4=;'N<*`.GF?NYC(>F<
+M+0-AT.&T96`@`.L@#HT@T.N?KNFA;F`A*P5.,-L9$>S7;$K;6@8*O.8)P`-4
+M$`1"X.]!0`9Q.3G)C::VKA1B0']O8$->H`3E7>_"7A=CSJV9]+4Q(C50DN1>
+MD%X9L>R?DN<"`U&,OMU/L/%/`'"D@0(771<=#E`GO_$JWQ<KJC;G:Q6D<9]^
+MTD:Z?</(7NR+]8MRX`,=#KLH.`=_`>TM<`4;/P07?QHKRMH?;;EX"5)>@`5>
+M$/,?T?.E<2E5&.?N^Q3/$B-1/_7H'O>.KN[F,0,=[HQ(`.K;/05R4<.Q6&AF
+M\!0=\_9,L9>&Z;?;+04)(`4"_!Q]L=.![TIT$AY27_B%^3;&$O9\7^.HN+@*
+M%&?E1L.EN-U8H/B,S[FLY/F-2X1@3,H*P`/V\NLKT_>EB,.?:^MO,`8HF)7C
+M_KEY:\+(."PHD`1"4.B[[@9LD`?U`?M,(/N*+Z:U+\F^CREWKOOAP?NL._UZ
+MJ]'C,?S%WP)0</S)7Q]R7_X43O<H0`,=3HD)-@1Z;]I2\!&,SXW9BM[6K!00
+MO[J'JA?/D;4['ATMP*UP/SGQ&M;%<B,#1B,CT('_-S4$8/#C)]8!S<4(@2(;
+M4MBPL!!S(K89"]/F!>+?<-I&TZ@O8,#7P([D0!Z(@$%`M=V!G#$"=5<"\`)?
+M3MI`/#80O3Y"!"0<!PQ-00D32)C.4W!!4"#0V-&M'$('HA>&RE!!$+60B.6&
+M,.2,3?D!8&Y,:2M@H;=8U\5[&YN-SADRI<*-0!^]8X(?XAJ5._-'!B.=AD,!
+M-:##Z2@H8#?L11+0`O..1,B1(.`$G@LP>7]F(@'<*.B"HH+)LS-M0R`!["MR
+MHVU$8*;9>"#`"U`!1$@%((IJVTM]X<CEB19AVIA``G`W"L<0ZA32YP4L(2;L
+M4#SJ@X4]+U`%\`,<`#B9,!16-"\0!1)`%*@#`J'6P;D=F`<>@]237.V""3:W
+M5C(?X`Q4*0U;BP@YA^%UGOI7"1F#93`9;H8S:`/67Q&0,=^DYH@XS;($#!0(
+M$`)5P`@8`5R23I)>/%EZ2Z&^M1'#(`7$T-S:8%K.\+T-3H@%HE<9P`+C:6`0
+M/:A'^::>`A!NQ`FL6`CTIK5(@]IH)40H#`JZY-<IRH"[&E[7[7/A+FT%]%;@
+M:*!;\_!O*`#E1L(X&.'K8(?/#41`P%6@F,"!PH;:$)=0,?,%YV18!9P-MT%M
+MC`8%4$3TE@)9+$0H^6&P@Q(C%H3@(0/&0AGJQ+*`_FZ`&FPY66#>T4'90ENN
+M&+[;;BC/[*&`*Y`&`$Z'TU#LS>HU**T'4_X3:ZN&'M'KY:7MAJ&RP.I9`\EL
+M)O8%*98SSF$]+%M'((!!E+53"^E6`LE]T>'ZX;"IF-^8PJ@9%J*AA5`QW342
+M.%R,6T]C+A^"DZ,W'KRAL6-ZLVUNF31]LMM8ST6<6W?$6(R$2J?LJ%WH:A<H
+M8`0FPBO0X1;$5&Q;F%$SFC;.V+:(4/]"6UCQ0)'%ZJ``1H+=BW'KBVY=1-;V
+MN7IA&D@B)2+W[0<%!;ORP%<$C1+I(/D)S>@%LD`*R(D[<2>B/QS@[M148O!8
+M'8L//J090_J:0`)H>#&BC+F5U<C>FL!(P(X@@&>]`9\%PZS(*+2.9D\[^L,Z
+M\/5@G,GSCBG/,H;'G@7&@)8"J"VX)'K-OQ!X&U#`^M(B28Q%/,7]X((D%VFX
+M>95+$I(+!7`%<@D4B%Y4(#RH-@9!&H9-A7!Y?L),58VD)!@&7L$C(7.@/ERQ
+M+!:]@L#:B2&WP=40O&%1?`@@]VH7")(WLAY@\\'6BYHJD6>*0_8CDA<0(]F"
+M0"MO<<]U#%JH`(06%7@!AD'&!,(A8-2:PVU85JEDI\$N:20$G\XK24^W@=!0
+M"!2P+F2A7O16Y"\YZD3TEP/N'B3J1<1KYPRG`]/^-(M-FP+IQ$CJP5.("E\`
+M)MPWJ>\V&#(8>08,&6X4@#6,14"[T),$`B'C.P-UX%(4OOV0)X;>?EA*-TQ!
+M/H=LI4"F',(X**W1!OY%I;1*OM9I@!B1<-6%D9&@`C:E;&`1GC)5SBZFD/W0
+MUCI9+)LB4HI*D#(2O(!]XV3Y3<V1D.2P!<B)>IF53&&,8(K;X)2XW?7+E!F!
+M!/`N1-?ED@,7.`FO,!8./+TH*9&AF"R#Z$\F=+AOT@+.BH&Q)0L#MA@&*I#I
+MC@"*PP)&('J)J7:Q>]C.TV$2M0?:10P>0/'JV[K$$^XC8WQ#O"CQ;MU+G(3`
+M,.@]1(IW&X(&'*"$+:`5GJ[4-;=8WJ(K@*?Q?<$Y8($J&Z+0\TSBP9J1OAX0
+MO:#`Y5-0Q.@OM(NQP&PHBD49="1$;!4N.?`8.DHK@4<L`)AMO!:I0[8-U`.6
+M9.IOY`S"1_5TG;;C7EINV,P!.UE3AF9'J6*.TCH-+PUE+:^E^<N6'`XV5('F
+MEP00C&8Q/^?E"L!):+=Z3@/61"^)P2"<AG\(1M)`I)E;$`.U6,7HEMDT8D8P
+MD6P'!GFFT^"]9(A0RTL<+,]5KE;Y^$"55G0.CV%!4,#7(#9+Y@^R*&/N;@Y#
+M+0<J,Q\6D`%R`0AE+;2%#O^7$7(KY^L-O$`L$/.8Y.H9&*S-VGD@TA>G!,_D
+M\W$<S`%%K[#W(#TG><(3XRO0W0D'1/IB0`(PG:>$06BM%.2!-AL'M`()``J,
+MLQ]"&D1G`I*9)D\+!,]QIC!Z&^\\GDXS&6;+2F=SOHD<>0(><=Y]3:V9)F7+
+M-%P9G*<(V))S:2L?G*(<&)43"MV!Y(#LW,!L"WLH[P/63!%HA%@;#W"?:H\3
+M*L\I@`:H$(5(G[1S%#H!V!AI,A#QY(0/L@G$Q@QT%WJF)9Q_'_,_!=`':A!S
+MR&OX7`Y$Z#7-Z!GWLJ7=DYI4TVI>PVRX#:4`G!1N(+&$CD1M%:AZG$`!B$KA
+M0^RV]%8G.)C:&!9<*SI$P";0-?_F<V")OY!L4KDL<;ON',1SG$)`+F#`"LC:
+M>-ON]*$1D`=PGMDF*)`;"$!<`Z'9W8G52/K:X118HA=4XL&YU5@:?:A92@H]
+M<X"ZSH$1J,9=^N2B82_;F3T6:CF<BD497JO1<08Y96<R@RA[G&*;XE_^A=/@
+M5K05[>2@9#!;JK\UF7?FG1'H/-ZQ:4V!)4`^TZ41`%C%9P<H%6Q5##\>P3.C
+MC(N(Y@7!![H"E;$P#V?+;;FMSX44Z@`8VR*G@;4MQM@U0[D5Z'(!]6$4FCZ!
+M$B-:9"=]HW\4<*8)B,*GQ@-HN`U:]#8P02@1'XR&$Q"CJ82,[D.V\P]#*>GB
+MA$D@`0"\8>=+3>/I+&%FU"P^A[]P`O"6;L1\G!!X7E+6HZU:)&LS""OBFW(P
+M*X;%M)@<0Z1/\PSVB+OGQN9="L4E,$=-"L;$0'TD*27EA'QO"F2P2%@1M1P9
+M96WPXI>-0JMW!0*D/'6HP12WH:F(2OK4:)/L"RVRC;(N8-H>J9[RK*C\@D&4
+MK]P9#_%H1G5?8&N/M`O(]@9.%IP,>SS@+\;3QI<'7$-^:PTZY(A01-2IY5;4
+MAW`U`4,,\`725]\87STIC4@U#2C5Y'8,\6GYRY;-D'@-`9E#<^;=]VPYN&0Z
+M:KJ5T3)>1A,XER&@[T&XE&DEHP[LI%L7SSGLQAO6,T1#QYB2[$)"DCX@$#R;
+M8E\X+XFAK>+5.9`H*Y<LS:BD;P2<KKV0)\RH:V![9^`U`-:ZU5B#)$01$V$O
+M$,Z%I2#P;*E@93UO<;%4D@71]G(&3;6II$_:.8L?).Y.JV0KC;01;24S,P0T
+M8P0*T"9)`$T(@2E`!&B`"Z@2$&/\<<)`6`1D*FP0#)%MR6Q0J_KHLJ5/#"O3
+M1`B8%Q`74&7+S<DY0W%KFK9L)P0`EK:*&TVTE5Q)VZ,Y3)MPBW<L8C!(U;Z@
+MK81E)7&CJB>6],QL5P7<@.<8<W$C\MU5\1IU2-\/J(P.9+WR5]BEOMK>6_VN
+M%(/JM</BBET^'JBRK;!K+^D0I7H:GD6!_1L=[K,BP#U`Z@*LVBA(4F-7S($^
+M8#2(0!\]#5/0-MRY0\52XRL9@*DQ(FYX#A%[8'WH<F6N%RY;,L?JB5W5I.RI
+M`E_%"2@`K#=>7^R^\8>/#^",`4.5!A"B,G6Q:J^,?JXLT@9<(`^X@K$#C!$C
+M@9(+@28=F&U[0,.V@$#5!R0-EH488&PI($K0-P=\@`+8`PRV=XC93-('"DF#
+MU58+$'.540U+[N"L(5F-==8./=DBBS)192&UL7YVOA2?.FMG#<>@=:OM:Y1*
+MOG8JX<)L2]M-=):E`975AK9<&OLZ2V0@/.#0_/'!9DL+\`.Z;$I-RL\5K)CL
+M\#(#E^*<+0B&B%AQ++K+EF5RXW`>;/)C@^RY1`%R@2ZTBY?89H8%UE!?\RB9
+MA+T4$&POGE)Y.CK%V%(,9%MHE*WPX5T6A+9-VVM5;>],^P2SS*G.;E:/1VRC
+M[0UKH9Z)Z&@.J:(NX]>S+;;G]L&FVZ*Q!V!6:'$#=3;.DHYYVR\SHPNJ=2-1
+MO^*&MPK7PJ2MA73HK^"N#.CB_G1)-&D9*.X(:+O5LUC<94/MLD56Y"F5244:
+M:*.SY0L'A>W@N:<@6HMIYN,J?R%ZU5>S22ZT[#;BLCLTB(+47^%N8H1I^%JP
+ME8GD#&]G$,T4&V@7@D+]43T>L"F:R)<-(JQGR^X'3NLE,:Y;]5^>R0W$TNK&
+M(C9@"VB'V/;.;BMYBP(B;(.8L#+1PM;6#.MBTTDZ86QH-^VJW;7+=MLN17@6
+M:0`/R*O3$1EB0!APNV3MK'$$M482V-I:X[MYC0;HM;D6$V9"3>`(>4TG``54
+M@7?7;F!C"AV!L-$!L'#8L$+CO;R85^V^`!5P<.6>"FB-GQ<$E$HF0/TFF>\"
+M7I?,64`+:6'&S)CR2EXRH(-P"V\A>D%O`B@3'$)/?`@PA@32DQH9#\M$-/C#
+MBR)ZHY=E6PVMX37$AME0&^;`8^B]WR$\\`I:TB&ZINT]''O!/Z`']<`>W`-\
+MD`]4E0WXAQ5``R`%G&H!+^$WS``W47N+;Y@PA9Z"19)2'E,K0@)KHVP!TS(4
+MWP00PO!?0"A?>W*83AN;^RDYE[TRG$_E-!@U,O![;2__?2LN1MLJ8-^[*QSP
+M!?LV$EBU#(L%W(#W[P4[MMRV)M*\+\CM?-Z++)H6&`(7D`S<.KZ@G6.FP7$-
+M*(QZ`GIM[_ME%/PR3>`O$(#Z%!1E>P&43?^6RO?;:LU`'L"+'6+<X,NI:H/1
+M0P(P`U@#DQB7VR#)9@`.L`%B8`[$"%=3%UPIVREN7D<E&`<\T2%V+QNHP:*W
+M\\8]%?`"TLGF_;P)X,VEDJ9TRW*([CH;?XOJOL@L&08,TPW[3^T4KF3@V1!I
+M5")I:*F]5/"M#IC`'5@;(]X.(<%#B`H#"#%>K+&X(M8!.V9$,(8U#,(IQ*',
+MK<DJ4W[U#PW"$1&XRJWL7L+/Z4H'QLVU#81UAI;3/A9/&LFGP`.X^`1L1+1%
+M9=$F2</%>&"%6L`;=MT4Q(F\`^4"4HF`AVL85,:Q#;$W++U>FVZ4J*X1Q!,!
+MIY0*H*E1)0)@VUGQ`3Q@"X#C(,!0LI7[DL`S]@\?*K+A!!X#++Y&+C`!+&-I
+MTHP7!SCQ`EM@"P0!;.Q,I274_5S2&.#XRQ<W3NH=C#S'R6H"QHMI/)!9'=(=
+M>72@G)ZZT)!J#)5>2!(_]ZWB#-:X.JP#UC,1DU+*$=93*#)U:$:HK_.!*7`<
+M-)&*07*6W$_`$*4D2KZ``!-`(<V1NW&,G$`:MR"<9YIP`UP88K#9QT#`CFR:
+MN!!E<8@8#?B5A*_-3PY^+.(Q#`'[BZ:(P`V(@$\`MU'.7_$7?.'8A%L&L:;4
+M9,(H&Z$LVD.4X^P55Y(Z((L142T^&'T!&)\`,>)B=_',U1.^&`0`8V$\`(LQ
+MS461\5@$#`$F(`7J\0A($PL9X'R=:IR5^'$"X(BK4AA'9H%+E:O%8P!@E;(R
+M!Y!56A6,1A9A(FS/,7+EY_`88%<'86T]PAS'8-IJ8-]E5\[&V[BC?6;6+)I!
+M,JK`86"8#.M>/N%\J1AAS5W*>#`7YB"@,D("/MX"Z[<-.&:C<0(\9EUX7+$P
+M&1N-)$`C%BJ>^`S#B[59T^0F+*CP#+N%4)>/>*]$>>.LD]&X;BL0O5TW"2M5
+M[=:%>)&WCCRM+M)`P!9E7;C$MS=$<2YW?*BDWK_PJ3<L(*!*HW0A(<;T3<AT
+MRQBCD@J<`-Y)VV`[-FPZW&%59!V>`&F8`MJD!0`3&O`8=F?H.L=O<:IV#&1T
+MA&\8:UM<3.*(J"_7L)315I[P$&_@/6C<RN$PV(",+C[QX=_)"*:S,IY%A?A>
+M,8)RO:W;>)X=!NRD7"XH`A:!@])QEY6T>`U%64B;B+UYJ))(6"8AV7$E.5^C
+M(9Z=-/@B);G03+&2'AJB^4(;>,ES8LJ:808A!M0`UZ`#^%D&6(=@9[[^WU*K
+M"MAJ/D2)OO1TYD.'1ELAUM6$6C"&6[$`&]D8=O4H/U&4..8NRS<Q,'G'OVDK
+M1QU;!HP8HM%TP%V>DE.*!.+J@TY^1+E^90Q$71!:&ZH$E;VO*MQ5UN-34R)8
+MWM/1@2ZCSAJ=P-[S2I6Z.5<.,&ETVI&)E_C*C8\,/DNR0/JF!VEUWCJ[PBZ#
+M3VR"!49#W!W5/L,F,]T*409N8R]L%TG@!$2:+ND&'H,"L4:W<8:RUB4+QDP$
+MM@)EYJ%+`D-4<AR9D"OI"Z'"AD7"W/FY<DAA'7?:*B$'4A2084_)>288U*$.
+M&&EB[3`RQX(0`0%[8-]&*(&-_]WD2-&H$D=32IN";A5G"9.)\X'FQ<KQP#<[
+MU@N8`A$P"1RJ^@RM$[6`G@-@>H989WX55RN6'.#72*($[^L4\`+T]6WDUV9)
+M#$`4'BAMULZ?7HD)H"B/9_!EJMUB'5"J'3(@M!72T!KV`FC`'"Z;'QL-;2Q>
+ML0U?B,=$UFJS5_0FL?VD06"+L)G,K8%X+*;X@I%&U1U;0<>0_(8*Y^IXJ*L%
+M(1GKLK/"K)TU'HC'T<N*B@!,\J+'3>:`77N;#>`!$0`7YK8V`26PBP;@;?S`
+M2@)WW[XVA!N8$3_X1P4>P^1N`2OY,3RDNU&H-??/;1`!S@GD5LLM#'L*RUW<
+M>AN31#T6P8]5`!MVPZZ;XXV1.G"*J\AM<-UM>`2@:-K=%WA`4M@4,1H-3+H$
+ML'E7AO^=1TG@!6"3^>"N,N)F>]VZ^V'S;H(!)^2`P(;3NDJ7%>\BL(.9MPS%
+M@M'K=2N`-QSO)%X+&A9LKPGV!=RM`,X39MN3-NP,\.LW:S0DE83L;"D`+I#8
+M\1V[+;1,M)):&X<5ZG0KKV\W]$Y7NU6V\`$^$."FP'Q:X"N#MB0&"'YV0``$
+MQ]#@!((+']&-)B!X/Z6._!N'66$;D,!1A0N``;84!82)#G?`#U4"'^$*8`1X
+M[Y19!?#%%\">1.`+D,LL$,._&?]6`;(;8H,N`YZ[=_<1$0';Z"E4C?<@`K0W
+M$&<8$7B&.^_P7<2E]Q&/#@:1B3MQ6D(?V4Y<^@QT"2PW;_!]P*UX0KD*GU.+
+M1Z_B37KY22L6X]\[=FRV!""^R?<2@,GK9(8/+_8]`DKKH>(F4L`2;G%`-Q[-
+M-)I^#8!<D-?Q_LVY<J<1!ZFV6_26\=E]1'SWQG@!>;;J"N\M/@5(]8C^?\SF
+M2D_Q22[$+?G0M&78>W@7[TO*>U#YG9#B9+R*4_+>W3->`*:VT9M\C0/QY(U-
+M=.&O".5C7([3\3;\PXG7$-G$;.>1#W%)7L1=.&^-X<K<E&-RJOL>5#D0IP)4
+MERG`<F$^S'OX2`,!Q;N$M_!KR%LM.`/'X.9\N6`=#W[%J&/TGN621H?TR1!1
+MS3EY&ICG;&>;0XPY#KV)%3@'XB7\@A]N"LY<TOD'!ROCO(8_\',NP=-Y!1?H
+M&9R!;_#<"LW-N*1YT96CGNOR.%*&^42]4][`/([O\VX^`OQY\5;HZ7P*-'2"
+MOL[/^4"7Z+8DMXK>9DZ\VODWJ>CPG%Z6X0:AT8GW->_H8(R7A_0G$LN]N5#[
+MYS4]"12G<3X$2MS,P>FE?'7'AUSNTVT.Y4.5&?0AODCR501)NA_W,N8%O>BR
+MJOX$7H,A2ZM4W*BGJ^+=U$WHS)GIL!N(9^U&HJ"4N<1FW^3;Q`I2'G@02NZN
+M\`^-$,6R'KV`*"MEJU:I<+I8M*V1S;V.[#CAXPC\F;_S4E[+`VE/+]YZO5CS
+M0%HXU$=Y[M;C\2X)&!@G($>RP.U<V`Y#K1^J<%[.Q_E$[^`,W`H\)*@^O:6Y
+MR[[L0#RS.PQCS=GUN6HCZ:"]:8WVTG[:_Z5J1^JO':[K[D-U6QTX-"(1(4"V
+M4X$.=\%7.@-_Z"X]HB=U=Q[-B341PNWQ#K4?DZS!W%*-*"_J,OQ*_U71WGZD
+M@&GW[G3@N)]TYU[=@8E#+^@0W:#;='"RR(&X4ZT_ITZ/QPC?3L1C^%=/Y&.]
+M>/]WH6%SY(@@!^[JO<$'<E2!!PJOFRCFPO.RKII)R<=!NQ-XN.TG>JW?.%75
+MG<#8RAFLJQ,[Y1THBB'\00Q86&7`!)CH=1,V>K#CZF9T?J;U]/[BKPF0E?&*
+M3P9@BXW^!%H#CB>C.IZDIZN749C78`SW\&L0)$8OO4:NJGKN;0-*.)",LR3?
+MS[]Y\6;R36`-*G?03@YES`#E(!N=AV9,TI#D7?Q9\I93X`W2^"I?O*>`@<-A
+MWBF?['@/#WIJ>!&8\B2^>!]F;H8J'RO&F&!O-9Y8T?Y>/>M@PB/MB:'#1_AN
+MHE6>`)"-7D*^!FST9]J0&VL&Z\-I@R]T\V).!4`Y@6?FQPH976\)S,'V0(('
+MXCK.,WWB:%S>T[HQL=Y&&FMP,"`NW=B`'6`#L8*MX\S_)-W8EP"C@:_[UJ]Z
+M7:_E@+@;:"&^'MAO=,_94U!\\9R8Q[Z;XP?5=L^QM+]4=<%>K@-+79;LW7>?
+M+@.QHJH[U<(J;?/`N4_K?0`N+/<X@DO"/'%.];@^3,.!^^KJI_VU<1].>P&6
+M!F@\'FBAMG>9SAY4I?LOWY3Q0J`B]VU82O$4T6OQ%,C"!^)SX2^,LR\^EZ8J
+MO%<7JC[73UN`U>^KNHY+QP6_UA_\B9_P_\7%Y^AZ0@D_?%OOON,2DT#W6[R[
+M5JP;9O.OT<=7``@_AXPI08$".HH*Z'!.8&HR@7#/PL;4`%P#:J`?=?FC_N7K
+MO9AG]-:J9_-@N13&"_R\3^"I/B>GL%4_!\X`1:YX4<_L+T"F/P0N;GGDYVT8
+ME7?(A&W"U'X>J'B_XL7:?::?E>_YF@9:<#_L(XQ^1/?QQ-%D$."+4RZ%.<#V
+MW;[B?PYI7>X3_I#?+G[NX<_[;.!HW@>V[O8M/T\)_)+_VRO5K[@%;$`7`!0Y
+M'^U1"/@L*6E8$4SV>\!`$`?9_QA61>V?_;9?]H.`>"_?`7IKI^DHO;Y/\.N.
+MW[-[.G_M@G_N4W[=#*-/YXMN_$2U70#^R._/E'^^9_Z5`_%+AP[!]S]#/JF/
+M<B?PEW0O#\2#/T.W[RP=MJ^,EPX"DOLX/^A"-O07?HP.NE'0\Y_V;G_Z=W/Y
+MO_SI_^I*_-P?D>?]_7WAWXYGT@%Q\)]RM^19??=>Z.<R*277QEVR!6P3Q!E(
+MU`4P?5>>$M;Z:7G-@9N7,JU!7\!@)M!,>Y*$1H;H>"M[7H0'`JX74Q[^-T[9
+M10)![)`"OG@KX)_G.)"`0(4)6!/I2G*.!]@7@(#X@HBWT6U6,$3Y<D>(?Z`=
+M$!CSS"!#H/\%GZ$`MHCCPX.E`?/!&\!L06])(''V!9@5T8NF5]4%.X9)(839
+M*!"CP0QXEH"`2P#?HX5L=#H.CU,>T1^<%1*H`FJ!T43T<@.P@12#&UC&'(%F
+MX`>H!5H!212:5]59`;9!>S86J080'S3WQV%/)PZ/=P9J@5-9])(#;'0U#FA6
+M6[42MMD(0.)A@7,@%!`"VDZJ2*#WQ`D-M)G;,YJ!`",`*,@)TH"0(.2DBH""
+MJQPI&)J9@IF@*[@*/H*>X!!03*@BKF`LV)_,@IC@@C`"[(*WH!^8"U9Y.P$E
+M*`M>@JX94S`"5'G%X`\("7J!,0"=-PK^@LS@*3@">('1H,T!"282JH@7Z`M:
+M@JV9-@@.=H,KX!VHBH"#XV`I&`PZ@^H@.J@%$@$DWKFPT34Y:X`[V`RB@IO@
+M9]<)?@%$`"@8`^``]R`PH@]J@ZJ@/\@*>H)$@"N8&Q2$!\E!6`OV@=(@0[@+
+MPC4083X(#.Z#PR!%Z`TRA%7>7I,12H3"(#2H$.*"`*$7&,14=?@@2>@,<H,G
+MH3$($(*#*L%(N!&:@QXA"&B587K6X#5D$-Z$F6`\F+L=@-63B1+_O&[\W_4W
+M;MQ72Z'4@,F$$JX>6_?O10W`@L"'\IU1KU_$%_O)*FQ`YM`'L`!;W+:3`X8[
+M)R`:8>MMA?*"5P@6NH"5SZ/Q-IR%!`,ULONMA56=6"C8D(4[8)P#%R)L98!:
+MR,DY@49@++$72CU^81.8GCR!42`K(:B5`5:@MK<5V@:%X1=(U,TEP,*VD=6M
+M-XXAP;`&S`&18?'6!F(K98PM4Q<$?@G`5K@B=(:P7AX(&A(>?&!F*`+8`6(`
+M:@@"$()@UR&X=B2"\][-810:#6@A#"`;5H(N(3^X%XX!,<!ON`R6@YE@0L@;
+MQH4R@'&(#2*'PJ`MJ!7&A3.`<T@.UF;"(#$X'<HJ-(!U&!P^@\-A#>`=`H7"
+M($RX',HJ-L!XF`UF@N?@=C@&W`#J(70(#^Z%9(!O.!?F?#_A>B@,]H/G(1E0
+M'-Z'-J%^Z`PJAZ4AP4`&-(<`(DN8'\J'J*!TV!]6APEB.+<@8H?.H';8'W:'
+M$2)`-R'2@B4A?2@>9H@^841('KZ$]&%Z""*VA",B*M@>]H?PX>Z''XJ(`B(J
+M*!3V?D4A;`'7\7ZY(2,H!21_.Y*6\Z?L@X+"J@#]@5-9A$NX_U5_/6(N)`8`
+MB;+?D&A#;8CO($E'%.J&-B)2B"2.?2+?W0>P/&-_PU,H*$2%J:&6N/.-!U10
+M5P?[$0QB`!T@`HPL2(`10`7P`8D<'W`"I`$GP'TH(1XD5$.^A#4D@@6B"+`&
+M%(<C"\,E)\8`=>)72`FZ"GOB7K@&-(>!8G0A)\H`A>):N,I5"8FB:[@&5(>-
+MXA`@)\X`D2(EJ#A4BF>BG]@=9HIR(@W0*59U1@!.`"INAVN`>$@JG@`UP*FX
+MRK4$J^)YN`:DAZ^B#2`K/G'L0ZW8)ZX!\.&K>`/LB@M#`.$KFH8XP)JX<#F*
+M)P`.0"P:`1W$L:@9Y@#*XG/!+.8`S^+\(2WZB;[AJP@#/(LAP8)@!-07B>+K
+M!@+\BCM`M2@HG@#?(H"XRH6+3,&X&!+LB2@`U#<&7(&LXCXFE3EX<N(30"QN
+M4QY/R%,NLHJQX;X8R,F)2`"QB`\J/7W!P&@K#@'*8IQX`C`!Q*)=R!1<AA*8
+MHD@$1(S\X@E`!!"+9D_KYS#^BDJ$P<@$R(E.`,BHWE!*9TC&:"DN&":CG+@$
+M5(PE(%XX[@B,%(.E:`1PC$P`%L`'P``57AE@*%9U=EYSPW8@-#%6SG@>M`'X
+MHJV(!"B+6D7"(R?Z`,1BIS;=$$\NH[G8)P(!P2)L@.D$`5-CU?BNI3=W0%RA
+M--J+3>.OJ*L\!E$CV'@"\`!BXW2S+:X!20#'>#">`$/`O]CQY$(XT\AH&C(!
+M=N/)>`+,C.YB;B<?X8QY@*+8%<:,)X`00"PR`0:1*>'C_!]^HV9X?3".5@"Q
+M^.9\2>-,Y>@G0@&!HYRH!1"+@`ZJ<R=X*UDCJR@%\(P^(]`H$PR-==Z=Q]#,
+M(_0B>I`VFH9R0.AX`A@!Q*(4<,^A`9)CR^@YRF"Z(Q10*&YT`@[+2#GFC#_?
+M^$?U77,,H,I@)8I][MN'(`&*;ML$$A!@7(`VGAMPY&54B6#HY[X!<=8C>_0:
+M<$P;76J`K;`N'\+WV![1?/85>P3R48_-7FETEP!=6QP5`.J-.^UC^<(=:7OE
+MHQD@[RV`35X#>"4^"_*<M^<RO4C7HZ5'!0!YW&/Q5N31`>YC7D+]B7T(9+<W
+MO"!\B-W]&$J0=4:>_YA1X4M[@:W'0,J/-"*8=_7%?0<DM]<G;9`3WYN1J&P!
+M0I[IM_D!<:6C7A",!62/#V,&\2QAHM[T./AAB>T"LU=$4@R>V`0(XI%V$628
+M*.N%CU=BX6=$4GS/'AT0-Z"/Q=OFR"Z1@1F8P:?MJ7APP/PX1`IWHE<@!0<@
+M(Z%$TN<1.8E:#L$VWJ4P`D4;<"2*?2Y3/.$:3("716;A!'"/59WP-"=$.+P'
+M:^.ND`$7Y!#I,LD!UV,>:4OPD0I>7O5`["__HQ#9(2%\@<H9*0>\?%``&AE/
+MZ"WB#IY#()51B"1[-.J1;U?>FN;;G3VY#X7`OKEO\4++QJ_90KI/"?9*X@ET
+M`.4R!LR2H0%7=TNF`!T:'0!K(05H`+\&%["2@\Q!,4O&"U;!KW!,+A:,(1RP
+M3*I$SB0O.08N$,)D^Z;EQ`MK@"LI36*38T2#4$WFB+LA,6D&O$O/)#F9O]&(
+M5.)1V(:!><P?5+@^``>O'FP0!NP1K$''$)FQ+C^.M@=/AH5NWSU9I:%(VMY.
+M(`/0A54=#T5/*@DJ4^0R4OT*2L)OE^SMDRX@..5/8H(-Y?,6\95Y6`5!6;RI
+M>?D$#E-"6"HW3/NB[<%Y;Y!&B?'=>?.31]E"@)1G@#[Y&$"459V,PA3P!69`
+ME_5`Y`S,F\]EPK1-<%T<UN?A"R8E*DC=:!^JA/5&II0*;`MKH^BM"]I>*TGS
+M!)4SS\`$U+$=Z=N:D^S%"]WDND`7LG4E&/;GT565N])5>9"T;%LE$!=5FHUW
+M7PCF4)Q@867$QTUR.V6ES=%5@F#S"%@IYR1[H8$M&52F17PCKI,SV)7:7B[9
+M&HP!02444+ZT?H`E7NE+,@E,1UQI!#"6K\%65[X`EDUE,5D&!)4*U`CS`JZ5
+M3=NN5%DFDVQ`4%D$$%,1ST^6%X8\B:5;R4Q>/*(E::D#CCNH8VK)1JR6<$!0
+M:3'2+;W0&A"_V'KQ`C095-XWV\@X05F*E09%4'DO)89]P6*(ILB61\,8`+_M
+M"G'E<<DB,)1]B6P)3IXXS>4X^2L8EV-*D9@B,I>]Y;L43]Y+L]5WR5;>E4,A
+M^5<CJI,*`.]G39H49V0`N-'!6@)#:0!1D#S82HU`-4R5'H-U\+JY;S4"%$8Q
+MO'STY7B`T7ETC)I^"2S$4P)F-C)7P'7%W%GI58(Q\X&K@5JLDEI.:*!-TF]?
+MWS-7U?&5M.1I`/CUE.F*S9/[9&J=S6/P50P!M005``4\!JL*^%+V4634'1\`
+MJ=QR*68<X4VPF)C.$`!CYGV/7XADSMV8)R8;D&.NF$=`\^-C0B.8W]&4`M0'
+MT8O]]D"H<!P.7&`TU'G6&6OS%_A]'ET9P_%U?3UEVD>1A1)YWXRY]J431..8
+M8DN-F,F>F+D`N0!EWQ=08-X&?("@,`0(`9#:$E"XN9DG4)PY9T(C@H(?@`)$
+M%_(-G%B8]1WGY)6)\8TI8R:;&?&5F#@F<*!C/@&(`:;S8D(C?&:-2636:$?F
+MCOF6+)FC7Q6B:48OD::*Z6DJF8]!J+D&/)F0BI3I!E"9YZ3SN-;Y?K)%0D??
+MG7_#GSJG_J%S&EQ,9Z@I@A$<,*%H*@<KI<X4(R`-3=`9P/@%&;^!#9`;M`%H
+MP&T0!"@.5R6[\`7D.6U`BFG[K0JQ)E&HTMEWXMN'.:8XFEP:<U=JQA$\9L+S
+M8P*`>L*3"0+L`9`**O04O&R"V>H1@#V0+>8^=DY&F8[DJ\EM%F[[&]&89:(I
+M6^;WQW9XF5S?5!5FA@<O&IG);K8!5F::*;M\4N3FYB6E,)P=@KSR!1@JL1:@
+M"0((FE@%.B(%\`&%$K]S,B8!1\`V(07D;TC=%N0%)`:P"RO4P=2&X42;B7'J
+M"1HGQUE?F@"!YO#SAG`>)"?G\8:0G"DGT)E;?1,D)^>D#>V;]E_&.09\`6Q`
+M@>EQ@IPZRC7A!)"<<=X1P`<0FD\`TWESM@$:I^ZS!5@!9<4>&4K$`'OFUQEV
+MC@%C)Z57!)Q^@@(,8&7.>]\FG.33N6XJ@(AC5M0[,,<UA'+2$G0$S=&Q9!73
+M!.A1LJ0LJ=@1@8@E0+`3FL(:5$`6TT/4@^US>%YI^0\15H9==R8CH#=:$M5"
+M!#0!!TJX6;S!3?_'DH`P5!1]@;?CA74XZT(E(<<M=O5/0D;>A7$Q@LU9%SB=
+M4*?4*2@`F;AGU-EQ<CI99\1YTK$2>=_*<`4\D`2&RY2ND!`EUU)P&I1[S%W3
+MB7,^G;YG?1D"Q)U0YL5Y>TZ?7X#8:05,`<?G@`%W:G\OVMHY=H*?R.<%B&:R
+M=5G/;-A-N'[)ID4I1JB=3Z?W205(5U3`^,E[<I_V)_ZI?@Z;Q97P-1\<FP>#
+MB*5)[08P@+.9`T";TB:UZ59:F]BFMDD<_)LT8MT)84J<(>8;"'WZ">?F`_F6
+M!`$PIO3)-'8X\&;T(F_^!2A`O:E/^"L>Z-<H`NR;"8"K"6L"G#2B^1=LYIK8
+MW?JGW;E_-!W\-VR"F.2F3<;<*264"Z/TLDF:,>;VUVZ2H/&F<S!OIJ#[R@J:
+MM8P!16AHD#O"H#(H!9H`!)Q89J2A90Z`72;A\64JG+9GP[E[/IS"YZ(Y<:82
+M0:@(FG-*G3SGQ^ESBIQ!I\E)=*J<7N?V"792GW!HSUDHT4%(IUF!<FZ=`T;7
+MF7;JH>8GV8E5C)]HY_R)B-:?;*<5X':.GW)G.C'O^:`8:,CS1C1%(8\DP\-H
+M*VD1";$6+9R/*-1YEL2=\AY1F`"*GFIH!EK&;*`H@%*"-!BA1R9B`(A>`2'H
+MU^ENEJ`)P`E*;T:A`9@LRBX8H2]HX1:#]ILSJ-%`8LF:JQT"N-]=H'6>P%9R
+M]90R*`QP3NYOY-NVTR!05;L9A<E.7)C_98:IE,B2]4$)VD@^507GFG9@@G^]
+M$(G)W`E][<+UZ2<8?2F`&LD$9)_V"@HP^\&CMA`RPA>\FO#HDSEW1G9$0+G)
+M@1:9G2:E^99<FDFHF:G\#)FD)D(J::Z8G^:Z*1TXF1"I32:1FIH**:K)9((O
+MF=_GQVKRFQQ7,LJ,(G7B'#$W[]F@%FCZE]*Q?\@?W2G!`:#%Y@`*?QJ@S&8"
+M^FQ&F\2+`TIE7)M+0;8)'&R;568%VM#!HAUHI:EN?J1*Z`C::B*C%.A).M\M
+M=#?H?=?2Z:#'GZ\)85ZB^]VP>2\UFH0'+"J,^C/(2"W:QEP3N"A3^J*-I,=H
+M21J5JJ+/*"O:H^D^``,UBHQ:H_I;7+<P0):(7069,T!4)Q:JY[Z%!K\DTS&_
+MP9OSG@IY[]ED6LX_FH;:'*X$5V?L\1-_Y*%R@"5[_RB9V1IXD!?,"2E`&J0H
+M0&8:**`*V6<UFFBJ>RS"F$)&J4,*2K+GF0)='20*.6Q"`<X!E74AI52L#<:8
+M[,F@^>CLYYD^!O]HAR./!J>IJ'JIF$J/*FFZTM;A"W:G`A'XL`C!9+>I7BZG
+M;YWXMMSI3(?*<;K1.4\6`BNQ`H5'4A#`YL^L/K*+OY*LG$I*A,=@EY:D>*EJ
+MUU[JI89E7UGML4K>E!WU"V&860L%.5@6?6A`?>`RR0;I7EV(,P&FAQ-4D=8A
+MI@2DO0<%#)M,$M=`(4@R9-2GYH[Z":]IS,!D;J;LT18P/TD1_A&&:@:D`%U`
+M]DF80I;5Y+`)'AU6[5%/J0*\IBO`"A!*`*@I9/2XE2JG3IW=6;S%IYV>M3=-
+M/D3)'MPA&_"GTNGS2.*X=9$>,=>6WF_M*38:NX%'&Z`/B5IP/13#N?>4\4#L
+MFQU@GMPN0P36P"4E*H\!H%(?9)`OI-O#\M$P<<#+1^V].&2J-;98I'7N&Z`2
+MH**FA-7/U>Z]>]U<"=JFWD9)0>$F15(,N][I]!@X>RT$"O*83@'ZB?Q2/,UD
+M:6I6<KL]FLQ="+"NE*;]YJ!:J&8,NP+OT:2R(_G/A,HE^:.CP98:!W0X[DVE
+M\Y1R7(^IR-:O2&"W@8#W&!!H;J1,=*+MC1IHQ,?_Z"V"0B,JA/H)563<((^"
+MD4PHFV,8>CSOB[.5X@T.GEA/J7V&$F"D,0JIL'%0`5X`C^P%G@FGA"II#2A1
+MNY#L)3+"`N^!`J@`C^J["<P`,Y@=FO*=)B454/K4^EVKM^JVNJY8J"A("^`#
+MR`:]JBXC,XV>#@_%55K:.B"7KH&N_A1Q0(MJC$8O*,B+&AZXJU5DM/>O/F%X
+M*FQ:B>HR99;"TA?PHKYJ_L@#V3JM'['G-^6K;,>^6D4&K()"N^H#]'J_WL$Z
+MKV)\[M;RZ7@R>/MJB;FQA@?99ZH5'H02*^LE*3,YK,B3SB90K`'_*HD5O>BL
+M.BN#ZL0\J`L"8=,QN!5@I*9*'?RCGFJ',]M\>-)$^T&UH`"YZN#P@2&1UP9;
+MZFHJJU_@_I.PYJA,`:"$J:(6JM3>&!V49AV-4NF</FL4#^\!B\ZJ=$#V*4/&
+M`5L`MYJH,*)8:_%FJDI*:X^Q5U,R"/M9:13PO"U#JS=5)O:4D@P*P+:>G9J4
+MW%JF_BO]3RAAC7JK$>M&&0;$4[849W>M1JW9R+MZ/HI>ZXJ_"JE@KNZJE"HH
+MN*F0RK*ZR_V3`L%C\%R::!L5FG*MJJPMA*XJ*/"JD&O$BH$>KKFI=Q#M!4Z[
+MDW&SK4![OYZQFJZRK2VK]NFN@JQLAZ"`N?ZK-.M8Y[$6K,&K\AJ[#IB02L!9
+MU34\#\^3*D;I17'#M<JQ_JY6),7@N6ZA[VDQ=P1`%!5/_RB?OCB]F6:T1?`1
+M[!N8JD!.?'N.&/"^T@&'*;`)]FU["20,*=C`J+-I;9JZ;%*U)Z/J)R1D.28J
+M@P8\!E&3/!HU\:)<W1R0@L9NG9J/8P@2L,/4O,D@6#^XD>M6C#(L,I4^NF_J
+MK#(HO%J<"JDI*;#)DEIWNN9+NH-JI>R<4G?3\:\:I)CZOZ(!C^D:!:_)'?S9
+MP--\4JD&;-%'?W:?D>@D&J^ZH9#HV%EVTJV0"A)+Q+:=28!608D.D(?*"HO^
+M$7]7*:\)TW%PFR:*.9$JI2`HIJF+GK"S9@-'E;:D6.RN"9-JI>]?5QJ]C*6T
+MZ!=[EJ(7N:@>&J26L:OH"^"K<J$XI`"K)V"PL"@"BX3*!@RL<%JKQJL1[`0+
+MQ%6PGX'"B:`YH3T9![O'J0`?K#L4PA:EY.NPR?AP7!X2U[J>WF\F;`UZ:U:E
+M+FE^UVMR</J=#,O?M6%**A!WON9+ZBO7VIMY+Q[/_8KJR:_^*U-0OQHR]VO^
+MVF$6I,:$V#?_45TVWU`)GWV)J,)C\(AM!P<*[\>8=EDPDT,QJ)8Z1EHLNYLB
+MKG09/!)*K`MF&_XZ^T4CCX$]&LXJ?3`F-_,B[9LEYM.AS8ZFUNBH>K^YG0XD
+M.AM]>EF2!FE:NR:R(@`%^Q<TLJT<-=LN0+*LDB1;>U*R,*B.X<V.L.MG'RL'
+MV*8(VFZ*C-:OO:Q`2B.FL"OI*(O&MK"F[!8KT\6PU-$S^S(1(84;+FO#!DLX
+M;$)K<WQ4Y5$/RWR>$4`L:.K$VI]1[-MIH3ZNO.A+&XDNL8ZK,6K30K%2;"A1
+M`U"Q9FQ*Q\+FH%IL^^=K=K%&IAS[-=:Q:RD9VXP&M<*?5:K&OK"H;!NKRD(J
+M<*P5:I;>HDQMAW#'/K5Y[!YKTM*F"^T`V]!&?.JLEB,H"+(AZ`*+&\BS\R@]
+MZ][0M/$F4*7(H@;[[`7[R$Z.`>WN,]!6LH4$:"#"TJ#I:EH+=&&?[NR4"<\"
+M6:=I(\G)>E8XDT-;DH:R4FGY9]$.M<4?5GK*;K0,7!Y;OI)OKU`9<(V41CC>
+MN?JZ6:E4(`@0!WP(0.I[">Z-=0*J7ZE152V$9)WJLSJH.,Q_!&O]'TAI_+BW
+MCJ8F0`"IH>X0C*$98//XMB!JO&JZ$B\K:(5US1BKO:55$$T:HU>K,FHTF*@=
+MS;D:\:D``20=T**^J"5M*PN93C6Q+-Z*ZJ&VIN`CD[_2L&'JMP>C*GM98I\J
+MJ%ZWXB:[M+6V?L\JZA%F;JX0;1^&O^Z;J^VK&<HJ$D3CI)JN?"YQ@VT`IA$[
+M2A9E6"+P4CA48M9#?JWK6Q#[MFX!L@'<B=B2I!+2H"IL92O#%E2P8DE@Q:KM
+MB:Q2K6&D2>O(.*L!D7.#XA(.U>JBJDQDJZDG"L*$+K<FUIH!R;%3T2JD-!YH
+MMS=F]#FP:J[L:G_JK8*KNQRGQ9D%0P6L12/D3A(^`/2:O`H*]NB-&T_N1T(0
+M2X4S:4:VU+JBIE:X,E-W&TH$D`=K@"O$0KGG'@S*X3X0.6N:("(\K".K,9>]
+MWJO87L@EO!ZK'>N0*^7^JW$NB$FQ`DL=&M75Y#JO_"WZRLOZM]7D6)?.GH^"
+MPF([W2%/U6V<&]J.MD#N6&?F#K@[:_7JYBXL$NMI\.?*MA02!C/=8*S"Z_?J
+ML2*O-*AXN\E*2*W$(Y/+LJ^"J3AJ2ZZWYEY).VRNN#D3PN`NX4P*@H&5[,DP
+MVNH#2]_6MZ1M^2)PK48][*>W%X1QQBJDZ=N^!O)H=QNO2GRA!'?[[(ZOI*:Y
+M^>Q:J-)NF+OM"@IB+KCKV_ZK-NOD:HPVK&\NFDD)PF2Q+!F%KCX=ZBU"2^`Z
+MMJSNISN??K)39BB[OR6F60!T$00`-8/ICA,&X&ED`*WK(WI9TVSH<JB<)\Z!
+M1T=&N2V&3,10$)R1SV<0&T!"N^+N_0BIEJ1%Z;#Y3*42IY)PI!G!;1U#"\>\
+M+0PS!J3F:!1O5\"C%%FV!@59*O5DD6<#WZ=`P#QV$5_QYN>>>*S2H:*Q#0OF
+M+5*ZD:*\CQHD0@2$H%Z6U7J7[ILE;-*;BKZY+:>_XJ@Y`2JO`F@&G+S27?VV
+M]#Z]K$3Q%H!)=SWEZ#D'4`@\!1F04]QE'ZVUBM;2L^#NT$NI$0&6VDL)UQ9]
+M<BUIFMB^FNWID9KO.KVJW2#XD"AW1.'46_6^;LLH44@%]+N0"(%AG2:H3A[Y
+MAJ/"9UYN5G*9S0@\SL9VWEZI,AD%*40DK4#?F+JN!*C"'J@;^4(\!^IM*QQ)
+M,G(&>@.+.JJ)RL;;X9JT^PIZ8Q4U?&_5NL!ZC+X79L2GGTX'GBH#VTOJDM=H
+MC%I`(J=Z*1B(,(B!&.,WA"(,#/@I9K-A(JC9:?0H1HRI8T!M:?A)@%2>^NG1
+MNG*,'QNQ_#6%K`<0-P:(`7KJ\L<EUIZT38H[_0HV0!SE,AK8`;,D$%=+L@O4
+M08EZG6*]LYTCF#)1`2VFRR`%=%TL@!C`;,V_TT%9VG5)DRB``%P?E'3IKO[;
+M%_"_F([_"P`+P`D``5S_'L!_:@+,:C*CEFBZDH-FO55=F8!UG&P+;I<THJ$I
+MA!AWEII*62X:[0LLZ&S.YWXKV(0_?VJ``P7HJ<N*Q],@`!S1`=?;%WB]_DH%
+ME^Q]E(^DRT0#4WPW2N%&!+-Y.?"=PUKUP#2B$&RDZKR,)G^9R869>(&@X/X"
+M92DHF%?)EI@A0/6+R-JUJPQ0=AL)91S,&,I[<`$B`)C'!O<K78MZ%P(`M@;M
+M8&O=#IN!W5MEKL4(CQM><%7Z,W>"H$``0PFQZ/C+'K5,<,#<R_U:J-SF/4L&
+M`Z/^2LO&_"EAQ0<=?,G*NR7F(LS.(K)4B"^9@G)ZG)WT8P6#<FQP"3`'L,%<
+M@!N@+(K!QF@=C,GRL=27"4/<2EHC&NV+5V:<=,`7X`:TC((PY4*XI*#`P@M*
+MM=BJY6<N/#G\<KTPN_`+RRIOP!<\A.+"NG#+Z-XH#A!<,:P+OTOU[`<%P9+!
+M1>/%E%*N4E!!QO"UL0%S<$%["=.@7NF'6[_Z#08F[:MAR;A22A9L^$6`LBFQ
+M6Z^EIL"/-N/0MA&-;O\K1Z2@D&$(F@@_IB#FY^(&5KHEI@5<EHH`<,"L(A"S
+MI>%/*`$![GL9*O9"$(\I"D/(,VX`?%5AQ6LTW"BA!`3LT_S#(L!=H"RB,@.Q
+M20MB^F0;,55HF*!*;6;%T[%&@%)#X0;LUK@5CWLSS^Q[3N&&:MR"OV0`S>83
+M;ZM`<7);NXZ_QRN`UP]'P/\P4ASE@BH/L4XK!HROL'"#>OHN"'M"=8-0KKY`
+ML;X""3(!70"<">"9`"0""A`&T\1=X@K8!(3%3Q4$%P+<*+MH7<O[ML&:YSJ:
+MS[T!+,*"FP+_9-Q+9$68K$"SK?4V]"!7-17^4@ZWPN?P'8R!(I=38!6H<<E5
+MKHT.Q"R=);!H5ZP$LL5C\1*$%I,!7K$GB"]@QD(#I'I0[!&TJI9"1Q1N8+!E
+M#`F^G6)Q:,RP_`HZ1FD,G>`+%_$1*29N;/#P&B`/G[\V,8WK)^C&]:P)</X.
+MMT\"AWK^"L7$+8>J`AS'(6KM"G=HJ2*Q!*P;$ZQ0,::9"#\&T[$/T//1!\:H
+M;DP5HYDU@AF*)U`N!68*B@=PAOE;5:<EY3>D`>[%7_X`\/&K4/)&?`'F%3D>
+MLPOEL0B`!:#'-^2@0%,PF%'8@RF72IAV<2AG88*YZ:J&2?/P:V/P7`P)WSDC
+MF%UL"0NVK[!)>R\]P;W4@E#Y#@NX+TQ,T^VO.[!5$"/DN<^!W0=GG@%?0(@,
+M!5>VY%RM"?Q=ME<L1FO\;;:&6@*@(H_(SA_N^:?\GC:'$!`$5`%$@%.+U%FQ
+M.&AF6]3RH,`F)GHCOZAF`.5R(Z,`J,P82R-ZMO/NH@GAE"]QG+:Q&Q%[3JJ'
+M;#1P=3Q.T;<9=\9;X!0`&BL0YZ3%"29OOJJQ)Y@&GLG<<490O(4)/24VJ4V^
+M?'3RU%?&'J<SZA]7HPZ;QB]E.`OKIH\F4>=`E*@8:$K)NA#*2QDTO`O_'X*"
+M$W)+)`87##'L*&/#=B;V-`4\I@0R?UEA"@8(<D(<OW2J<;$)^@C;FY%PA#Q5
+M3L@'+0W*(E.G1:J%+(W6I9(MDAK\%G/:*"N1_`(CXTSSBU62HQPFX[N8?GFL
+MKA1QJ&`->F!8VH:IR48>FSPFHX&L<3V1'L_)Z$&=?)!DE<+D%I<GY[QR75=9
+M('_*N>]2-HYNH]^DX2NC+KY^,I&*!Q?+>,GIU'KVE'?$"X$H<Y7;Z(29SWVC
+M"')HD%66`>7R=%JCAKT8WZS\7M7*4V:2"KLQ"",875EH;,NNK9V\()>CBN#)
+M>YQ^R<URP=LF?P%O<FN,)A=N(RHPF>C""U\`>[0&],M"S6<K_LZ5V]8\PC`3
+MDVL`M@PQ+W<2L_3;BU;,8C)/G!;3@=%R_;%O=LR&Z;X),L\!4!C)?*F(MYDE
+M0[59^LJ6)1080[1,OT)]X+Z=(:"O?_=_K$J)H+GW*T3-D"F\=*BN3*6OQ,?^
+MUI)'R3<I+"^H=6'N-/O&?$Z9=I:L?`JCQH3[Y?:4:W+!"S97#36SYT`FKQ=P
+M,M,<6JZ5=X!$RR+SR>CRD,J<&@UML])\!^C-7RU2!RM#HP$S73HP1WRF:5Y:
+MS(V6T\9KR;J$/$@S:.G+CLU>J;@#&;O-O$^R%S?7S4YA#0@G)\Y5W_#;)PO.
+M3QU+4Q?@Q<HDZBS7_<MRJ;/@.,/#D;/)W"WG@+II.+I3)I/19+`<_0Z_P&8Z
+M.5MJET;F&8`\_P7[9GBI3)X!2^)H)CNOEP#SRB#NJ%J)Z@\I.M/,%_-@=CH+
+MR:FS@KHZP\I&P^<L-#^UB[/XYI4*S+CSUJL[6\_#5N.C&%*!C"$<^"D@S0;%
+MIIHE.,W7Y'B*-<MA]G,/:-LFIM+O=;DC>K0U99Z2:FHJ3)]=Y2OL;*+87#2:
+MR;BOLH[(.,//2@7JDUS2SV@**H<@C\X>-`@`!5W,)@Z<#`+X!R1TSAP:#Z2+
+MX&Z8KH;0H^EL7`3$JPFT//PCCF;&:)KHJ'2LT3-(IM,NSTQ!D&BK2BD]]((@
+M1!NC\0+R;+AJ*C"H\VQDZM`I,?E:01N%@*]Q>BXGI^GRX/Q"CZ?9IRU3!/ZW
+M2G"ZF\;X7W?$^:PXT\Y3<.,\C1+,>.^M3+X!EWXP4CC^2K>;\_"LH`*;(<`U
+M85;P<&GFE=PN1%4*Y_NP-Z<K?$1L7+E4QL_R'R@$G,YR<9A\,0."WW/YW``3
+MA0&8'LTR&*M?'MI2?'K19,#-]9\M*X-N!;S[WI(G`!<P+$[/@#,6S3K;G3YJ
+M,%E*G]),WV6!)NB=4\`X`4WB;+`+?<FC64FHDL+@QXEE5;20FCZGT7/I&@TY
+MN\_[KG*J(Q9S]])LEB(BS4IT,\@_=UD^]+1W"N:F[$(B"/W2>\1S@=L4P9XA
+M@NQ9ELUJU/2"4!F?@J/I#0V2N;X%<V.+\1FXA\H^J."6$D/='A?$GL4V,V>\
+M`F(O&C-]</?B;].L'_T.9L_A\LRL^5K,B70N&!*XJ'2O;48B^$21=.><"[E]
+M^V";"42_J#BT\"L^!\[D<U4'!>G)Z#,:K?6RIVYT[$9>0HD5M>^,V?30#37S
+M[-HV@V;J-=U!9].E+P'=3==Y\'3LR4O%4EL9.3U0G].V63I=1,L!['0;+:E^
+MT^F*/(VRT=,"GJ:*3]O-^S0CO>8:U19R)WE8GH(=,MS,/2?4`.%"'=<ZU"``
+M1-U/N],8M+EZ"IJ+F\/XRR3"U!KU"MD!9]&M<_'V4:>7PK1(K?=2U87O=&I!
+M,WKWTEL6&JQO*G6Q8V0R#?XI]3LO8,W"DRT6";E87;/5[*>E-\GJ^LL4B+_J
+MKX"*VTHR8"2V$2E!6;`H=3`YY"E%W[SP&,QC64`]IBP^5YLPI!(0%*VU:AB]
+ML-C4=[0!#9IZUO*",\DTC-;"F6G]&*#6&^[MY.KF`<4J*BI)O]93XEY-5^_)
+M5_0_G??"$2N`H'`#/*;E#`[#,!*N"T+19T,?UJ)7]1M."`JE]+>H256_+6KV
+M66*J`-KU:%HGG@#3+GD-!WRWW#47X%T#,^4NSEJ]@J;JM87:7:/7_W3'VT?Z
+ML1U#7`51]"6PZ&J-XEJY=2'$P+ED+6@*UB"E>J^$AZ`0-!PDH750''%>KV5K
+M_\)%7JO/,<5@73/-\P%""UL[>;(UL@#UPIO++=8#E`T[VMDI&)DQV$>$H+`?
+MF"<1-@R:SA(><VTZ)Z7.-OL4,".#ZM<%96RT#TJIVME4&/#1392?7.2]<M53
+M&0C@HIY[)K2@,`[G/INJ'("TBL%*]EMKC*:S2+97S62/U<`,=5#]Y@&D\\VL
+M4%?96W8]@0A[V,4U%0U2(W6I]$AM*TO.OZP+*]6FLM11-*I`R$1%1.;)KV!1
+M+7+)%K>E-ZJ$V\+HD;6VJ;9B,%&J^%RLVRZ(`;IE@]!T&*ZJ*\0,IS*:+7`W
+M^H6Q'@1:;,6^E%Z8<\1G!X0&5""8O6\.FT\`BM`6&4PI2[+WT:``E`NV4?!*
+MDUGG2`P%D-7"2\;@I<4(O&Q,%A;]6]E*3?E7L@A*PK`@ES(^DC`&Q5/B560`
+M4V$MW\F.Z!T`![BK=X"<V7$*"E>`$:`]QA9ZZAFP%(@!ATJCBW(,JNPSOHM7
+M6Z)$84`W(Z>Q=#9"1\R1;P4QU37NZ`?/Y^O&01)Z2,-Z:R+<,.[;7:))U7FW
+M=DQ&Z'T!:J]D3?'](#5P]1MQ<I"4P[\P+[NR3I\G-U3FRU07E'R7D-7"TWM%
+M\C[<;]5R<V#G#%>E#4WH2=SL$5E=!,PM(,]94ND6KN6UOI2X&MQTPGJ]`HC7
+MS%U];5ZGPNAU[4K'I=SW-;F;K,C7F>Y=S26EQ,ET%0LC%\E7*?)'OC$^;12E
+MI;:JU$,(J&*X5A&S7_$AJG*0Q0?:&4\^`41;2`6JI'54=Q4A`_S/GY36_2]T
+MS84?*N?UV*IEMS_C]7S=)BVG9Y08$;"GU!,>H!8M$@E,3QO21JL_\W3S!=_L
+M7<+A%-7)J.D+M.(V:M:8TEGKW5(WC'F74$^;\)$[!%H%L*<O-:T5>\TN<^=W
+MB\5?0(5IH2[>F+?F31;#U^Q1#(!Y?PAHRFC*>2<%&Z<A0]IL<<_4Q/G#QE++
+MY6C@-[5(Z&KNG,D2NY]!7D@7"&G)WK.@1HS,4C=9S01\"!1"YK3,50U(&L<=
+MQ/[>>W<9$'5[W4JO6ZI?GW3(#_']WG"98`SM?4R7I+XW\=U\_]UY*8M,),_9
+M-#*$F8V>$O!9;NIP*Y7\]8`5_JK4/9N)T!K$V)TO^TM[D`%8,Y'U=.E`%D1C
+MW3_?,MTO@]I58HUB9>%L#3_*AS-IRC$OEAYS\WPM<SO9,K4\'XO+^_+"7;ZY
+MWX.N)NQG9YN$]4S[F/YWG-4&#M?5#F7?MMUMF\9%@)XZ5C[,_3'#G""/RJ]F
+MJ6Q02[#Y+!`W6FJP"D3+AH19V@8RJ"S)L<IV\.T=C=[.(_CF((-ROR6U7+<V
+M/SY'F]J1L0;6\439.$:D`9#VMC)-YP5OZBH7:\UQW9S[Y@9DX0B@>*`D6$1>
+M5M?,03(=!_>8N@7#OV0U$0`QT-/H=RIQV/%9Z0H1]!E\%(-NB4D$%9B`-Q'D
+MA#+(M;&M'7;8D:_!5<G?KAI<L`C@A:`,@./<ZZCVI_IK.0?1S3&;JPCP`O@-
+M2`+3$;FQR.8V5FIKGK&8;=$-PW:V;NS14(E?XA_""Z")@\^6+2@>(^>@R1U'
+MJVZ;M&ZX(*[Z;M^@+/H]?S,=$JUX.P5$X:1!>\590^$1Q:MI!D3:KJT`"=MJ
+MX0(#%ZY2?^'+>!B^9I'A\(@9/O%E$A.@I/QVHL;7Y&/JAB^X<7B@_6C!+G5X
+M]288Q`F!+)S`A__3?KB/`XBKQ&,*G]5ILQ&A1!O`!D@C8+8(,`<#IS5%.*N-
+M.^(ATC\=(CVFA/8`FXIE7%?EBZJ+XZ^9!%F=KGRG4@[#:[H=%+V"F-87\-J5
+M,5]0.%BH,G2O6CO8X_EHP?#@D`$A`PS*,T>GQFA>R2[LDG!![4!BU0Y'>!).
+M;#98EI5WH&@G'PF$_':,6^').!B^ES;C5;.6`XW'DT&`&.X%&;9=,Y9IW11,
+MP+BX_7,7XU6X&]"+PZ=^55]:OHQ<;X"0]%:YNRHUMJ%DS0M1^16NC,>3CR50
+MSJ8*Y3YY43Z-'[S5N&WK-<^4-44VCE5LXUQ:-VXT_%-'86F0XB8`VQ/%YVR7
+MOW3DQ+=[G!Y>8ESH8D5NP^8W#H<?8^(X!@Y[VN'GN#T-FN[AL58?#B?\X1+M
+M#SJF>.7]@EO!9R7D3O8]#@+EXT"`LIA)^.-S.4`.>`_D\/BPE=IV;'R69+EH
+MCY"$]#]+&A_2$9\8(`]SVU+#WGUZ/`8RM-R+&EO>B7#M^N7Q0._4Y,J$5TH0
+M>:5D+.^HI,&UZFR'$@",$+&78ZLS3&94G$.L)::S#6U_`7=)%BPHZ.;3KHV,
+M_.0!/@0*$)Q;V3)3?)VS`C/3N:#@G?L`T7;92`9<YSIKNAH"Z`$"P5V2`"?"
+M+:OM#0N#1[*YOL0U,%FI3&OJ5B(CJ4QHVH\3#`Q%\\R@[P<..@G1I>X%IT<;
+MCJ9\YFZE0GXBS=\-^7#N)SBJJSE9_8M7A;*1VY>!).?QE-!J^VQE&5>!OOH>
+M3J!!>KZ>_]W*-5G],>!5V`H&91>TRV[%ZDL>5Q&^N7<`G/<5T?>;;=+R4/'4
+M89>!T%-93$I[5+XNZ$T9$VG9GO-YM!V>NZMBP!=0QM29L`$1,")=`3:Q/%R=
+MW^>]<7;N;,>K6;JT75^"Z=6V36.,QN<5PK--GW\!]OE>#@OOZ)CVF))[IVI4
+M59ZB\?%`VK?1T'U7(44ZTUBX[=@R.2O76#U4_5-?_!I\YBKU[I$GA.4]>33^
+MDRN;0;GLDI8;Y=0X$6*-"S;8^!LREW<!'5J>0I@[`8KD'LF-FS"%&UY.\>GF
+M@+DA.?&)`8%*#%"8BP"P5A,$!T1NP:RM+M@,YMZ!KCY^\NIRJO<(K#N`@CF&
+M[AW(`+MZK_XB(>95G6)N2C#F<SB458Y[(WBXIDJ9"PR6.73@CF?FU?H;?JVC
+M$I25<Y`KS8;UU!OH[@:Q*(ANSJ5[Z81,F#ZF]^&C1/+SCFOFDQ,D]W/E#$DW
+MZW%,44K*9IU<CXOF%44^/@*8Y@^ZJ7Y+J.8"N8F>5^$%!O%3@`KM+X$*/*R;
+M"ZR(Y%[N93LHL:BJ;J3KY?/ZI_(O3,L87Y:I28.A8$P;]0<K!""[O.X#=.DF
+M^V59&N3J*#L&G0.I5$R!+86JR:4HU:?`1[Q(+=//RB()3GPM/`P&VRLO4J(^
+M^XD`=\`+&J^"Y`%2"JJOH,K_BT5EJ=,N541)CD0SZ#&[G4ZS!RJI.AR0H5.W
+M[K,0KH8J#+$L["*C+^CX!D/>CV<./$"OWCZ"`#Z`M-X:?+`!;%G[QW+H]/A,
+M_*'+[20$66VJ$E:W\X/FK?%2952A_D+'J[($`<O:$NMMP$`IE,S05S9S%Y!O
+M<6H>A4!&73>(F/`JL]/L'2>8+@08`?()8<OF:++X,,;N-[WL7_9_7K*3[7B"
+MR*ZHF[2PBY#VIW=L#O2@?AHX[H<Z\.VLMP&8;/">J%<Z@RJ49#_?"3-Z]DFU
+M\PM6N[[5EY@!H%YTX+6[V5,F:R[>@IB':_'11@E<=F2CA.HA?.1Q\6&XCFN%
+M-?MK!HQK6#,K)WM/-^,:@KXEKS>OFYVJY9`\>$"O8)X0(8]!"5%@/J:75..:
+MY[@VO-@,0XAO.8>*FM;**5EU,M@.9A/B72KZSHW/!\&8@\VIOIJ$>(=3*:?I
+MVBK^CK?'G<^=GT"(;P'X^^DGCYX`+T"=^'F;H`02":\SX0$F?'O-!:CP),((
+M7\)OUR#`":`#H-?9YUB`OQ=N-OP+OP+$`.-GSFUV6=97,5/`KWR4W-Y_(CKO
+M[[#IT=JIKN]*E@:/&RC!+83N>;ES\*EG4J/E^`#89^U:8E+Q7,,6X,73`1IN
+M#N\"U-P0J_\^;:/@.O?->N[&FU#\6(`.DW41,MKB1AIK9[SR#9J:D<C($2NS
+MW6R`/%%MC!;R::32=\<C\KOGS_NRH0`C//9B7[O7-3<4A(V]`)F#?Y#!1YQ@
+M<"$/>%>Y$>?'#F:'DCOY)F\T5))HI*`0C4B`4CQ`.G'_GU4=G^[VY$[23P'?
+MN#KN,J@O;+Y/Y>:KF@GG>.^S-RD5OC>\XSO+I\L3Z9)V]?C"E[_7GW#7WDT3
+M0!R:[OU>?V;W^N[//*:R_+[!M2T(M\\O/\,D>P%![7J?AQ(VV\OFR6?N(_I]
+M'J`CHZ"\NBOLX;8L^G(N`CT^DVKNM`)9:U-J!"V>;"-=BWBW97K)8S3,#F:C
+M\BP3LV&GDP&Z,*ARFI99-W'1HW>3\C"F-;\?=#@KO'EPOOLSF'<90R),`5N@
+M$=`$3'<A/`K`T2<!C]0SE-)U],*=N[D)/Z8'KN;)V;DMVLH>/[%6QP$#W>2Z
+M!K$7_7Z0T=?K([U'#]*/ICU]2C_'3.CX*T0?VKEU4(#_2U:ONH]D-Z\7B=UE
+M`,Z+O;OR(3><G:X8`0F&$(`R\&]J&+KS>2%+'P$'I@*K8'W)FZ%Q86!RQ106
+MZKK'EG:+H#ET]9E!.O$]'%^LP9RP?-$&5)#SLAL4!\"!;W!8S`W,`;G`'Q3`
+MU<%UP'C"-=P!]`4>B`?D`1J3$:1%>)G4I<M@7^(7C9`>P`?MP7N`$(!?C"?H
+M!=>;.^8BCY@S]:\C;5Z0-@026P`.T`5L`3`!(PG$M6`D`A[@(2S:P,@,!N!)
+MDOV(RT1?9F%5@QF0VH^?>P!P(-RS`,-]<1_O577MTJO5<8IO;>K#9ER$Y1M=
+M"Q:(16#=:QO6IC)]:M88?DW^&Q13C!`?/^-,'UK/?_"\N,''T$'\)_JE72SC
+MPIOGHLO$!BA5-*1%7-6U8`/0N]X:O>5ZV6S/VC>1UUEQ,]O'"0/0GQ#;Y/!P
+M@"Y6+)P`=T"=R,*U8>W]K9ZAX@#R??'&M(N0*:K66#TR?<V!*`K&D"]R`"14
+M&E'P(_CV"P@S?E6=J[F\R[A6PKEHJ)C%X7&'$,*'`/4QQ4"SB;\_*^S9G"_6
+M^L2;EIUM8Z1!>L]?:HT^=T?>DBL2*CYSUVT+"AUYBE\:WO<)P/,N&J2@)<";
+M\VH)8G'#JO3!GHM!/I(^Y+_D]WWQ1HY+?UA+3#8%,1&<V'B_,HW@][W*6L^Z
+M"A`<:3#;[%CW/7L?O3CY_+((4`)`<2M8E4_>PV?XY.?4%YZ36+X,*N3?]US^
+MN5B\"6D^NC:#38>_#/Y]_]ZS`5N`)-]>>]?J,=QBKO<K<CC:TH+E`88W@T_D
+M"Z$H@/[\SQSL^'@*^N`T5J37^V"\)2.O#'"J5#&P99(ABX+7!X$^,HJ$M^2.
+M/GQO$Q3QE#RN7^2'^FG`-GO!AZ9*E;N)8S=#3'ZP7ZO:`+Y^`B#;@P:1/B6*
+M!\0`Y.]]_^S3`5L`$1]*G`!O0)WHHG[AUKY^WRUL^[U^:W3?_R"=L'&[VZ/V
+M$0,1SP+8^6-`NP(#A`$E`!FP!M3[!R(38`J;PFLB?[H4I`"NHV/0K:4!+,"C
+MSP)<^X!^(E.U$P?K_C&Q!;3[J'HK;[B>]@X_Q"_1GHL-OQ3Q\*\*3.RY>.[;
+M-D2(<7N7O/LE0+RO!@@!_7Y'`?!/^P+PD-^;!L"'L,0_<:?':*8,&GY<HU9"
+M"&>?.K>T"Y4_.`C(+*38!S*+S`8OC)33"MS8KPU)WZ[X,'Y\^<>2Q>%$C*_\
+MW+U6YKD8$,3YGSZHG_'[]FE`B,KD*_S0.\-/\?\%)'S$/^QG_')`V7_QDZ_G
+MXDUL\WRW[@US-.2C^^>)R'\(P_LFP,F?\O,4*S^UG_"__"._S!]RT_Q<?O&V
+M\XO@XOUI`/2+]C]ST`PQ-_B"3=*O?GK\OW[3WW!"_4U_@TGK3]5S/\CO"8_\
+M>+\:H"_Z^T1UP/_W+_PP_S$N^'^HA#^-B-4K/"@#A`EL)G=0?QY):5)2[EL;
+ML`;L:[0[T)*^3]:!2G\L4ATJ,CWL32<LX7!=[B]W],=*`J8P>!__7):"XF\)
+M!J_!=XJT51*?&B'5)PD-'\."3[_A]Q(@A`_+B_()\"%,,.C^@13!(*''[>7_
+M3AZHK/\-^L@O-\PV+T"%^0*DOFR'#.`#F`#%(=-KZWMR[3]>WM-L7G"S>?_5
+MWX!_"I^JSO"O9++7,?Z93NIW2+\'GT4,S43^`_WI"=)_E2PO&X5._O?^8^;%
+M_\Q_F0/ZG_T/_X<2V/_U_Q9U_S^.00`P^,4BH_VAO$P40P`QA'6JW":)8^",
+MWTI9YS:C%BI+%K?*NFID7GZ`0,`@H!!P"$@$+`(:`8^`2,`DH!)P"<@$;`(Z
+C`9^`4,`HH!1P"D@%K`):`:^`6,`LH!9P"\@%[`)Z`;^`8`$D
+`
+end
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..dcbc0b5
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile
@@ -0,0 +1,15 @@
+SUBDIR= Doc Etc
+
+FILES= README.FIRST
+
+beforeinstall:
+ for file in ${FILES}; \
+ do \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$$file ${DESTDIR}${BINDIR}/$$file ; \
+ done
+
+afterdistribute: beforeinstall
+
+.include "Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Misc/Makefile.inc b/usr.sbin/pcvt/Misc/Makefile.inc
new file mode 100644
index 0000000..6c5322a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile.inc
@@ -0,0 +1,3 @@
+DISTRIBUTION= doc
+BINDIR= /usr/share/pcvt
+BINMODE= 0444
diff --git a/usr.sbin/pcvt/Misc/README.FIRST b/usr.sbin/pcvt/Misc/README.FIRST
new file mode 100644
index 0000000..7194a72
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/README.FIRST
@@ -0,0 +1,291 @@
+================================================================================
+| |
+| P C V T - VT220 Terminal Emulation Driver |
+| ------------------------------------------- |
+| |
+| NetBSD 0.9, 1.0 |
+| FreeBSD 1.0, 1.1, 1.1.5.1, 2.0 |
+| |
+| Release 3.20 April 1995 |
+| |
+| ---------------------------- |
+| ========>> BETA 24 <<======= |
+| ---------------------------- |
+| |
+| (c) Copyright 1992,1993,1994,1995 by |
+| |
+| Hellmuth Michaelis |
+| Eggerstedtstrasse 28 |
+| 22765 Hamburg |
+| 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@hcs.de)
+
+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
+ - Help you to make money ....
+
+
+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
+
+
+Tested Operating Systems
+--------------------------------------------------------------------------------
+
+ NetBSD 0.9 pcvt release 3.20-b2 tested
+
+ NetBSD 1.0 pcvt release 3.20-b24 tested
+
+ NetBSD-current (post 1.0) reported to run (end of March '95)
+
+
+ FreeBSD 1.1R pcvt release 3.20-b7 tested
+
+ FreeBSD 1.1.5.1R pcvt release 3.20-b24 tested
+
+ FreeBSD 2.0 pcvt release 3.20-b24 tested
+
+ FreeBSD-current (post 2.0) reported to run (end of March '95)
+
+
+
+Installation / Upgrade
+--------------------------------------------------------------------------------
+
+ R E A D (!!!) THE INSTRUCTIONS IN THE Doc/INSTALL.xxxBSD FILES CAREFULLY !
+ ==========================================================================
+
+ Again: PLEASE R E A D T H E M !!!!! (Thank You! ;-)
+ ========================================
+
+ If you read them, you should have NO problems installing pcvt on your
+ system, if you don't read them, you'll probably run into problems ...
+
+ If you run into any difficulties, please read Doc/NotesAndHints !
+
+NOTE 1:
+-------
+ It is highly recommended in order to configure the driver into the system,
+ that you remove (and/or backup) your previous kernel compile directory and
+ do a fresh "config" with the new pcvt configuration. This has to be done
+ because the chain "config/make depend/make" obviously does not resolve ALL
+ dependencies!
+
+NOTE 2:
+-------
+ You MUST copy or link the Util/Makefile.inc.X for your flavour of xxx(x)BSD
+ to Util/Makefile.inc . This is because FreeBSD handles manual pages
+ in a differnet way than NetBSD. Also you have to edit Doc/Makefile
+ to make this changes for the pcvt(4) manpage.
+
+NOTE 3:
+-------
+ The driver now (from 2.10 on) DEPENDS on the BIOS video display setting
+ stored in the RTC CMOS Ram - verify your configuration setting!
+
+NOTE 4:
+-------
+ If you are using the pcvt termcap entry from Etc/Termcap, please reinstall
+ this into /usr/share/misc/termcap if you are upgrading, the entry had bugs
+ in release 3.10 and all earlier releases.
+
+ After doing that, you may need to generate a new termcap database for newer
+ FreeBSD (2.0 and up) and NetBSD (1.0 and up) Releases:
+ cd to /usr/share/misc and execute 'cap_mkdb termcap'.
+
+NOTE 5:
+-------
+ The default keyboard layout is documented in Doc/Keyboard.HP unless you
+ compiled with PCVT_VT220KEY which is described in Doc/Keyboard.VT. Please
+ note that PCVT_VT220KEYB is not much supported, because i don't use it.
+
+NOTE 6:
+-------
+ When upgrading from a previous version of the driver, you can remove now
+ the directory /usr/share/misc/vgafonts. It was moved in release 3.20 to
+ /usr/share/misc/pcvtfonts.
+
+NOTE 7:
+-------
+ In case you don't like pcvt's white on red kernel messages, have a look at
+ the end of pcvt_conf.h, this is the place to change them.
+
+
+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@hcs.de or
+ hm@altona.hamburg.com
+
+I will support this driver as my time permits it, feel free to contact me!
+
+Have fun!
+
+Hellmuth
+
+
+@home
+-----
+e-mail: hm@altona.hamburg.com
+ tel: +49 / 40 / 384298
+s-mail: Eggerstedtstr. 28, 22765 Hamburg, Europe
+
+
+@work
+-----
+e-mail: hm@hcs.de
+ tel: +49 / 40 / 55903-170
+ fax: +49 / 40 / 5591486
+s-mail: GFKT HCS Computertechnik GmbH, Oldesloer Str. 97-99,
+ 22457 Hamburg, Europe
diff --git a/usr.sbin/pcvt/cursor/Makefile b/usr.sbin/pcvt/cursor/Makefile
new file mode 100644
index 0000000..b5afd6c
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/Makefile
@@ -0,0 +1,3 @@
+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..7333108
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.1
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1992,1993,1994 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 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.
+.\"
+.\" @(#)cursor.1, 3.20, Last Edit-Date: [Mon Dec 19 20:30:40 1994]
+.\"
+.Dd December 19, 1994
+.Dt CURSOR 1
+.Sh NAME
+.Nm cursor
+.Nd set cursor shape for the pcvt VT220 video driver
+.Sh SYNOPSIS
+.Nm cursor
+.Op Fl d Ar device
+.Op Fl n Ar screenno
+.Op Fl s Ar lineno
+.Op Fl e Ar lineno
+.Sh DESCRIPTION
+The
+.Nm cursor
+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..1c7c140
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.c
@@ -0,0 +1,157 @@
+/*
+ * 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]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm adding option -d <device>
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ 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)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(screen == -1)
+ {
+ struct stat stat;
+
+ if((fstat(fd, &stat)) == -1)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+
+ screen = minor(stat.st_rdev);
+ }
+
+ cursorshape.start = start;
+ cursorshape.end = end;
+ cursorshape.screen_no = screen;
+
+ if(ioctl(fd, VGACURSOR, &cursorshape) == -1)
+ {
+ perror("cursor - ioctl VGACURSOR failed, error");
+ exit(1);
+ }
+ 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 (/dev/ttyvX), default current\n");
+ 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..59e8ad6
--- /dev/null
+++ b/usr.sbin/pcvt/demo/Makefile
@@ -0,0 +1,54 @@
+PROG= playvt
+SRCS= playvt.c
+DEMOS= chardemo.vt colors.vt sgr.vt
+DEMOS+= outerlimit.vt twzone.vt cowscene.vt xmas.vt
+NOMAN=
+
+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>
+
+CLEANFILES+= ${DEMOS}
+
+# 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..efcd4c7
--- /dev/null
+++ b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
@@ -0,0 +1,53 @@
+begin 664 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..1d5d0da
--- /dev/null
+++ b/usr.sbin/pcvt/demo/colors.vt.gz.uu
@@ -0,0 +1,15 @@
+begin 664 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..b34d798
--- /dev/null
+++ b/usr.sbin/pcvt/demo/playvt.c
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ *
+ */
+
+static char *id =
+ "@(#)playvt.c, 1.00, Last Edit-Date: [Sun Jan 1 18:32:22 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm want to see my xmas greeting ... :-)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ 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..099c3a4
--- /dev/null
+++ b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
@@ -0,0 +1,11 @@
+begin 664 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..c96ad03
--- /dev/null
+++ b/usr.sbin/pcvt/fed/Makefile
@@ -0,0 +1,29 @@
+
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+#CFLAGS = -g -Wall -pipe
+CFLAGS = -O -Wall -pipe -I/usr/local/include
+OBJS = fed.o select.o edit.o misc.o
+LIB = ncurses
+DEST = /usr/local/bin
+
+fed: $(OBJS)
+ $(CC) -o fed $(OBJS) -l$(LIB) -L/usr/local/lib
+
+$(OBJS): fed.h
+
+clean:
+ rm -f *.o fed *core* trace*
+
+install:
+ ${INSTALL} -c -s -o bin -g bin fed $(DEST)
+
+.endif
diff --git a/usr.sbin/pcvt/fed/edit.c b/usr.sbin/pcvt/fed/edit.c
new file mode 100644
index 0000000..67d616b
--- /dev/null
+++ b/usr.sbin/pcvt/fed/edit.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by 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 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 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, 3.00, last edit-date: [Sun Jan 2 20:08:27 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * edit.c font editor edit character
+ * ------------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm some debugging & cleanup
+ *
+ *---------------------------------------------------------------------------*/
+
+#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.c b/usr.sbin/pcvt/fed/fed.c
new file mode 100644
index 0000000..d2fa4f3
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by 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 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 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, 3.00, last edit-date: [Sun Jan 2 20:08:45 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * fed.c font editor main file
+ * -------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm activating font save
+ *
+ *---------------------------------------------------------------------------*/
+
+#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, 1994 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..0911d37
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by 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 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 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.h, 3.00, last edit-date: [Sun Jan 2 20:10:31 1994]
+ */
+
+#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..c6707ba
--- /dev/null
+++ b/usr.sbin/pcvt/fed/misc.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by 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 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 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, 3.00, last edit-date: [Sun Jan 2 20:09:21 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * misc.c font editor misc routines
+ * -----------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm writefont routine
+ *
+ *---------------------------------------------------------------------------*/
+
+#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..b6993bb
--- /dev/null
+++ b/usr.sbin/pcvt/fed/select.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by 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 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 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, 3.00, last edit-date: [Sun Jan 2 20:09:36 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * select.c font editor select character
+ * ----------------------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm debugging
+ *
+ *---------------------------------------------------------------------------*/
+
+#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..9cbe4e5
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/Makefile
@@ -0,0 +1,3 @@
+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..8b55edb
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.1
@@ -0,0 +1,58 @@
+.TH FONTEDIT 1 LOCAL
+.SH NAME
+fontedit \- Edit fonts.
+.SH SYNOPSIS
+.B fontedit file
+.SH DESCRIPTION
+.I Fontedit
+is used to edit the down line reloadable character set (DRCS) of a 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
+.I Fontedit
+takes one command line parameter, a file name. This file is
+used to save the character set. If the file exists when \fIfontedit\fP
+is invoked, it is read in to initialize the DRCS. The file is written
+to when \fIfontedit\fP exits.
+.PP
+Commands to fontedit take the form of function keys. The current
+definitions are:
+.IP \fBHELP\fP
+Display a help screen.
+.IP \fBF6\fP
+Turn the pixel under the cursor on.
+.IP \fBF7\fP
+Turn the pixel under the cursor off.
+.IP \fBF13\fP
+Clear the display area.
+.IP \fBFind\fP
+Save the current font in the font table. Update the DRCS display.
+.IP \fBSelect\fP
+Extract the entry selected by the cursor in the DRCS display.
+.IP \fBPrev\fP
+Move the cursor to the previous entry in the DRCS display.
+.IP \fBNext\fP
+Move the cursor to the next entry in the DRCS display.
+.IP \fBInsert\fP
+Insert a blank line at the current cursor position. The bottom row is lost.
+.IP \fBRemove\fP
+Remove the row at the current cursor position. All rows below the
+current one are shifted up.
+.IP \fBCursors\fP
+Move the cursor in the main display area.
+.PP
+If the screen gets garbled, press <control-L>.
+.PP
+To exit \fIfontedit\fP, press <control-D>. The DRCS will be saved in
+\fIfile\fP. To exit without saving the DRCS, hit interrupt (usually
+DEL).
+.SH DIAGNOSTICS
+.I Fontedit
+will issue a warning when the entry being worked on is not saved, and
+some potentially destructive command, like \fBSelect\fP is used. To
+override the warning message, immediately reissue the command.
+.SH AUTHOR
+Greg Franks.
+
+
diff --git a/usr.sbin/pcvt/fontedit/fontedit.c b/usr.sbin/pcvt/fontedit/fontedit.c
new file mode 100644
index 0000000..d9ed357c
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.c
@@ -0,0 +1,925 @@
+/*
+ * 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
+ */
+
+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..59ed20b
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/Makefile
@@ -0,0 +1,19 @@
+# $Id: Makefile,v 1.7 1997/12/20 00:40:05 bde Exp $
+
+.SUFFIXES: .uu
+.uu:
+ rm -f ${.TARGET}
+ uudecode ${.IMPSRC}
+
+FONTS= vt220h.808 vt220h.810 vt220h.814 vt220h.816 \
+ vt220l.808 vt220l.810 vt220l.814 vt220l.816
+#FONTS+= vt100pc.814 vt100sg.814
+CLEANFILES= ${FONTS}
+
+all: ${FONTS}
+
+beforeinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${FONTS} ${DESTDIR}${FONTDIR}
+
+.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..a5a5cef
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/Makefile
@@ -0,0 +1,4 @@
+PROG= ispcvt
+MAN8= ispcvt.${MAN8EXT}
+
+.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..7c9108b
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.8
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1992, 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 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.
+.\"
+.\" @(#)ispcvt.1, 3.20, Last Edit-Date: [Tue Apr 4 12:35:54 1995]
+.\"
+.Dd April 4, 1995
+.Dt ISPCVT 8
+.Sh NAME
+.Nm ispcvt
+.Nd verify if current video driver is pcvt driver
+.Sh SYNOPSIS
+.Nm ispcvt
+.Op Fl c
+.Op Fl d Ar device
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm ispcvt
+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 ispcvt
+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 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.
+.Sh RETURN VALUE
+.Bl -tag -width Ds
+.Pp
+.It Sy 0
+driver is pcvt and major and minor numbers match
+.It Sy 1
+open or ioctl system call failed
+.It Sy 2
+driver name mismatch
+.It Sy 3
+name matched, release major number mismatch
+.It Sy 4
+name & major number matched, release minor number mismatch
+.It Sy 5
+usage error
+.El
+.Pp
+.Sh BUGS
+No known bugs
+.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..813e9dd
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1992, 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.
+ *
+ */
+
+static char *id =
+ "@(#)ispcvt.c, 3.20, Last Edit-Date: [Wed Apr 5 17:53:28 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm upgraded to report pcvt compile time configuration
+ * -hm PCVT_INHIBIT_NUMLOCK patch from Joerg
+ * -hm PCVT_META_ESC patch from Joerg
+ * -hm PCVT_PCBURST
+ * -hm new ioctl VGAPCVTINFO
+ * -hm new CONF_ values for 3.10
+ * -hm new CONF_ values for 3.20
+ * -hm removed PCVT_FAKE_SYSCONS10
+ * -hm added PCVT_PORTIO_DELAY
+ * -hm removed PCVT_386BSD
+ * -hm add -d option to specify a device
+ * -hm PCVT_XSERVER -> XSERVER
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ struct pcvtid pcvtid;
+ struct pcvtinfo pcvtinfo;
+ int c;
+ char *p;
+ int verbose = 0;
+ int config = 0;
+ int dflag = 0;
+ int fd;
+ char *device;
+
+ while( (c = getopt(argc, argv, "vcd:")) != -1)
+ {
+ switch(c)
+ {
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'c':
+ config = 1;
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ if(verbose)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ }
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(ioctl(fd, VGAPCVTID, &pcvtid) == -1)
+ {
+ if(verbose)
+ perror("ispcvt - ioctl VGAPCVTID failed, error");
+ exit(1);
+ }
+
+ if(!strcmp(pcvtid.name, PCVTIDNAME))
+ {
+ if(pcvtid.rmajor == PCVTIDMAJOR)
+ {
+ if(pcvtid.rminor != PCVTIDMINOR)
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - minor revision: expected %d, got %d\n", PCVTIDMINOR, pcvtid.rminor);
+ exit(4); /* minor revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - major revision: expected %d, got %d\n", PCVTIDMAJOR, pcvtid.rmajor);
+ exit(3); /* major revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - name check: expected %s, got %s\n", PCVTIDNAME, pcvtid.name);
+ exit(2); /* name mismatch */
+ }
+
+ if(verbose)
+ {
+ fprintf(stderr,"\nispcvt: kernel and utils match, driver name [%s], release [%1.1d.%02.2d]\n\n",pcvtid.name,pcvtid.rmajor,pcvtid.rminor);
+ }
+
+ if(config == 0)
+ exit(0);
+
+ if(ioctl(fd, VGAPCVTINFO, &pcvtinfo) == -1)
+ {
+ if(verbose)
+ perror("ispcvt - ioctl VGAPCVTINFO failed, error");
+ exit(1);
+ }
+
+ if(verbose)
+ {
+ switch(pcvtinfo.opsys)
+ {
+ case CONF_NETBSD:
+ p = "PCVT_NETBSD";
+ break;
+
+ case CONF_FREEBSD:
+ p = "PCVT_FREEBSD";
+ break;
+
+ default:
+ case CONF_UNKNOWNOPSYS:
+ p = "UNKNOWN";
+ break;
+
+ }
+ fprintf(stderr,"Operating System = %s\t", p);
+ fprintf(stderr,"OS Release Id = %u\n", pcvtinfo.opsysrel);
+ 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_BACKUP_FONTS = %s",
+ (pcvtinfo.compile_opts & CONF_BACKUP_FONTS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_CTRL_ALT_DEL = %s",
+ (pcvtinfo.compile_opts & CONF_CTRL_ALT_DEL) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_EMU_MOUSE = %s",
+ (pcvtinfo.compile_opts & CONF_EMU_MOUSE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_INHIBIT_NUMLOCK = %s",
+ (pcvtinfo.compile_opts & CONF_INHIBIT_NUMLOCK) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_KEYBDID = %s",
+ (pcvtinfo.compile_opts & CONF_KEYBDID) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_KBD_FIFO = %s",
+ (pcvtinfo.compile_opts & CONF_KBD_FIFO) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_META_ESC = %s",
+ (pcvtinfo.compile_opts & CONF_META_ESC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NOFASTSCROLL = %s",
+ (pcvtinfo.compile_opts & CONF_NOFASTSCROLL) ? "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_PALFLICKER = %s",
+ (pcvtinfo.compile_opts & CONF_PALFLICKER) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PORTIO_DELAY = %s",
+ (pcvtinfo.compile_opts & CONF_PORTIO_DELAY) ? "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_SIGWINCH = %s",
+ (pcvtinfo.compile_opts & CONF_SIGWINCH) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SLOW_INTERRUPT = %s",
+ (pcvtinfo.compile_opts & CONF_SLOW_INTERRUPT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SW0CNOUTP = %s",
+ (pcvtinfo.compile_opts & CONF_SW0CNOUTP) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USEKBDSEC = %s",
+ (pcvtinfo.compile_opts & CONF_USEKBDSEC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USL_VT_COMPAT = %s",
+ (pcvtinfo.compile_opts & CONF_USL_VT_COMPAT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_VT220KEYB = %s",
+ ((u_int)pcvtinfo.compile_opts & (u_int)CONF_VT220KEYB) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_WAITRETRACE = %s",
+ (pcvtinfo.compile_opts & CONF_WAITRETRACE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"XSERVER = %s",
+ (pcvtinfo.compile_opts & CONF_XSERVER) ? "ON" : "OFF");
+
+ fprintf(stderr,"\n\n");
+ }
+ else /* !verbose */
+ {
+ fprintf(stderr,"BSD Version = %u\n", pcvtinfo.opsys);
+ 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 [-v] [-c] [-d device]\n");
+ fprintf(stderr,"options: -v be verbose\n");
+ fprintf(stderr," -c print compile time configuration\n");
+ fprintf(stderr," -d <name> use devicefile <name> for verification\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..08cbf3b
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/Makefile
@@ -0,0 +1,36 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= kbdio
+SRCS= kbdio.y lex.l y.tab.h
+
+#YACC= bison
+#YFLAGS+= -yd # Bison only
+
+YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+NOMAN=
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR} #-g
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "kbdio is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/kbdio/kbdio.y b/usr.sbin/pcvt/kbdio/kbdio.y
new file mode 100644
index 0000000..8f825f1
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/kbdio.y
@@ -0,0 +1,338 @@
+/* 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 "$Header: /home/ncvs/src/usr.sbin/pcvt/kbdio/Attic/kbdio.y,v 1.2 1995/04/01 17:16:26 joerg Exp $"
+
+/*
+ * $Log: kbdio.y,v $
+ * Revision 1.2 1995/04/01 17:16:26 joerg
+ * Update pcvt to 3.20b23
+ *
+ * Submitted by: Hellmuth Michaelis <hm@altona.hamburg.com>
+ *
+ * Revision 1.1.1.1 1995/02/05 13:49:24 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * 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 <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..b7592fe
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/lex.l
@@ -0,0 +1,107 @@
+%{
+/*
+ * 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 "$Header: /home/ncvs/src/usr.sbin/pcvt/kbdio/Attic/lex.l,v 1.2 1995/04/01 17:16:27 joerg Exp $"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.2 1995/04/01 17:16:27 joerg
+ * Update pcvt to 3.20b23
+ *
+ * Submitted by: Hellmuth Michaelis <hm@altona.hamburg.com>
+ *
+ * Revision 1.1.1.1 1995/02/05 13:49:24 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * 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..77a9f26
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/Makefile
@@ -0,0 +1,14 @@
+PROG= kcon
+DEVICE= /dev/ttyv0
+CFLAGS+= -I${.CURDIR}/../keycap -DKEYB_DEVICE=\"${DEVICE}\"
+
+.if exists(${.OBJDIR}/../keycap)
+LIBKEYCAP= ${.OBJDIR}/../keycap/libkeycap.a
+.else
+LIBKEYCAP= ${.CURDIR}/../keycap/libkeycap.a
+.endif
+
+DPADD= ${LIBKEYCAP}
+LDADD= ${LIBKEYCAP}
+
+.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..b430340
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.1
@@ -0,0 +1,122 @@
+.\" Copyright (c) 1992,1993,1994 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 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.1, 3.20, Last Edit-Date: [Wed Jan 25 16:34:56 1995]
+.\"
+.Dd January 25, 1995
+.Dt KCON 1
+.Sh NAME
+.Nm kcon
+.Nd keyboard control and remapping
+.Sh SYNOPSIS
+.Nm kcon
+.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
+.Nm kcon
+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 (
+.Ar +
+) or disable (
+.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.
+.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.
+.Sh SEE ALSO
+.Xr keycap 3 ,
+.Xr keycap 5
+.Sh BUGS
+.Nm kcon
+detects several inconsistencies in the keycap database. In case of errors
+.Nm kcon
+exits with an error message. If this happens, the keyboard may remain in
+an undefined state. To recover from such situation, execute
+.Nm 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.
+
+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..1f9cded
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 1992,1993,1994 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.
+ *
+ */
+
+static char *id =
+ "@(#)kcon.c, 3.20, Last Edit-Date: [Wed Jan 25 16:33:08 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * kcon.c Keyboard control and remapping
+ * ----------------------------------------------
+ *
+ * based on "keymap" which was written by
+ * Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * -hm a first rewrite
+ * -hm rewrite for pcvt 2.0 distribution
+ * -hm adding show current typematic values
+ * -hm hex/octal/esc output choices
+ * -hm remapping debugging
+ * -hm garbage output for remapped keys bugfix
+ * -hm patch from Lon Willet, adding -R
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.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[];
+{
+ extern char *optarg;
+ extern int optind;
+
+ 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..cc6767d
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/Makefile
@@ -0,0 +1,32 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+LIB = keycap
+CAPDIR = /usr/share/misc
+CAPPATH = $(CAPDIR)/keycap.pcvt
+KEYCAPSRC= keycap.src
+CFLAGS += -DKEYCAP_PATH=\"$(CAPPATH)\"
+SRCS = keycap.c
+MAN3 = keycap.${MAN3EXT}
+MLINKS+= keycap.${MAN3EXT} kgetent.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetnum.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetflag.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetstr.${MAN3EXT}
+MAN5 = man5/keycap.${MAN5EXT}
+#CLEANFILES+= keycap.0 man5/keycap.0
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${.CURDIR}/${KEYCAPSRC} ${DESTDIR}${CAPPATH}
+
+.include <bsd.lib.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/keycap/keycap.3 b/usr.sbin/pcvt/keycap/keycap.3
new file mode 100644
index 0000000..b62b427
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.3
@@ -0,0 +1,124 @@
+.\"
+.\" 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]
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 3
+.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.
+.Pp
+.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..9bbb7e5
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.c
@@ -0,0 +1,377 @@
+/*-
+ * 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.
+ *
+ */
+
+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 "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(2,"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(2, "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(2, "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(2, "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..d4545ba
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.src
@@ -0,0 +1,630 @@
+# Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis, 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.
+#
+# @(#)keycap.src, 3.20, Last Edit-Date: [Wed Mar 8 20:53:01 1995]
+#
+#---------------------------------------------------------------------------
+#
+# keyboard mappings for vt220 emulator pcvt 3.00
+# ----------------------------------------------
+#
+# DEC MCS and/or ISO-Latin-1 Characterset used
+#
+# MF II Keyboards fully supported
+# AT Keyboards lack a ALTGR Key, so they cannot be handled by
+# the current driver implementation .... sorry
+#
+# If you design a new entry for national keyboards, please
+# send it to hm@hcshh.hcs.de, thank you !
+#
+# Many entries are taken from the 386BSD patchkit 0.2.4 codrv
+#
+#---------------------------------------------------------------------------
+#
+# -hm patch from Thomas Gellekum
+# -hm renamed finnish "f8" entry to "f1"
+#
+#---------------------------------------------------------------------------
+
+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=/:S9=(:S10=):S11==:\
+ :K12=':S12=\077:\
+ :K13=\354:S13=\136:\
+ :K27=\350:S27=\351:\A27=[:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\362:S40=\347:A40=@:\
+ :K41=\340:S41=\260:A41=#:\
+ :K42=\371:S42=\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:
+
+#
+# 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..d15cd5b
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/man5/keycap.5
@@ -0,0 +1,130 @@
+.\"
+.\" 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]
+.\" $Id: keycap.5,v 1.5 1997/02/22 16:09:10 peter Exp $
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 5
+.Sh NAME
+.Nm keycap
+.Nd keyboard mapping data base
+.Sh SYNOPSIS
+.Nm keycap
+.Sh DESCRIPTION
+The
+.Nm keycap
+file
+is a data base describing keyboard mappings, used by
+.Xr kcon 1 .
+.Pp
+Entries in
+.Nm keycap
+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
+.Pp
+.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 "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 "K<n> str bind a string to a 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 a altgr key
+
+.It "tc str Entry of similar map \- must be last."
+.El
+
+Parameter <n> describing the key number can have values from 1 to 128.
+
+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 keycap
+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.
+
+The entry
+.Dq Li K100=hugo
+binds the string 'hugo' to the key number 100.
+
+The entry
+.Dq Li K100=^D
+binds the control character EOT (0x04) to the key number 100.
+
+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..c0f454f
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/Makefile
@@ -0,0 +1,3 @@
+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..64fd3de
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1992, 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 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.
+.\"
+.\" @(#)loadfont.1, 3.20, Last Edit-Date: [Tue Apr 4 13:06:00 1995]
+.\"
+.Dd April 4, 1995
+.Dt LOADFONT 1
+.Sh NAME
+.Nm loadfont
+.Nd is used to load fonts into EGA or VGA boards for use by the 'pcvt' video
+driver.
+.Sh SYNOPSIS
+.Nm loadfont
+.Op Fl c Ar charsetno
+.Op Fl d Ar devicefile
+.Op Fl f Ar fontfilename
+.Op Fl i
+.Sh DESCRIPTION
+The
+.Nm loadfont
+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 charactersets available.
+.Sh FILES
+The following fontfiles are available in the pcvt distribution:
+
+.nf
+/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
+.fi
+.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..ce4abc3
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.c
@@ -0,0 +1,345 @@
+/*
+ * 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 =
+ "@(#)loadfont.c, 3.20, Last Edit-Date: [Fri Apr 7 10:13:16 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * load a font into vga character font memory
+ *
+ * -hm removing explicit HGC support (same as MDA ..)
+ * -hm new pcvt_ioctl.h SIZ_xxROWS
+ * -hm add -d option
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.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[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ 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)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(info == 1)
+ {
+ int i;
+
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+
+ 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)
+ {
+ char buffer[80];
+ sprintf(buffer, "cannot open file %s for reading", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((fstat(fileno(in), sbp)) != 0)
+ {
+ char buffer[80];
+ sprintf(buffer, "cannot fstat file %s", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ 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:
+ fprintf(stderr,"error, file %s is no valid font file, size=%d\n",argv[1],sbp->st_size);
+ exit(1);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ {
+ fprintf(stderr,"error, malloc failed\n");
+ exit(1);
+ }
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ {
+ fprintf(stderr,"error reading file %s, size = %d, read = is no valid font file, size=%d\n",argv[1],sbp->st_size, ret);
+ exit(1);
+ }
+
+ 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)
+ {
+ perror("loadfont - ioctl VGASETFONTATTR failed, error");
+ exit(1);
+ }
+}
+
+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)
+ {
+ perror("loadfont - ioctl VGALOADCHAR failed, error");
+ exit(1);
+ }
+ }
+}
+
+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)
+ {
+ perror("loadfont - ioctl VGAGETFONTATTR failed, error");
+ exit(1);
+ }
+ 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/mcon/Makefile b/usr.sbin/pcvt/mcon/Makefile
new file mode 100644
index 0000000..1a3b934
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/Makefile
@@ -0,0 +1,3 @@
+PROG= mcon
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/mcon/mcon.1 b/usr.sbin/pcvt/mcon/mcon.1
new file mode 100644
index 0000000..61bd467
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/mcon.1
@@ -0,0 +1,166 @@
+.\" Copyright (c) 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.
+.\" 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 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.
+.\"
+.\" @(#)mcon.1, 3.00, Last Edit-Date: [Mon Jan 10 21:28:22 1994]
+.\"
+.Dd January 3, 1994
+.Dt MCON 1
+.Sh NAME
+.Nm mcon
+.Nd controls pcvt mouse emulator
+.Sh SYNOPSIS
+.Nm mcon
+.Op Fl l Ar left-button-key
+.Op Fl m Ar mid-button-key
+.Op Fl r Ar right-button-key
+.Op Fl a Ar accel-time
+.Op Fl s Ar 0 | false | \&no
+.Op Fl s Ar 1 | true | yes
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm mcon
+utility controls the configurable parameters for the mouse emulator of
+.Xr pcvt 4 .
+.br
+.Em NB :
+The mouse emulator is not configured in by default; the system's config
+file needs to specify an option line
+
+.Em options Dq PCVT_EMU_MOUSE
+
+in order to get its functionality.
+.Pp
+Either way, the
+.Nm
+program must be called with an argument
+.Ar device
+that specifies the device node used for the mouse emulation. This is
+usually the first device node of the
+.Xr pcvt 4
+driver not being used as a virtual terminal device. E.\ g., if you
+have configured eight virtual terminals
+.Pq the default value ,
+named
+.Pa /dev/ttyv0
+through
+.Pa /dev/ttyv7 ,
+the mouse emulator would allocate
+.Pa /dev/ttyv8 .
+
+If
+.Nm
+is called without any option, it will print the actual values of the
+configurable parameters.
+
+If called with an option, the program attempts to set up the new value.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl l Ar left-button-key
+.It Fl m Ar mid-button-key
+.It Fl r Ar right-button-key
+Maps the named
+.Ar button key
+to emulate either the left, middle, or right mouse button.
+.Ar Button key
+is the usual name for that key. Normal ASCII keys are denoted by the
+character they're labeled with, function keys are named
+.Em f1
+through
+.Em f10 .
+Note that the AT function keys
+.Em f11
+and
+.Em f12
+are
+.Em extended
+keys that cannot be mapped to be used with the mouse emulator since
+it only allows basic PC-scancode keys to be used.
+
+.It Fl a Ar accel-time
+Set the time limit for the internal accelerator to
+.Ar accel-time
+milliseconds. Key events occurring after a longer time than this limit
+will move the mouse cursor in single steps. Key events arriving more
+frequently will move the cursor accelerated by a factor of 6. Note that
+despite of
+.Em milliseconds
+being the unit of choice here, the time resolution is restricted by the
+timer tick distance of the underlying operating system, usually to a
+granularity of 10 milliseconds.
+
+.It Fl s Ar 0 | false | \&no
+.It Fl s Ar 1 | true | yes
+The first form disables, the second form enables the
+.Em sticky
+behaviour of the mouse buttons. Sticky mouse keys behave much like
+toggle-buttons: on first press, they become active, on second press,
+they're deactivated. Pressing another button will deactivate any
+other sticky button anyway.
+
+Sticky buttons might be more convenient since you don't need 20 fingers
+at all; on the other hand, they make it virtually impossible to initiate
+double or triple mouse clicks.
+.El
+.Sh EXAMPLES
+The following example would install the default behaviour of the
+mouse emulator:
+
+.Nm mcon
+.Fl l Ar f1
+.Fl m Ar f2
+.Fl r Ar f3
+.Fl a Ar 250
+.Fl s Ar \&no
+.Pa /dev/ttyv8
+.Sh BUGS
+The key names used to map the button-emulating keys to scan codes
+.Pq and vica verse
+are based on the American keyboard layout. This would usually not
+cause any trouble since the
+.Dq button-of-choice
+is certainly some function key that should be equal for any national
+keyboard layout.
+.Pp
+The mouse emulator is a rude hack at all; its only purpose is to provide
+a device to move the pointer within an X-windowing environment.
+.Sh SEE ALSO
+.Xr X 1 ,
+.Xr pcvt 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Xr pcvt 4 ,
+release 3.00.
+.Sh AUTHORS
+The mouse emulator has been contributed by
+.if n Joerg Wunsch.
+.if t J\(:org Wunsch.
diff --git a/usr.sbin/pcvt/mcon/mcon.c b/usr.sbin/pcvt/mcon/mcon.c
new file mode 100644
index 0000000..db39944
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/mcon.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 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.
+ * 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 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.
+ *
+ *
+ * @(#)mcon.c, 3.20, Last Edit-Date: [Tue Dec 20 14:53:15 1994]
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -jw initial version; includes a basic mapping between PeeCee
+ * scan codes and key names
+ * -hm changed sys/pcvt_ioctl.h -> machine/pcvt_ioctl.h
+ *
+ *---------------------------------------------------------------------------*/
+
+/*
+ * Utility program to wire the mouse emulator control ioctl to the
+ * user level. Allows setting of any configurable parameter, or
+ * display the current configuration.
+ */
+
+#include <machine/pcvt_ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/fcntl.h>
+
+static const char *keynames[] = {
+ "", "esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
+ "-", "+", "bksp", "tab", "q", "w", "e", "r", "t", "y", "u",
+ "i", "o", "p", "[", "]", "enter", "ctrl", "a", "s", "d", "f",
+ "g", "h", "j", "k", "l", ";", "\"", "`", "lshift", "\\",
+ "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "rshift",
+ "prtscr", "alt", "space", "caps", "f1", "f2", "f3", "f4",
+ "f5", "f6", "f7", "f8", "f9", "f10", "numlock", "scrolllock",
+ "kp7", "kp8", "kp9", "kp-", "kp4", "kp5", "kp6", "kp+",
+ "kp1", "kp2", "kp3", "kp0", "kp."
+};
+
+
+const char *scantoname(int scan) {
+ if(scan >= sizeof keynames / sizeof(const char *))
+ return "???";
+ else
+ return keynames[scan];
+}
+
+int nametoscan(const char *name) {
+ int i;
+ for(i = 0; i < sizeof keynames / sizeof(const char *); i++)
+ if(strcmp(keynames[i], name) == 0)
+ return i;
+ return -1;
+}
+
+
+int main(int argc, char **argv) {
+ int c, errs = 0, fd, do_set = 0;
+ int left = 0, mid = 0, right = 0, accel = 0, sticky = -1;
+ struct mousedefs mdef;
+
+ while((c = getopt(argc, argv, "l:m:r:a:s:")) != -1)
+ switch(c) {
+ case 'l':
+ left = nametoscan(optarg);
+ do_set = 1;
+ if(left == -1) goto keynameerr;
+ break;
+
+ case 'm':
+ mid = nametoscan(optarg);
+ do_set = 1;
+ if(mid == -1) goto keynameerr;
+ break;
+
+ case 'r':
+ right = nametoscan(optarg);
+ do_set = 1;
+ if(right == -1) goto keynameerr;
+ break;
+
+ keynameerr:
+ {
+ fprintf(stderr, "unknown key name: %s\n",
+ optarg);
+ errs++;
+ }
+ break;
+
+ case 'a':
+ accel = 1000 * strtol(optarg, 0, 10);
+ do_set = 1;
+ break;
+
+ case 's':
+ if(strcmp(optarg, "0") == 0
+ || strcmp(optarg, "false") == 0
+ || strcmp(optarg, "no") == 0)
+ sticky = 0;
+ else if(strcmp(optarg, "1") == 0
+ || strcmp(optarg, "true") == 0
+ || strcmp(optarg, "yes") == 0)
+ sticky = 1;
+ else {
+ fprintf(stderr, "invalid argument to -s: %s\n",
+ optarg);
+ errs++;
+ }
+ do_set = 1;
+ break;
+
+ default:
+ errs++;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(errs || argc != 1) {
+ fprintf(stderr, "usage: "
+ "mouse [-l key][-m key][-r key][-a acctime][-s 0|1] "
+ "mousedev\n");
+ return 2;
+ }
+
+ if((fd = open(argv[0], O_RDONLY)) < 0) {
+ perror("open(mousedev)");
+ return 2;
+ }
+ if(ioctl(fd, KBDMOUSEGET, &mdef) < 0) {
+ perror("ioctl(KBDMOUSEGET)");
+ return 1;
+ }
+
+ if(!do_set) {
+ printf("Current mouse emulator definitions:\n"
+ "left button: %s\n"
+ "middle button: %s\n"
+ "right button: %s\n"
+ "acceleration limit: %d msec\n"
+ "sticky buttons: %s\n",
+ scantoname(mdef.leftbutton),
+ scantoname(mdef.middlebutton),
+ scantoname(mdef.rightbutton),
+ mdef.acceltime / 1000,
+ mdef.stickybuttons? "yes": "no");
+ return 0;
+ }
+
+ if(left) mdef.leftbutton = left & 0x7f;
+ if(mid) mdef.middlebutton = mid & 0x7f;
+ if(right) mdef.rightbutton = right & 0x7f;
+
+ if(accel) mdef.acceltime = accel;
+ if(sticky != -1) mdef.stickybuttons = sticky;
+
+ if(ioctl(fd, KBDMOUSESET, &mdef) < 0) {
+ perror("ioctl(KBDMOUSESET)");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/pcvt/scon/Makefile b/usr.sbin/pcvt/scon/Makefile
new file mode 100644
index 0000000..6fdb94e
--- /dev/null
+++ b/usr.sbin/pcvt/scon/Makefile
@@ -0,0 +1,3 @@
+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..7787fda
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.1
@@ -0,0 +1,214 @@
+.\" 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.
+.\"
+.\" @(#)scon.1, 3.00, Last Edit-Date: [Mon Jan 10 21:30:48 1994]
+.\"
+.Dd December 31, 1993
+.Dt SCON 1
+.Sh NAME
+.Nm scon
+.Nd controls screen modes for pcvt video driver
+.Sh SYNOPSIS
+.Nm scon
+.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
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar entry,red,green,blue
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar default
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar list
+.Nm scon
+.Op Fl v
+.Fl t Ar timeout
+.Nm scon
+.Op Fl v
+.Fl 1 | Fl 8
+.Sh DESCRIPTION
+The
+.Nm scon
+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
+.Pq 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.
+
+If used with argument
+.Dq Ar default ,
+this flag will restore the default palette
+.Po
+as installed by VGA ROM BIOS after hardware reset
+.Pc .
+
+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.
+
+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
+.Pq 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
+.Pq 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.
+
+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..d42d5c4
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.c
@@ -0,0 +1,856 @@
+/*
+ * 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.
+ *
+ */
+
+static char *id =
+ "@(#)scon.c, 3.20, Last Edit-Date: [Sun Sep 25 12:33:21 1994]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm moving fd for default device from 1 -> 0 for such things
+ * as "scon -p list | more" to be possible
+ * (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+ * -hm adding option "a" for just returning the type of video adaptor
+ * -hm removing explicit HGC support, same as MDA ...
+ * -hm vga type/family/132col support info on -l
+ * -hm force 24 lines in DEC 25 lines mode and HP 28 lines mode
+ * -hm fixed bug with 132 column mode display status display
+ * -jw added 132/80 col mode switching
+ * -hm removed -h flag, use -? now ... ;-)
+ * -hm S3 chipsets ..
+ * -hm Cirrus chipsets support from Onno van der Linden
+ * -hm -m option, display monitor type
+ * -hm bugfix, scon -c <screen-num> cleared dest screen, fixed
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.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[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ 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)
+ {
+ fprintf(stderr,
+ "-p list is mutual exclusive "
+ "with other -p options\n");
+ return 2;
+ }
+ Pflag = 3;
+ }
+ else if(!strcmp(optarg, "default"))
+ {
+ if(Pflag)
+ {
+ fprintf(stderr,
+ "multiple -p default not "
+ "allowed\n");
+ return 2;
+ }
+ Pflag = 2;
+ } else {
+ unsigned idx, r, g, b;
+
+ if(Pflag > 1)
+ {
+ fprintf(stderr,
+ "-p default and -p i,r,g,b "
+ "ambiguous\n");
+ return 2;
+ }
+ Pflag = 1;
+ parsepopt(optarg, &idx, &r, &g, &b);
+ if(idx >= NVGAPEL)
+ {
+ fprintf(stderr,
+ "index %u in -p option "
+ "out of range\n", idx);
+ return 2;
+ }
+ 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)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ 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)
+ {
+ perror("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)
+ {
+ perror("ioctl(VGASETCOLMS)");
+ exit(2);
+ }
+ 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)
+ {
+ perror("ioctl(fd, VGAWRITEPEL)");
+ return 2;
+ }
+ }
+ 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)
+ {
+ perror("ioctl VGASETSCREEN failed");
+ exit(1);
+ }
+ 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)
+ {
+ perror("ioctl VGASETSCREEN failed");
+ exit(1);
+ }
+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)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+ 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)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+ 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)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+
+ 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)
+ {
+ perror("ioctl(VGAREADPEL)");
+ exit(2);
+ }
+ 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) {
+ fprintf(stderr, "too few args in -p i,r,g,b\n");
+ exit(2);
+ }
+
+ 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;
+ }
+ fprintf(stderr, "arg ``%s'' in -p option not recognized\n",
+ firstarg);
+ exit(2);
+}
diff --git a/usr.sbin/pcvt/set2061/CAUTION b/usr.sbin/pcvt/set2061/CAUTION
new file mode 100644
index 0000000..e1eba06
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/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/set2061/ICD2061Aalt.c b/usr.sbin/pcvt/set2061/ICD2061Aalt.c
new file mode 100644
index 0000000..a74ec4e
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/ICD2061Aalt.c
@@ -0,0 +1,297 @@
+/*
+ * This code is derived from code available from the STB bulletin board
+ */
+
+/* $XFree86: mit/server/ddx/x386/common_hw/ICD2061Aalt.c,v 2.6 1994/04/15 05:10:30 dawes Exp $ */
+
+#ifndef KERNEL
+#include "compiler.h"
+#else
+#define GCCUSESGAS
+#define PCVT_STANDALONE 1
+#endif
+
+#define SEQREG 0x03C4
+#define MISCREG 0x03C2
+#define MISCREAD 0x03CC
+
+double fref = 14.31818 * 2.0;
+char ascclk[] = "VIDEO CLOCK ?";
+
+unsigned short clknum;
+unsigned short vlbus_flag;
+unsigned short card;
+unsigned short crtcaddr;
+unsigned short clockreg;
+
+static double range[15] = {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, 73.5,
+ 75.6, 80.9, 83.2, 91.5, 100.0, 120.0, 120.0};
+
+#ifdef __STDC__
+static double genratio(unsigned int *p, unsigned int *q, double tgt);
+static double f(unsigned int p, unsigned int q, double basefreq);
+#if 0
+static void prtbinary(unsigned int size, unsigned int val);
+#endif
+static void wait_vb();
+static void wrt_clk_bit(unsigned int value);
+static void init_clock(unsigned long setup, unsigned short crtcport);
+#else
+static double genratio();
+static double f();
+#if 0
+static void prtbinary();
+#endif
+static void wait_vb();
+static void wrt_clk_bit();
+static void init_clock();
+#endif
+
+void AltICD2061SetClock(frequency, select)
+register long frequency; /* in Hz */
+int select;
+{
+ unsigned int m, mval, ival;
+ int i;
+ long dwv;
+ double realval;
+ double freq, fvco;
+ double dev, devx;
+ double delta, deltax;
+ unsigned int p, q;
+ unsigned int bestp, bestq;
+ unsigned char tmp;
+
+ crtcaddr=(inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
+
+
+ outb(crtcaddr, 0x11); /* Unlock CRTC registers */
+ tmp = inb(crtcaddr + 1);
+ outb(crtcaddr + 1, tmp & ~0x80);
+
+ outw(crtcaddr, 0x4838); /* Unlock S3 register set */
+ outw(crtcaddr, 0xA039);
+
+ clknum = select;
+
+ freq = ((double)frequency)/1000000.0;
+ if (freq > range[14])
+ freq =range[14];
+ else if (freq <= 6.99)
+ freq = 7.0;
+
+/*
+ * Calculate values to load into ICD 2061A clock chip to set frequency
+ */
+ delta = 999.0;
+ dev = 999.0;
+ ival = 99;
+ mval = 99;
+
+ fvco = freq / 2;
+ for (m = 0; m < 8; m++) {
+ fvco *= 2.0;
+ for (i = 14; i >= 0; i--)
+ if (fvco >= range[i])
+ break;
+ if (i < 0)
+ continue;
+ if (i == 14)
+ break;
+ devx = (fvco - (range[i] + range[i+1])/2)/fvco;
+ if (devx < 0)
+ devx = -devx;
+ deltax = genratio(&p, &q, fvco);
+ if (delta < deltax)
+ continue;
+ if (deltax < delta || devx < dev) {
+ bestp = p;
+ bestq = q;
+ delta = deltax;
+ dev = devx;
+ ival = i;
+ mval = m;
+ }
+ }
+ fvco = fref;
+ for (m=0; m<mval; m++)
+ fvco /= 2.0;
+ realval = f(bestp, bestq, fvco);
+ dwv = ((((((long)ival << 7) | bestp) << 3) | mval) << 7) | bestq;
+
+/*
+ * Write ICD 2061A clock chip
+ */
+ init_clock(((unsigned long)dwv) | (((long)clknum) << 21), crtcaddr);
+
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb(); /* 0.10 second delay... */
+}
+
+static double f(p, q, base)
+ unsigned int p;
+ unsigned int q;
+ double base;
+ {
+ return(base * (p + 3)/(q + 2));
+ }
+
+static double genratio(p, q, tgt)
+ unsigned int *p;
+ unsigned int *q;
+ double tgt;
+ {
+ int k, m;
+ double test, mindiff;
+ unsigned int mmax;
+
+ mindiff = 999999999.0;
+ for (k = 13; k < 69; k++) { /* q={15..71}:Constraint 2 on page 14 */
+ m = 50.0*k/fref - 3;
+ if (m < 0)
+ m = 0;
+ mmax = 120*k/fref - 3; /* m..mmax is constraint 3 on page 14 */
+ if (mmax > 128)
+ mmax = 128;
+ while (m < mmax) {
+ test = f(m, k, fref) - tgt;
+ if (test < 0) test = -test;
+ if (mindiff > test) {
+ mindiff = test;
+ *p = m;
+ *q = k;
+ }
+ m++;
+ }
+ }
+ return (mindiff);
+ }
+
+#if 0
+static void prtbinary(size, val)
+ unsigned int size;
+ unsigned int val;
+ {
+ unsigned int mask;
+ int k;
+
+ mask = 1;
+
+ for (k=size; --k > 0 || mask <= val/2;)
+ mask <<= 1;
+
+ while (mask) {
+ fputc((mask&val)? '1': '0' , stderr);
+ mask >>= 1;
+ }
+ }
+#endif
+
+static void wait_vb()
+ {
+ while ((inb(crtcaddr+6) & 0x08) == 0)
+ ;
+ while (inb(crtcaddr+6) & 0x08)
+ ;
+ }
+
+
+#ifdef __STDC__
+static void init_clock(unsigned long setup, unsigned short crtcport)
+#else
+static void init_clock(setup, crtcport)
+ unsigned long setup;
+ unsigned short crtcport;
+#endif
+ {
+ unsigned char nclk[2], clk[2];
+ unsigned short restore42;
+ unsigned short oldclk;
+ unsigned short bitval;
+ int i;
+ unsigned char c;
+
+#ifndef PCVT_STANDALONE
+ (void)xf86DisableInterrupts();
+#endif
+
+ oldclk = inb(0x3CC);
+
+ outb(crtcport, 0x42);
+ restore42 = inb(crtcport+1);
+
+ outw(0x3C4, 0x0100);
+
+ outb(0x3C4, 1);
+ c = inb(0x3C5);
+ outb(0x3C5, 0x20 | c);
+
+ outb(crtcport, 0x42);
+ outb(crtcport+1, 0x03);
+
+ outw(0x3C4, 0x0300);
+
+ nclk[0] = oldclk & 0xF3;
+ nclk[1] = nclk[0] | 0x08;
+ clk[0] = nclk[0] | 0x04;
+ clk[1] = nclk[0] | 0x0C;
+
+ outb(crtcport, 0x42);
+ i = inw(crtcport);
+
+ outw(0x3C4, 0x0100);
+
+ wrt_clk_bit(oldclk | 0x08);
+ wrt_clk_bit(oldclk | 0x0C);
+ for (i=0; i<5; i++) {
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(clk[1]);
+ }
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(nclk[0]);
+ wrt_clk_bit(clk[0]);
+ wrt_clk_bit(nclk[0]);
+ wrt_clk_bit(clk[0]);
+ for (i=0; i<24; i++) {
+ bitval = setup & 0x01;
+ setup >>= 1;
+ wrt_clk_bit(clk[1-bitval]);
+ wrt_clk_bit(nclk[1-bitval]);
+ wrt_clk_bit(nclk[bitval]);
+ wrt_clk_bit(clk[bitval]);
+ }
+ wrt_clk_bit(clk[1]);
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(clk[1]);
+
+ outb(0x3C4, 1);
+ c = inb(0x3C5);
+ outb(0x3C5, 0xDF & c);
+
+ outb(crtcport, 0x42);
+ outb(crtcport+1, restore42);
+
+ outb(0x3C2, oldclk);
+
+ outw(0x3C4, 0x0300);
+
+#ifndef PCVT_STANDALONE
+ xf86EnableInterrupts();
+#endif
+
+ }
+
+static void wrt_clk_bit(value)
+ unsigned int value;
+ {
+ int j;
+
+ outb(0x3C2, value);
+ for (j=2; --j; )
+ inb(0x200);
+ }
diff --git a/usr.sbin/pcvt/set2061/Makefile b/usr.sbin/pcvt/set2061/Makefile
new file mode 100644
index 0000000..38cc44d
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/Makefile
@@ -0,0 +1,13 @@
+PROG= set2061
+SRCS= main.c ICD2061Aalt.c
+CFLAGS+= -DGCCUSESGAS -DPCVT_STANDALONE
+NOMAN=
+
+all: $(PROG)
+
+install:
+ @${ECHO} "set2061 is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+$(PROG): compiler.h
diff --git a/usr.sbin/pcvt/set2061/README b/usr.sbin/pcvt/set2061/README
new file mode 100644
index 0000000..76109f9
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/README
@@ -0,0 +1,22 @@
+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 !!!
+ ===========================================================
+
+Read the file "CAUTION" before proceeding !!!
+
+The files:
+
+ ICD2061Aalt.c and
+ compiler.h
+
+come from the Xfree86 2.1 distribution and have been slightly modified to
+fit in a non-XFree environment.
+
+I use it to program the clock generator ICD2061a on my S3 928 based ELSA
+Winner VGA board to 40MHz for clock generator #2: set2061 -n2 -f40000000.
+
+This enables me to use 132 columns mode on this VGA board.
diff --git a/usr.sbin/pcvt/set2061/compiler.h b/usr.sbin/pcvt/set2061/compiler.h
new file mode 100644
index 0000000..4ff14bd
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/compiler.h
@@ -0,0 +1,341 @@
+/* $XFree86: mit/server/ddx/x386/common/compiler.h,v 2.3 1993/10/03 14:55:28 dawes Exp $ */
+/*
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ *
+ * 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 Thomas Roell not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Thomas Roell makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THOMAS ROELL 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.
+ *
+ * $Header: /home/ncvs/src/usr.sbin/pcvt/set2061/Attic/compiler.h,v 1.2 1995/05/30 03:49:45 rgrimes Exp $
+ */
+
+
+#ifndef _COMPILER_H
+#define _COMPILER_H
+
+#ifndef __STDC__
+# ifdef signed
+# undef signed
+# endif
+# ifdef volatile
+# undef volatile
+# endif
+# ifdef const
+# undef const
+# endif
+# define signed /**/
+# ifdef __GNUC__
+# define volatile __volatile__
+# define const __const__
+# else
+# define const /**/
+# endif /* __GNUC__ */
+#endif /* !__STDC__ */
+
+#ifdef NO_INLINE
+
+extern void outb();
+extern void outw();
+extern unsigned int inb();
+extern unsigned int inw();
+#if NeedFunctionPrototypes
+extern unsigned char rdinx(unsigned short, unsigned char);
+extern void wrinx(unsigned short, unsigned char, unsigned char);
+extern void modinx(unsigned short, unsigned char, unsigned char, unsigned char);
+extern int testrg(unsigned short, unsigned char);
+extern int textinx2(unsigned short, unsigned char, unsigned char);
+extern int textinx(unsigned short, unsigned char);
+#else /* NeedFunctionProtoypes */
+extern unsigned char rdinx();
+extern void wrinx();
+extern void modinx();
+extern int testrg();
+extern int textinx2();
+extern int textinx();
+#endif /* NeedFunctionProtoypes */
+
+#else /* NO_INLINE */
+
+#ifdef __GNUC__
+
+#ifndef FAKEIT
+#ifdef GCCUSESGAS
+
+/*
+ * If gcc uses gas rather than the native assembler, the syntax of these
+ * inlines has to be different. DHD
+ */
+
+static __inline__ void
+outb(port, val)
+short port;
+char val;
+{
+ __asm__ __volatile__("outb %0,%1" : :"a" (val), "d" (port));
+}
+
+
+static __inline__ void
+outw(port, val)
+short port;
+short val;
+{
+ __asm__ __volatile__("outw %0,%1" : :"a" (val), "d" (port));
+}
+
+static __inline__ unsigned int
+inb(port)
+short port;
+{
+ unsigned char ret;
+ __asm__ __volatile__("inb %1,%0" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+static __inline__ unsigned int
+inw(port)
+short port;
+{
+ unsigned short ret;
+ __asm__ __volatile__("inw %1,%0" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+#else /* GCCUSESGAS */
+
+static __inline__ void
+outb(port, val)
+ short port;
+ char val;
+{
+ __asm__ __volatile__("out%B0 (%1)" : :"a" (val), "d" (port));
+}
+
+static __inline__ void
+outw(port, val)
+ short port;
+ short val;
+{
+ __asm__ __volatile__("out%W0 (%1)" : :"a" (val), "d" (port));
+}
+
+static __inline__ unsigned int
+inb(port)
+ short port;
+{
+ unsigned char ret;
+ __asm__ __volatile__("in%B0 (%1)" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+static __inline__ unsigned int
+inw(port)
+ short port;
+{
+ unsigned short ret;
+ __asm__ __volatile__("in%W0 (%1)" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+#endif /* GCCUSESGAS */
+
+#else /* FAKEIT */
+
+static __inline__ void
+outb(port, val)
+ short port;
+ char val;
+{
+}
+
+static __inline__ void
+outw(port, val)
+ short port;
+ short val;
+{
+}
+
+static __inline__ unsigned int
+inb(port)
+ short port;
+{
+ return 0;
+}
+
+static __inline__ unsigned int
+inw(port)
+ short port;
+{
+ return 0;
+}
+
+#endif /* FAKEIT */
+
+#else /* __GNUC__ */
+#if !defined(AMOEBA) && !defined(_MINIX)
+# if defined(__STDC__) && (__STDC__ == 1)
+# define asm __asm
+# endif
+# ifdef SVR4
+# include <sys/types.h>
+# ifndef __USLC__
+# define __USLC__
+# endif
+# endif
+# include <sys/inline.h>
+#endif
+#endif
+
+/*
+ *-----------------------------------------------------------------------
+ * Port manipulation convenience functions
+ *-----------------------------------------------------------------------
+ */
+
+#ifndef __GNUC__
+#define __inline__ /**/
+#endif
+
+/*
+ * rdinx - read the indexed byte port 'port', index 'ind', and return its value
+ */
+static __inline__ unsigned char
+#ifdef __STDC__
+rdinx(unsigned short port, unsigned char ind)
+#else
+rdinx(port, ind)
+unsigned short port;
+unsigned char ind;
+#endif
+{
+ if (port == 0x3C0) /* reset attribute flip-flop */
+ (void) inb(0x3DA);
+ outb(port, ind);
+ return(inb(port+1));
+}
+
+/*
+ * wrinx - write 'val' to port 'port', index 'ind'
+ */
+static __inline__ void
+#ifdef __STDC__
+wrinx(unsigned short port, unsigned char ind, unsigned char val)
+#else
+wrinx(port, ind, val)
+unsigned short port;
+unsigned char ind, val;
+#endif
+{
+ outb(port, ind);
+ outb(port+1, val);
+}
+
+/*
+ * modinx - in register 'port', index 'ind', set the bits in 'mask' as in 'new';
+ * the other bits are unchanged.
+ */
+static __inline__ void
+#ifdef __STDC__
+modinx(unsigned short port, unsigned char ind,
+ unsigned char mask, unsigned char new)
+#else
+modinx(port, ind, mask, new)
+unsigned short port;
+unsigned char ind, mask, new;
+#endif
+{
+ unsigned char tmp;
+
+ tmp = (rdinx(port, ind) & ~mask) | (new & mask);
+ wrinx(port, ind, tmp);
+}
+
+/*
+ * tstrg - returns true iff the bits in 'mask' of register 'port' are
+ * readable & writable.
+ */
+
+static __inline__ int
+#ifdef __STDC__
+testrg(unsigned short port, unsigned char mask)
+#else
+tstrg(port, mask)
+unsigned short port;
+unsigned char mask;
+#endif
+{
+ unsigned char old, new1, new2;
+
+ old = inb(port);
+ outb(port, old & ~mask);
+ new1 = inb(port) & mask;
+ outb(port, old | mask);
+ new2 = inb(port) & mask;
+ outb(port, old);
+ return((new1 == 0) && (new2 == mask));
+}
+
+/*
+ * testinx2 - returns true iff the bits in 'mask' of register 'port', index
+ * 'ind' are readable & writable.
+ */
+static __inline__ int
+#ifdef __STDC__
+testinx2(unsigned short port, unsigned char ind, unsigned char mask)
+#else
+testinx2(port, ind, mask)
+unsigned short port;
+unsigned char ind, mask;
+#endif
+{
+ unsigned char old, new1, new2;
+
+ old = rdinx(port, ind);
+ wrinx(port, ind, old & ~mask);
+ new1 = rdinx(port, ind) & mask;
+ wrinx(port, ind, old | mask);
+ new2 = rdinx(port, ind) & mask;
+ wrinx(port, ind, old);
+ return((new1 == 0) && (new2 == mask));
+}
+
+/*
+ * testinx - returns true iff all bits of register 'port', index 'ind' are
+ * readable & writable.
+ */
+static __inline__ int
+#ifdef __STDC__
+testinx(unsigned short port, unsigned char ind)
+#else
+testinx(port, ind, mask)
+unsigned short port;
+unsigned char ind;
+#endif
+{
+ return(testinx2(port, ind, 0xFF));
+}
+
+#endif /* NO_INLINE */
+#endif /* _COMPILER_H */
diff --git a/usr.sbin/pcvt/set2061/main.c b/usr.sbin/pcvt/set2061/main.c
new file mode 100644
index 0000000..49862fa
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/main.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1994 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 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 =
+ "@(#)set2061.c, 1.00, Last Edit-Date: [Sun Jan 15 19:52:05 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm start using 132 columns on my Elsa Winner
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+void AltICD2061SetClock(long frequency, int select);
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ int fd;
+ int c;
+ long freq = -1;
+ int no = -1;
+
+ while( (c = getopt(argc, argv, "f:n:")) != -1)
+ {
+ switch(c)
+ {
+ case 'f':
+ freq = atoi(optarg);
+ break;
+
+ case 'n':
+ no = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(freq == -1 || no == -1)
+ usage();
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = DEFAULTFD;
+
+ if(ioctl(fd, KDENABIO, 0) < 0)
+ {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+
+ AltICD2061SetClock(freq, no);
+
+ (void)ioctl(fd, KDDISABIO, 0);
+
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\nset2061 - program the ICD2061 video clock chip\n");
+ fprintf(stderr,"usage: set2061 -f <freq> -n <no>\n");
+ fprintf(stderr," -f <freq> frequency in Hz\n");
+ fprintf(stderr," -n <no> clock generator number\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/userkeys/Makefile b/usr.sbin/pcvt/userkeys/Makefile
new file mode 100644
index 0000000..2e7452f
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/Makefile
@@ -0,0 +1,3 @@
+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..215d2f8
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.1
@@ -0,0 +1,131 @@
+.TH VT220KEYS 1
+.UC 4
+.SH NAME
+vt220keys \- define SHIFTED function keys on VT220 terminal
+.SH SYNTAX
+.B vt220keys
+[
+.B \-cil
+]
+[ keyname keystring ] ...
+.SH DESCRIPTION
+.I Vt220keys
+sets up a "vt220 terminal" in vt200 mode to allow user
+definition of the SHIFTED function keys. Each
+\f2keyname\f1 specified on the command line will be loaded with
+the corresponding \f2keystring\f1.
+A \f2keyname\f1 is one of the following "words":
+F6 F7 F8 F9 F10 F11 ESC F12 BS F13 LF F14 HELP DO F17 F18 F19 F20.
+\f2Keystrings\f1
+must be quoted if spaces, tabs, or shell metacharacters are included.
+.PP
+.B Vt220keys
+expects to receive some combination of option flags and/or
+argument pair(s), otherwise a usage message
+is printed.
+.PP
+The options are:
+.TP
+.B \-c
+Clears all SHIFTED function key definitions before setting them to user
+defined strings.
+.TP
+.B \-i
+Read the initialization file
+.I $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
+\f2keyname\f1 and the second field is the \f2keystring\f1.
+The second field extends to the end of the line, thus a
+\f2keystring\f1
+may include spaces or tabs. A newline (return) may be specified
+within the string by using the C Language notation for newline (\\n).
+.TP
+.B \-l
+Locks the function keys from further definition.
+Locking occurs after processing the initialization file (if the "i"
+option is specified) and any argument
+pairs.
+The only way
+to unlock is by turning the power off.
+.SH EXAMPLES
+vt220keys -ci
+.br
+vt220keys F6 'nroff -ms '
+.br
+vt220keys -i F20 'cc -O -c '
+.br
+vt220keys -l HELP man
+.SH "OTHER FEATURES"
+Pressing the function keys without using the shift key, generates
+a string of characters. With
+\f2csh\f1(1) this string can be aliased to some command. For example:
+.br
+ alias ^[[17~ "ls -CR | more"
+.br
+where ^[[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
+.B Vt220keys
+can be called from your .login or .profile file. Typically an user
+will create a initialization file and include a line like
+.br
+ vt220keys -ci
+.br
+OR
+.br
+ vt220keys -cil
+.br
+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:
+.sp
+.in +.5i
+The escape character can be generated by typing ^[ (control-[).
+.sp
+Use
+.B vt220keys
+as follows (note ^[ is control-[)
+.br
+.in +.5i
+vt220keys ESC '^['
+.in
+.br
+This will require you
+to press the SHIFT key and ESC to generate the escape sequence.
+.sp
+Some editors, allow other character(s) to be substituted for the
+escape character. For example with
+.B emacs
+include this line in your .emacs_pro:
+.br
+ (bind-to-key "ESC-prefix" "\\033[23~")
+.br
+Thus when the ESC key is pressed, emacs will allow the characters
+generated (^[[23~) to perform the same function as the escape
+character.
+.in
+.SH FILES
+$HOME/.vt220rc \- initialization file
+.SH "SEE ALSO"
+VT220 Programmer Reference Manual
+.br
+VT220 Programmer Pocket Guide
+
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.c b/usr.sbin/pcvt/userkeys/vt220keys.c
new file mode 100644
index 0000000..55664c5
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.c
@@ -0,0 +1,297 @@
+/*
+ * 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
+
+*/
+
+#include <stdio.h>
+#include <ctype.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[];
+{
+ /* these are defined in the getopt routine */
+ extern char *optarg; /* argument give to an option */
+ extern int optind; /* argv index after option processing */
+
+ 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 */
+ char *strcpy();
+
+ (void) strcpy(prog, *argv); /* 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 */
+
+ /* system calls and subroutines */
+ FILE *fopen();
+ char *strcpy();
+ char *strcat();
+ char *fgets();
+ char *getenv();
+
+ /* construct full path name for init file */
+ home = getenv("HOME");
+ (void) strcpy(path, home);
+ (void) strcat(path,"/");
+ (void) strcat(path,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..9edd896
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/Makefile
@@ -0,0 +1,38 @@
+# $Id: Makefile,v 1.9 1998/05/06 15:23:53 bde Exp $
+
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= vgaio
+MAN8= vgaio.${MAN8EXT}
+
+SRCS= vgaio.y lex.l y.tab.h
+YACC= yacc
+
+#YFLAGS+= -yd # Bison
+#YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CFLAGS= -O2 # due to a gcc bug, it compiles only with -O2!
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR}
+
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "vgaio is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/vgaio/lex.l b/usr.sbin/pcvt/vgaio/lex.l
new file mode 100644
index 0000000..7fb6ef2
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/lex.l
@@ -0,0 +1,86 @@
+%{
+/*
+ * 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 "$Header: /home/ncvs/src/usr.sbin/pcvt/vgaio/Attic/lex.l,v 1.1.1.1 1995/02/05 13:49:23 jkh Exp $"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.1.1.1 1995/02/05 13:49:23 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * 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..a7a4c0f
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.8
@@ -0,0 +1,143 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: vgaio.8,v 1.2 1998/03/23 08:26:31 charnier Exp $
+.\" -hm updated 31.12.94
+.\"
+.Dd December 31, 1994
+.Dt VGAIO 8
+.Sh NAME
+.Nm vgaio
+.Nd perform input/output on a Video Graphics Array
+.Sh SYNOPSIS
+.Nm vgaio
+.Op Fl d
+.Sh DESCRIPTION
+.Ss Purpose
+.Nm Vgaio
+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 vgaio
+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.
+
+Symbolic register names look like:
+
+.D1 Ao Em reggroup Ac Ao Em regnumber Ac
+
+with
+.Aq Em regnumber
+being any hexadecimal number
+.Pq 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.
+
+An input instruction has the form
+
+.D1 Ao Em regname Ac ?
+
+and will cause
+.Nm
+to output a line like
+
+.Bd -ragged -offset indent
+.Ao Em regname Ac \& = 0x Ns
+.Aq Em number
+.Ed
+
+An output instruction looks like
+
+.Bd -ragged -offset indent
+.Ao Em regname Ac =
+.Aq Em number
+.Ed
+
+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.
+
+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.
+
+Remember, to use this program, your kernel has to be compiled with XSERVER
+being defined !
+
+.Sh AUTHORS
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t 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..61b9064
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.h
@@ -0,0 +1,63 @@
+/*
+ * 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.2 1995/03/05 22:46:27 joerg
+ * Upgrade to beta 3.20/b22
+ *
+ * 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..ec169d8
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.y
@@ -0,0 +1,255 @@
+%{
+/*
+ * 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 "$Header: /home/ncvs/src/usr.sbin/pcvt/vgaio/Attic/vgaio.y,v 1.1.1.1 1995/02/05 13:49:24 jkh Exp $"
+
+/*
+ * $Log: vgaio.y,v $
+ * Revision 1.1.1.1 1995/02/05 13:49:24 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * 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 <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..3c6f089
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/Makefile
@@ -0,0 +1,6 @@
+
+PROG= vttest
+CFLAGS+= -traditional -DUSEMYSTTY
+SRCS= main.c esc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vttest/README b/usr.sbin/pcvt/vttest/README
new file mode 100644
index 0000000..589d08f
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/README
@@ -0,0 +1,57 @@
+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 adress is:
+
+Network-mail adress: (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..b2bce92
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/esc.c
@@ -0,0 +1,398 @@
+#include "header.h"
+
+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(0,&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(0,result+i,1);
+ if (i++ == 78) break;
+ }
+#else
+#ifdef SIII
+ while(read(2,result+i,1) == 1)
+ if (i++ == 78) break;
+#else
+ while(ioctl(0,FIONREAD,&l1), l1 > 0L) {
+ while(l1-- > 0L) {
+ read(0,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(0,&val,1);
+#else
+#ifdef SIII
+ while(read(2,&val,1));
+#else
+ long l1;
+ ioctl (0, FIONREAD, &l1);
+ while(l1-- > 0L) read(0,&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..300564a
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/header.h
@@ -0,0 +1,43 @@
+#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>
+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..02cbc62
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/main.c
@@ -0,0 +1,2016 @@
+/*
+ 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.
+
+*/
+
+#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, onbrk);
+ signal(SIGTERM, 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("/dev/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();
+}
+
+#ifdef UNIX
+onbrk() {
+ signal(SIGINT, onbrk);
+ if (reading)
+ brkrd = 1;
+ else
+ longjmp(intrenv, 1);
+}
+
+onterm() {
+ signal(SIGTERM, onterm);
+ longjmp(intrenv, 1);
+}
+#endif
+
+holdit() {
+ inflush();
+ printf("Push <RETURN>");
+ readnl();
+}
+
+readnl() {
+#ifdef UNIX
+ char ch;
+ fflush(stdout);
+ brkrd = 0;
+ reading = 1;
+ do { read(0,&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..c410492
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/vttest.1
@@ -0,0 +1,13 @@
+.TH VTTEST 1 "LOCAL"
+.SH NAME
+vttest \- test VT100-type terminal
+.SH SYNOPSIS
+.B vttest
+.SH DESCRIPTION
+.I Vttest
+is a program designed to test the functionality of a 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..d629790
--- /dev/null
+++ b/usr.sbin/periodic/Makefile
@@ -0,0 +1,11 @@
+# Makefile for periodic(8)
+#
+# $Id$
+
+MAN8= periodic.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/periodic.sh ${DESTDIR}${BINDIR}/periodic
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/periodic/periodic.8 b/usr.sbin/periodic/periodic.8
new file mode 100644
index 0000000..19ae3af
--- /dev/null
+++ b/usr.sbin/periodic/periodic.8
@@ -0,0 +1,137 @@
+.\" 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.
+.\"
+.\" $Id: periodic.8,v 1.8 1998/12/29 22:45:15 hoek Exp $
+.\"
+.Dd 13 August 1997
+.Os FreeBSD 3.0
+.Dt PERIODIC 8
+.Sh NAME
+.Nm periodic
+.Nd
+run periodic system functions
+.Sh SYNOPSIS
+.Nm periodic
+.Ao
+.Cm daily | weekly | monthly |
+.Ar path Op path ...
+.Ac
+.Sh DESCRIPTION
+The
+.Nm
+program is intended to be called by cron(8) to execute shell scripts
+located in the specified directory.
+.Pp
+One, and only one, of the following arguments should be specified:
+.Bl -tag -width Fl
+.It Cm daily
+Perform the standard daily periodic executable run.
+This usually occurs early in the morning (local time).
+.It Cm weekly
+Perform the standard weekly periodic executable run.
+This usually occurs on Sunday mornings.
+.It Cm monthly
+Perform the standard monthly periodic executable run.
+This usually occurs on the first day of the month.
+.It Ar path
+An absolute path to a directory containing a set of executables to be run.
+.El
+.Pp
+The
+.Nm
+program will run each executable file in the directory or directories
+specified. If a file does not have the executable bit set, it will be
+ignored silently.
+.Sh ENVIRONMENT
+The
+.Nm
+command 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 /etc/periodic
+.It Pa /etc/crontab
+The
+.Nm
+program is typically called via entries in the system default cron 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/rc.conf
+The
+.Pa rc.conf
+system registry contains a variable
+.Va local_periodic
+which may be configured to specify additional top level standard
+periodic directories, such as
+.Pa /usr/local/etc/periodic
+and
+.Pa /usr/X11R6/etc/periodic .
+.El
+.Sh EXAMPLES
+The system crontab should have entries for
+.Nm
+similar to the following example:
+.Pp
+.Dl # do daily/weekly/monthly maintenance
+.Dl 0 2 * * * root periodic daily 2>&1
+.Dl 0 3 * * 6 root periodic weekly 2>&1
+.Dl 0 5 1 * * root periodic monthly 2>&1
+.Pp
+Additionally, the system registry will typically have a
+.Va local_cron
+variable reading:
+.Dl local_cron="/usr/local/etc/periodic /usr/X11R6/etc/periodic" # cron script dirs.
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr crontab 5 ,
+.Xr rc.conf 5 ,
+.Xr cron 8
+.Rs
+.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
+program first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Paul Traina Aq pst@FreeBSD.org
diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh
new file mode 100644
index 0000000..b195bf8
--- /dev/null
+++ b/usr.sbin/periodic/periodic.sh
@@ -0,0 +1,67 @@
+#!/bin/sh -
+#
+# $Id: periodic.sh,v 1.7 1999/01/01 17:37:33 billf Exp $
+#
+# 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/rc.conf ]; then
+ . /etc/defaults/rc.conf
+elif [ -r /etc/rc.conf ]; then
+ . /etc/rc.conf
+fi
+
+dir=$1
+run=`basename $dir`
+
+# If a full path was not specified, check the standard cron areas
+
+if [ "$dir" = "$run" ] ; then
+ dirlist=""
+ for top in /etc/periodic ${local_periodic} ; do
+ if [ -d $top/$dir ] ; then
+ dirlist="${dirlist} $top/$dir"
+ fi
+ done
+
+# User wants us to run stuff in a particular directory
+else
+ for dir in $* ; do
+ if [ ! -d $dir ] ; then
+ echo "$0: $dir not found" 1>&2
+ exit 1
+ fi
+ done
+
+ dirlist="$*"
+fi
+
+host=`hostname`
+echo "Subject: $host $run run output"
+
+# 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 dir in $dirlist ; do
+ for file in $dir/* ; do
+ if [ -x $file -a ! -d $file ] ; then
+ $file
+ fi
+ done
+done
diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile
new file mode 100644
index 0000000..37a1f05
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,3 @@
+SUBDIR=lib add create delete info
+
+.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..73ab2a3
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,10 @@
+# $Id$
+
+.if exists(${.OBJDIR}/../lib)
+LIBINSTALL= ${.OBJDIR}/../lib/libinstall.a
+.else
+LIBINSTALL= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+# Inherit BINDIR from one level up.
+.include "../Makefile.inc"
diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README
new file mode 100644
index 0000000..a5a517d
--- /dev/null
+++ b/usr.sbin/pkg_install/README
@@ -0,0 +1,8 @@
+This is the pkg_install suite of tools for doing maintainance of
+software "packages". More documentation is available in the man pages
+for each individual command.
+
+This code was written by Jordan Hubbard for FreeBSD, snatched and
+mildly reshaped by John Kohl in NetBSD and the changes taken back into
+FreeBSD again by Jordan, who then proceeded to add another couple
+of dozen features on top. Whee! :-)
diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile
new file mode 100644
index 0000000..749285d
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,12 @@
+# $Id$
+
+PROG= pkg_add
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+DPADD= ${LIBINSTALL} ${LIBFTPIO} ${LIBMD}
+LDADD= ${LIBINSTALL} -lftpio -lmd
+
+SRCS= main.c perform.c futil.c extract.c
+
+.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..a216944
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * 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(char *, Package *);
+void apply_perms(char *, 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..ac8d4bf
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,256 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: extract.c,v 1.22 1998/10/12 20:01:48 jkh Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.h"
+#include "add.h"
+
+
+#define STARTSTRING "tar cf - "
+#define TOOBIG(str) ((strlen(str) + 22 + strlen(home) + where_count > maxargs) \
+ || (strlen(str) + 6 + strlen(home) + perm_count > maxargs))
+
+#define PUSHOUT(todir) /* push out string */ \
+ if (where_count > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar --unlink -xf - -C "); \
+ strcat(where_args, todir); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, "can not invoke %d byte tar pipeline: %s", \
+ 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(char *name, char *home, PackingList start, PackingList stop)
+{
+ PackingList q;
+ char try[FILENAME_MAX], bup[FILENAME_MAX], *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;
+ }
+ }
+}
+
+void
+extract_plist(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, "can't get argument list space");
+ }
+ perm_args = alloca(maxargs);
+ if (!perm_args) {
+ cleanup(0);
+ errx(2, "can't get argument list space");
+ }
+
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ perm_args[0] = 0;
+
+ last_chdir = 0;
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+
+ /* Reset the world */
+ Owner = NULL;
+ Group = NULL;
+ Mode = NULL;
+ last_file = NULL;
+ Directory = 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];
+
+ if (strrchr(p->name,'\'')) {
+ cleanup(0);
+ errx(2, "Bogus filename \"%s\"", p->name);
+ }
+
+ /* 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 = snprintf(&perm_args[perm_count], maxargs - perm_count, "'%s' ", p->name);
+ if (add_count > maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "oops, miscounted strings!");
+ }
+ 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 = snprintf(&where_args[where_count], maxargs - where_count, " '%s'", p->name);
+ if (add_count > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "oops, miscounted strings!");
+ }
+ where_count += add_count;
+ add_count = snprintf(&perm_args[perm_count],
+ maxargs - perm_count,
+ "'%s' ", p->name);
+ if (add_count > maxargs - perm_count) {
+ cleanup(0);
+ errx(2, "oops, miscounted strings!");
+ }
+ 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, "unable to cwd to '%s'", p->name);
+ }
+ Directory = p->name;
+ }
+ else
+ 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, "no last file specified for '%s' command", p->name);
+ }
+ if (strstr(p->name, "%D") && Directory == NULL) {
+ cleanup(0);
+ errx(2, "no directory specified for '%s' command", p->name);
+ }
+ format_cmd(cmd, 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;
+ }
+ 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..1385ed5
--- /dev/null
+++ b/usr.sbin/pkg_install/add/futil.c
@@ -0,0 +1,93 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: futil.c,v 1.6 1997/02/22 16:09:17 peter Exp $";
+#endif
+
+/*
+ * 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 <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 = index(cp1, '/')) !=NULL )
+ *cp2 = '\0';
+ if (fexists(dir)) {
+ if (!isdir(dir))
+ return FAIL;
+ }
+ else {
+ if (vsystem("mkdir %s", dir))
+ 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(char *dir, char *arg)
+{
+ 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..002222f
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,212 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.22 1999/01/26 22:31:23 billf Exp $";
+#endif
+
+/*
+ *
+ * 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 <err.h>
+#include <sys/param.h>
+#include <objformat.h>
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "hvIRfnrp:SMt:";
+
+char *Prefix = 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 20
+char pkgnames[MAX_PKGS][MAXPATHLEN];
+char *pkgs[MAX_PKGS];
+
+static char *getpackagesite(char *);
+int getosreldate(void);
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, err;
+ char **start;
+ char *cp;
+
+ char *remotepkg = NULL, *ptr;
+ static char binformat[1024];
+ static char packageroot[MAXPATHLEN] = "ftp://ftp.FreeBSD.org/pub/FreeBSD/ports/";
+
+ 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':
+ strcpy(FirstPen, optarg);
+ break;
+
+ case 'S':
+ AddMode = SLAVE;
+ break;
+
+ case 'M':
+ AddMode = MASTER;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > MAX_PKGS) {
+ warnx("too many packages (max %d)", MAX_PKGS);
+ return(1);
+ }
+
+ 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 (getenv("PACKAGESITE") == NULL) {
+ getobjformat(binformat, sizeof(binformat), &argc, argv);
+ strcat(packageroot, getpackagesite(binformat));
+ }
+ else
+ strcpy(packageroot, (getenv("PACKAGESITE")));
+ remotepkg = strcat(packageroot, *argv);
+ if (!((ptr = strrchr(remotepkg, '.')) && ptr[1] == 't' &&
+ ptr[2] == 'g' && ptr[3] == 'z' && !ptr[4]))
+ strcat(remotepkg, ".tgz");
+ }
+ if (!strcmp(*argv, "-")) /* stdin? */
+ pkgs[ch] = "-";
+ else if (isURL(*argv)) /* preserve URLs */
+ pkgs[ch] = strcpy(pkgnames[ch], *argv);
+ else if ((Remote) && isURL(remotepkg))
+ pkgs[ch] = strcpy(pkgnames[ch], remotepkg);
+ 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)))
+ warnx("can't find package '%s'", *argv);
+ else
+ pkgs[ch] = strcpy(pkgnames[ch], cp);
+ }
+ }
+ }
+ }
+ /* 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();
+ }
+ /* Make sure the sub-execs we invoke get found */
+ setenv("PATH", "/sbin:/usr/sbin:/bin:/usr/bin", 1);
+
+ /* Set a reasonable umask */
+ umask(022);
+
+ if ((err = pkg_perform(pkgs)) != 0) {
+ if (Verbose)
+ warnx("%d package addition(s) failed", err);
+ return err;
+ }
+ else
+ return 0;
+}
+
+static char *
+getpackagesite(char binform[1024])
+{
+ int reldate;
+
+ reldate = getosreldate();
+
+ if (reldate == 300005)
+ return "i386/packages-3.0/";
+ else if (300004 > reldate && reldate >= 300000)
+ return "i386/packages-3.0-aout/Latest/" ;
+ else if (300004 < reldate)
+ return !strcmp(binform, "elf") ? "i386/packages-3-stable/Latest/" :
+ "i386/packages-3.0-aout/Latest/";
+
+ return("");
+
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_add [-vInrfRMS] [-t template] [-p prefix]",
+ " 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..3b5df61
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,523 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.53 1998/09/11 07:26:54 jkh Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.h"
+#include "add.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, *tmp, *extract;
+ FILE *cfile;
+ int code;
+ PackingList p;
+ struct stat sb;
+ int inPlace;
+ /* 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];
+
+ 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.tgz 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 %d bytes", 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 (!inPlace && min_free(playpen) < sb.st_size * 4) {
+ warnx("projected size of %d exceeds available free space.\n"
+"Please set your PKG_TMPDIR variable to point to a location with more\n"
+ "free space and try again", 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 (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 fields */
+ PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous";
+
+ /* See if we're already registered */
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ if (isdir(LogDir) && !Force) {
+ warnx("package `%s' already recorded as installed", PkgName);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for dependencies */
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Package `%s' depends on `%s'.\n", PkgName, p->name);
+ if (vsystem("pkg_info -e %s", p->name)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake) {
+ if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
+ snprintf(path, FILENAME_MAX, "%s/%s.tgz", getenv("_TOP"), p->name);
+ 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", PkgName);
+ if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) {
+ 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);
+ sprintf(pre_arg, "");
+ sprintf(post_arg, "");
+ } 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", PkgName);
+ if (!Fake && vsystem("./%s %s %s", pre_script, PkgName, pre_arg)) {
+ warnx("install script returned error status");
+ unlink(pre_script);
+ code = 1;
+ goto success; /* nothing to uninstall yet */
+ }
+ if (new_m) unlink(pre_script);
+ }
+
+ /* 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", PkgName);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (Verbose)
+ printf("mtree -U -f %s -d -e -p %s\n", MTREE_FNAME, p ? p->name : "/");
+ if (!Fake) {
+ if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s", MTREE_FNAME, p ? p->name : "/"))
+ warnx("mtree returned a non-zero status - continuing");
+ }
+ unlink(MTREE_FNAME);
+ }
+
+ /* 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", PkgName);
+ if (!Fake && vsystem("./%s %s %s", post_script, PkgName, post_arg)) {
+ warnx("install script returned error status");
+ unlink(post_script);
+ code = 1;
+ goto fail;
+ }
+ unlink(post_script);
+ }
+
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[FILENAME_MAX];
+ FILE *cfile;
+
+ umask(022);
+ if (getuid() != 0)
+ warnx("not running as root - trying to record install anyway");
+ if (!PkgName) {
+ warnx("no package name! can't record package, sorry");
+ code = 1;
+ goto success; /* well, partial anyway */
+ }
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ 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);
+ 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);
+ sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
+ cfile = fopen(contents, "w");
+ if (!cfile) {
+ 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, cfile);
+ fclose(cfile);
+ move_file(".", DESC_FNAME, LogDir);
+ move_file(".", COMMENT_FNAME, LogDir);
+ if (fexists(DISPLAY_FNAME))
+ move_file(".", DISPLAY_FNAME, LogDir);
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to record dependency on package `%s'\n", p->name);
+ sprintf(contents, "%s/%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ basename_of(p->name), REQUIRED_BY_FNAME);
+ cfile = fopen(contents, "a");
+ if (!cfile)
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ else {
+ fprintf(cfile, "%s\n", PkgName);
+ if (fclose(cfile) == EOF)
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ if (Verbose)
+ printf("Package %s registered in %s\n", PkgName, 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..763cab7
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,457 @@
+.\"
+.\" 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_add.1
+.\" $Id: pkg_add.1,v 1.29 1999/01/17 15:08:09 jseger Exp $
+.\"
+.Dd November 25, 1994
+.Dt pkg_add 1
+.Os FreeBSD
+.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
+.Ar pkg-name [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 ``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
+insure 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 [... pkg-name]
+The named packages are installed. A package name of - 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 a 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 ``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.
+.El
+.Pp
+One or more
+.Ar pkg-name
+arguments may be specified, each being either a file containing the
+package (these usually ending with the ``.tgz'' 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
+ftp://ftp.freebsd.org/pub/FreeBSD/packages/shells/bash-1.14.4.tgz).
+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
+.Nm pkg_add
+is fairly simple. It extracts each package's "packing list"
+into a special staging directory, parses it,
+and then runs through the following sequence to fully extract the contents:
+.Bl -enum -indent indent
+.It
+Check if the package is already recorded as installed. If so,
+terminate installation.
+.It
+Scan all the package dependencies (from
+.Cm @pkgdep
+directives, see
+.Xr pkg_create 1 )
+and make sure each one is met. If not, try and find the missing
+dependencies' packages and auto-install them; if they can't be found
+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 -filled -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 -filled -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar PRE-INSTALL
+.Ed
+
+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.
+
+.Cm 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
+.Cm Fl i
+and
+.Cm 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 -filled -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 -filled -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.
+
+.Cm 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
+.Cm Fl i
+and
+.Cm Fl I
+flags to
+.Xr pkg_create 1 ).
+
+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 ``before and after'' actions. But, separating the
+functionality is more advantageous and easier from a maintainence 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 seperated 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.
+.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 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.
+.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 mktemp 3 ,
+.Xr sysconf 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+for initial work and ongoing development.
+.An John Kohl
+for NetBSD refinements.
+.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..d347d8e
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,12 @@
+# $Id$
+
+PROG= pkg_create
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+DPADD= ${LIBINSTALL} ${LIBFTPIO} ${LIBMD}
+LDADD= ${LIBINSTALL} -lftpio -lmd
+
+SRCS= main.c perform.c pl.c
+
+.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..45b90b1
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,48 @@
+/* $Id: create.h,v 1.13 1997/10/08 07:46:19 charnier Exp $ */
+
+/*
+ * 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 PlayPen[];
+extern int Dereference;
+extern int PlistOnly;
+
+void check_list(char *, Package *);
+int pkg_perform(char **);
+void copy_plist(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..55810c2
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,171 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.17 1997/10/08 07:46:23 charnier Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.h"
+#include "create.h"
+
+static char Options[] = "YNOhvf:p:P:c:d:i:I:k:K:r:t:X:D:m:s:";
+
+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 PlayPen[FILENAME_MAX];
+int Dereference = 0;
+int PlistOnly = 0;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+
+ 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 = YES;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 's':
+ SrcDir = optarg;
+ break;
+
+ case 'f':
+ Contents = 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':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'X':
+ ExcludeFrom = optarg;
+ break;
+
+ case 'h':
+ Dereference = 1;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'm':
+ Mtree = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ 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)
+ warnx("missing package name"), usage();
+ *pkgs = NULL;
+ if (start[1])
+ warnx("only one package name allowed ('%s' extraneous)", start[1]),
+ usage();
+ 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",
+"usage: pkg_create [-YNOhv] [-P pkgs] [-p prefix] [-f contents] [-i iscript]",
+" [-I piscript] [-k dscript] [-K pdscript] [-r rscript] ",
+" [-t template] [-X excludefile] [-D displayfile] ",
+" [-m mtreefile] -c comment -d description -f packlist ",
+" pkg-name");
+ 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..5e7443d
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,344 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.46 1998/12/05 06:28:58 asami Exp $";
+#endif
+
+/*
+ * 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 "lib.h"
+#include "create.h"
+
+#include <err.h>
+#include <signal.h>
+#include <sys/syslimits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static void sanity_check(void);
+static void make_dist(char *, char *, char *, Package *);
+
+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;
+ char *suf;
+ int compress;
+
+ /* Preliminary setup */
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+ 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, "unable to open contents file '%s' for input", Contents);
+ }
+ }
+ plist.head = plist.tail = NULL;
+
+ /* chop suffix off if already specified, remembering if we want to compress */
+ len = strlen(pkg);
+ if (len > 4)
+ if (!strcmp(&pkg[len - 4], ".tgz")) {
+ compress = TRUE;
+ pkg[len - 4] = '\0';
+ }
+ else if (!strcmp(&pkg[len - 4], ".tar")) {
+ compress = FALSE;
+ pkg[len - 4] = '\0';
+ }
+ else
+ /* default is to compress packages */
+ compress = TRUE;
+ if (compress)
+ suf = "tgz";
+ else
+ suf = "tar";
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps) {
+ if (Verbose && !PlistOnly)
+ printf("Registering depends:");
+ while (Pkgdeps) {
+ cp = strsep(&Pkgdeps, " \t\n");
+ if (*cp) {
+ add_plist(&plist, PLIST_PKGDEP, 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);
+ /*
+ * 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_of(pkg));
+
+ /*
+ * 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, "can't open file %s for writing", CONTENTS_FNAME);
+ }
+ write_plist(&plist, fp);
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "error while closing %s", 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(char *home, char *pkg, char *suffix, Package *plist)
+{
+ char tball[FILENAME_MAX];
+ PackingList p;
+ int ret;
+ char *args[50]; /* Much more than enough. */
+ int nargs = 0;
+ int pipefds[2];
+ FILE *totar;
+ pid_t pid;
+
+ args[nargs++] = "tar"; /* argv[0] */
+
+ if (*pkg == '/')
+ snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suffix);
+ else
+ snprintf(tball, FILENAME_MAX, "%s/%s.%s", home, pkg, suffix);
+
+ args[nargs++] = "-c";
+ args[nargs++] = "-f";
+ args[nargs++] = tball;
+ if (index(suffix, 'z')) /* Compress/gzip? */
+ args[nargs++] = "-z";
+ 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 gzip'd tar ball in '%s'\n", tball);
+
+ /* Set up a pipe for passing the filenames, and fork off a tar process. */
+ if (pipe(pipefds) == -1) {
+ cleanup(0);
+ errx(2, "cannot create pipe");
+ }
+ if ((pid = fork()) == -1) {
+ cleanup(0);
+ errx(2, "cannot fork process for tar");
+ }
+ if (pid == 0) { /* The child */
+ dup2(pipefds[0], 0);
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execv("/usr/bin/tar", args);
+ cleanup(0);
+ errx(2, "failed to execute tar command");
+ }
+
+ /* Meanwhile, back in the parent process ... */
+ close(pipefds[0]);
+ if ((totar = fdopen(pipefds[1], "w")) == NULL) {
+ cleanup(0);
+ errx(2, "fdopen failed");
+ }
+
+ 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, "tar command failed with code %d", ret);
+ }
+}
+
+static void
+sanity_check()
+{
+ if (!Comment) {
+ cleanup(0);
+ errx(2, "required package comment string is missing (-c comment)");
+ }
+ if (!Desc) {
+ cleanup(0);
+ errx(2, "required package description string is missing (-d desc)");
+ }
+ if (!Contents) {
+ cleanup(0);
+ errx(2, "required package contents list is missing (-f [-]file)");
+ }
+}
+
+
+/* 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);
+}
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..8596261
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,462 @@
+.\"
+.\" 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
+.\" $Id: pkg_create.1,v 1.26 1998/06/26 07:15:37 jkoshy Exp $
+.\"
+.\" 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 FreeBSD
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhv
+.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
+.Fl c Ar comment
+.Fl d Ar description
+.Fl f Ar packlist
+.Ar pkg-name
+.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 ``packing list'' for package from the file
+.Ar packinglist
+or
+.Cm stdin
+if
+.Ar packinglist
+is a
+.Cm -
+(dash).
+.It Fl c Ar [-]desc
+Fetch package ``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 Ar [-]desc
+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
+.Em "FreeBSD 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.
+
+.Cm Note:
+if the
+.Cm 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, along with 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 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 PACKING LIST DETAILS section below).
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the initial directory ``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.
+
+.Cm Note:
+if the
+.Cm 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 ``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.
+.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 (using
+.Xr more 1 )
+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.
+.El
+.Pp
+.Sh PACKING LIST DETAILS
+The ``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 imbeding 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 ``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 ``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 than 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.
+.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 sysconf 3
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Bx Free .
+.Sh AUTHORS
+.An Jordan Hubbard
+for most of the work.
+.An John Kohl
+for NetBSD refinements.
+.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..6a01b6c
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,228 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pl.c,v 1.11 1997/10/08 07:46:35 charnier Exp $";
+#endif
+
+/*
+ * 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 "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(char *home, Package *pkg)
+{
+ char *where = home;
+ char *there = NULL;
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_SRC) {
+ there = p->name;
+ }
+ else if (p->type == PLIST_FILE) {
+ char *cp, name[FILENAME_MAX], buf[33];
+
+ sprintf(name, "%s/%s", there ? there : where, p->name);
+ if ((cp = MD5File(name, buf)) != 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;
+ p = tmp;
+ }
+ }
+ p = p->next;
+ }
+}
+
+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) strlen(str) + 6 + strlen(home) + where_count > maxargs
+#define PUSHOUT() /* push out string */ \
+ if (where_count > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xpf -"); \
+ if (system(where_args)) { \
+ cleanup(0); \
+ errx(2, "can't invoke tar pipeline"); \
+ } \
+ 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(char *home, Package *plist)
+{
+ PackingList p = plist->head;
+ char *where = home;
+ char *there = NULL, *mythere;
+ char *where_args, *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, "can't get argument list space");
+ }
+
+ 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 > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "oops, miscounted strings!");
+ }
+ 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 > maxargs - where_count) {
+ cleanup(0);
+ errx(2, "oops, miscounted strings!");
+ }
+ 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..15b8f56
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,11 @@
+# $Id$
+
+PROG= pkg_delete
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+DPADD= ${LIBINSTALL} ${LIBFTPIO} ${LIBMD}
+LDADD= ${LIBINSTALL} -lftpio -lmd
+
+SRCS= main.c perform.c
+
+.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..c26345c
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,33 @@
+/* $Id$ */
+
+/*
+ * 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 NoDeInstall;
+extern Boolean CleanDirs;
+extern Boolean Force;
+extern char *Directory;
+extern char *PkgName;
+
+#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..863dc4a
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,108 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.10 1997/09/18 14:08:40 phk Exp $";
+#endif
+
+/*
+ *
+ * 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 <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static char Options[] = "hvDdnfp:";
+
+char *Prefix = NULL;
+Boolean NoDeInstall = FALSE;
+Boolean CleanDirs = FALSE;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **pkgs, **start;
+
+ 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 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ if (!Fake && getuid() != 0)
+ errx(1, "you must be root to delete packages");
+ 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, "usage: pkg_delete [-vDdnf] [-p prefix] pkg-name ...\n");
+ 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..dc85e02
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.c
@@ -0,0 +1,295 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.17 1998/11/11 06:09:04 jkh Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static int pkg_do(char *);
+static void sanity_check(char *);
+static void undepend(PackingList, char *);
+static char LogDir[FILENAME_MAX];
+
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ for (i = 0; pkgs[i]; i++)
+ 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 home[FILENAME_MAX];
+ PackingList p;
+ char *tmp;
+ int len;
+ /* support for separate pre/post install scripts */
+ int new_m = 0;
+ char pre_script[FILENAME_MAX] = DEINSTALL_FNAME;
+ char post_script[FILENAME_MAX];
+ char pre_arg[FILENAME_MAX], post_arg[FILENAME_MAX];
+
+ if (!pkg || !(len = strlen(pkg)))
+ return 1;
+ if (pkg[len - 1] == '/')
+ pkg[len - 1] = '\0';
+
+ /* Reset some state */
+ if (Plist.head)
+ free_plist(&Plist);
+
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+
+ if (!fexists(LogDir)) {
+ warnx("no such package '%s' installed", pkg);
+ return 1;
+ }
+
+ if (!getcwd(home, FILENAME_MAX)) {
+ cleanup(0);
+ errx(2, "unable to get current working directory!");
+ }
+
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+
+ if (!isemptyfile(REQUIRED_BY_FNAME)) {
+ char buf[512];
+ warnx("package `%s' is required by these other packages\n"
+ "and may not be deinstalled%s:",
+ pkg, Force ? " (but I'll delete it anyway)" : "" );
+ cfile = fopen(REQUIRED_BY_FNAME, "r");
+ if (cfile) {
+ while (fgets(buf, sizeof(buf), cfile))
+ fprintf(stderr, "%s", buf);
+ fclose(cfile);
+ } else
+ warnx("cannot open requirements file `%s'", REQUIRED_BY_FNAME);
+ 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;
+ sprintf(post_script, "%s", POST_DEINSTALL_FNAME);
+ sprintf(pre_arg, "");
+ sprintf(post_arg, "");
+ } else {
+ if (fexists(DEINSTALL_FNAME)) {
+ sprintf(post_script, "%s", DEINSTALL_FNAME);
+ sprintf(pre_arg, "DEINSTALL");
+ sprintf(post_arg, "POST-DEINSTALL");
+ }
+ }
+
+ if (!NoDeInstall && 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, "Toto! This doesn't look like Kansas anymore!");
+ }
+
+ if (!Fake) {
+ /* 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 && 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, "Toto! This doesn't look like Kansas anymore!");
+ }
+
+ if (!Fake) {
+ if (vsystem("%s -r %s", REMOVE_CMD, 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;
+ if (Verbose)
+ printf("Attempting to remove dependency on package `%s'\n", p->name);
+ if (!Fake)
+ undepend(p, pkg);
+ }
+ return 0;
+}
+
+static void
+sanity_check(char *pkg)
+{
+ if (!fexists(CONTENTS_FNAME)) {
+ cleanup(0);
+ errx(2, "installed package %s has no %s file!", pkg, CONTENTS_FNAME);
+ }
+}
+
+void
+cleanup(int sig)
+{
+ if (sig)
+ exit(1);
+}
+
+static void
+undepend(PackingList p, char *pkgname)
+{
+ char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
+ char fbuf[FILENAME_MAX];
+ FILE *fp, *fpwr;
+ char *tmp;
+ int s;
+
+ sprintf(fname, "%s/%s/%s",
+ (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ p->name, REQUIRED_BY_FNAME);
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ warnx("couldn't open dependency file `%s'", fname);
+ return;
+ }
+ sprintf(ftmp, "%s.XXXXXX", fname);
+ s = mkstemp(ftmp);
+ if (s == -1) {
+ fclose(fp);
+ warnx("couldn't open temp file `%s'", ftmp);
+ return;
+ }
+ fpwr = fdopen(s, "w");
+ if (fpwr == NULL) {
+ close(s);
+ fclose(fp);
+ warnx("couldn't fdopen temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+ if (fbuf[strlen(fbuf)-1] == '\n')
+ fbuf[strlen(fbuf)-1] = '\0';
+ if (strcmp(fbuf, pkgname)) /* no match */
+ fputs(fbuf, fpwr), putc('\n', fpwr);
+ }
+ (void) fclose(fp);
+ if (fchmod(s, 0644) == FAIL) {
+ warnx("error changing permission of temp file `%s'", ftmp);
+ fclose(fpwr);
+ remove(ftmp);
+ return;
+ }
+ if (fclose(fpwr) == EOF) {
+ warnx("error closing temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ if (rename(ftmp, fname) == -1)
+ warnx("error renaming `%s' to `%s'", ftmp, fname);
+ remove(ftmp); /* just in case */
+ 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..9941afe
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,246 @@
+.\"
+.\" 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
+.\"
+.Dd November 25, 1994
+.Dt pkg_delete 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl vDdnf
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.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 ``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 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.
+.El
+
+.Pp
+.Sh TECHNICAL DETAILS
+.Nm pkg_delete
+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 -filled -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 -filled -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.
+
+.Cm Note:
+The
+.Ar DEINSTALL
+keyword will not appear if separate scripts for deinstall and post-deinstall
+are given during package creation time (using the
+.Cm Fl k
+and
+.Cm 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.
+
+The
+.Nm post-deinstall
+script is called as:
+.Bd -filled -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.
+
+.Cm 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
+.Cm Fl k
+and
+.Cm Fl K
+flags to
+.Xr pkg_create 1 ).
+
+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.
+
+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.
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+for most of the work.
+.An John Kohl
+for NetBSD refinements.
+.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..e071687
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,11 @@
+# $Id$
+
+PROG= pkg_info
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+DPADD= ${LIBINSTALL} ${LIBFTPIO} ${LIBMD}
+LDADD= ${LIBINSTALL} -lftpio -lmd
+
+SRCS= main.c perform.c show.c
+
+.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..fed1164
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,59 @@
+/* $Id$ */
+
+/*
+ * 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
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 60
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 0x0001
+#define SHOW_DESC 0x0002
+#define SHOW_PLIST 0x0004
+#define SHOW_INSTALL 0x0008
+#define SHOW_DEINSTALL 0x0010
+#define SHOW_REQUIRE 0x0020
+#define SHOW_PREFIX 0x0040
+#define SHOW_INDEX 0x0080
+#define SHOW_FILES 0x0100
+#define SHOW_DISPLAY 0x0200
+#define SHOW_REQBY 0x0400
+#define SHOW_MTREE 0x0800
+
+extern int Flags;
+extern Boolean AllInstalled;
+extern Boolean Quiet;
+extern char *InfoPrefix;
+extern char PlayPen[];
+extern char *CheckPkg;
+
+extern void show_file(char *, char *);
+extern void show_plist(char *, Package *, plist_t);
+extern void show_files(char *, Package *);
+extern void show_index(char *, char *);
+
+#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..a01eeeb
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,159 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.14 1997/10/08 07:47:26 charnier Exp $";
+#endif
+
+/*
+ *
+ * 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 <err.h>
+#include "lib.h"
+#include "info.h"
+
+static char Options[] = "acdDe:fikrRpLqImvhl:";
+
+int Flags = 0;
+Boolean AllInstalled = FALSE;
+Boolean Quiet = FALSE;
+char *InfoPrefix = "";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'a':
+ AllInstalled = 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 '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 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 't':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Set some reasonable defaults */
+ if (!Flags)
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start && !AllInstalled && !CheckPkg)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ return pkg_perform(start);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: pkg_info [-cdDikrRpLqImv] [-e package] [-l prefix]",
+ " pkg-name [pkg-name ...]",
+ " pkg_info -a [flags]");
+ 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..84a3b9a
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,216 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.25 1998/09/11 07:26:58 jkh Exp $";
+#endif
+
+/*
+ * 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 "lib.h"
+#include "info.h"
+
+#include <signal.h>
+
+static int pkg_do(char *);
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+ char *tmp;
+
+ signal(SIGINT, cleanup);
+
+ tmp = getenv(PKG_DBDIR);
+ if (!tmp)
+ tmp = DEF_LOG_DIR;
+ /* Overriding action? */
+ if (CheckPkg) {
+ char buf[FILENAME_MAX];
+
+ snprintf(buf, FILENAME_MAX, "%s/%s", tmp, CheckPkg);
+ return abs(access(buf, R_OK));
+ }
+ else if (AllInstalled) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ if (!isdir(tmp))
+ return 1;
+ dirp = opendir(tmp);
+ if (dirp) {
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
+ err_cnt += pkg_do(dp->d_name);
+ (void)closedir(dirp);
+ }
+ }
+ else
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ return err_cnt;
+}
+
+static char *Home;
+
+static int
+pkg_do(char *pkg)
+{
+ Boolean installed = FALSE, isTMP = FALSE;
+ char log_dir[FILENAME_MAX];
+ char fname[FILENAME_MAX];
+ Package plist;
+ FILE *fp;
+ struct stat sb;
+ char *cp = NULL;
+ int code = 0;
+
+ if (isURL(pkg)) {
+ if ((cp = fileGetURL(NULL, pkg)) != 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);
+ if (unpack(fname, "+*")) {
+ 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 {
+ char *tmp;
+
+ sprintf(log_dir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+ if (!fexists(log_dir)) {
+ warnx("can't find package `%s' installed or in a file!", pkg);
+ return 1;
+ }
+ 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);
+ if (Flags & SHOW_COMMENT)
+ show_file("Comment:\n", COMMENT_FNAME);
+ 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)-1);
+ 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);
+ if (Flags & SHOW_FILES)
+ show_files("Files:\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);
+}
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..9105cca
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,169 @@
+.\"
+.\" 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_info.1
+.\" $Id: pkg_info.1,v 1.18 1998/07/14 08:28:15 jkoshy Exp $
+.\"
+.Dd November 25, 1994
+.Dt pkg_info 1
+.Os FreeBSD
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm pkg_info
+.Op Fl cdDikrRpLqImv
+.Op Fl e Ar package
+.Op Fl l Ar prefix
+.Ar pkg-name [pkg-name ...]
+.Nm pkg_info
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, either packed up in
+files or already installed on the system
+with the
+.Xr pkg_create 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 v
+Turn on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q
+Be ``quiet'' in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl c
+Show the comment (one liner) 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 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 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 ``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 -filled -offset indent -compact
+Note: This should really not be necessary with pkg_info,
+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
+.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/<pkg-name> .
+.Sh ENVIRONMENT
+.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.
+.Pp
+.Ev PKG_DBDIR
+specifies an alternative location for the installed package database.
+.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 mktemp 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.An Jordan Hubbard
+for most of the work.
+.An John Kohl
+for NetBSD refinements.
+.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..9c00a7e
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,199 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: show.c,v 1.11 1997/10/08 07:47:38 charnier Exp $";
+#endif
+
+/*
+ * 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 "lib.h"
+#include "info.h"
+
+void
+show_file(char *title, char *fname)
+{
+ FILE *fp;
+ char line[1024];
+ int n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp)
+ printf("ERROR: show_file: Can't open '%s' for reading!\n", fname);
+ else {
+ while ((n = fread(line, 1, 1024, fp)) != 0)
+ fwrite(line, 1, n, stdout);
+ fclose(fp);
+ }
+ printf("\n"); /* just in case */
+}
+
+void
+show_index(char *title, char *fname)
+{
+ FILE *fp;
+ char line[MAXINDEXSIZE+2];
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ warnx("show_file: can't open '%s' for reading", fname);
+ return;
+ }
+ if(fgets(line, MAXINDEXSIZE+1, fp)) {
+ if(line[MAXINDEXSIZE-1] != '\n')
+ line[MAXINDEXSIZE] = '\n';
+ line[MAXINDEXSIZE+1] = 0;
+ fputs(line, stdout);
+ }
+ fclose(fp);
+}
+
+/* Show a packing list item type. If type is -1, show all */
+void
+show_plist(char *title, Package *plist, plist_t type)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ if (p->type != type && type != -1) {
+ 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" : "\tPackage depends on: %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;
+
+ default:
+ cleanup(0);
+ errx(2, "unknown command type %d (%s)", p->type, p->name);
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Show all files in the packing list (except ignored ones) */
+void
+show_files(char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ 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;
+ }
+ p = p->next;
+ }
+}
diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile
new file mode 100644
index 0000000..3876b10
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,10 @@
+LIB= install
+SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c
+CFLAGS+= ${DEBUG}
+NOPROFILE= yes
+NOPIC= yes
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
new file mode 100644
index 0000000..0087c18
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -0,0 +1,62 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: exec.c,v 1.5 1997/02/22 16:09:46 peter Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.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;
+}
+
diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c
new file mode 100644
index 0000000..9aed73c
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,556 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: file.c,v 1.34 1998/10/14 18:52:04 jkh Exp $";
+#endif
+
+/*
+ * 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 "lib.h"
+#include <err.h>
+#include <ftpio.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <time.h>
+#include <sys/wait.h>
+
+/* Quick check to see if a file exists */
+Boolean
+fexists(char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if something is a directory */
+Boolean
+isdir(char *fname)
+{
+ struct stat sb;
+
+ if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Check to see if file is a dir, and is empty */
+Boolean
+isemptydir(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;
+}
+
+Boolean
+isfile(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 and is empty. If nonexistent or not
+ a file, say "it's empty", otherwise return TRUE if zero sized. */
+Boolean
+isemptyfile(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 URL specification */
+Boolean
+isURL(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
+ * working.
+ */
+ if (!fname)
+ return FALSE;
+ while (isspace(*fname))
+ ++fname;
+ if (!strncmp(fname, "ftp://", 6))
+ return TRUE;
+ return FALSE;
+}
+
+/* Returns the host part of a URL */
+char *
+fileURLHost(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if ((ret = where) != NULL) {
+ while (*fname && *fname != '/' && max--)
+ *where++ = *fname++;
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ ret = fname;
+ while (*fname && *fname != '/')
+ ++fname;
+ *fname = '\0';
+ return ret;
+}
+
+/* Returns the filename part of a URL */
+char *
+fileURLFilename(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if ((ret = where) != NULL) {
+ while (*fname && *fname != '/')
+ ++fname;
+ if (*fname == '/') {
+ while (*fname && max--)
+ *where++ = *fname++;
+ }
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ while (*fname && *fname != '/')
+ ++fname;
+ return fname;
+}
+
+#define HOSTNAME_MAX 64
+/*
+ * Try and fetch a file by URL, returning the directory name for where
+ * it's unpacked, if successful.
+ */
+char *
+fileGetURL(char *base, char *spec)
+{
+ char host[HOSTNAME_MAX], file[FILENAME_MAX];
+ char pword[HOSTNAME_MAX + 40], *uname, *cp, *rp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ struct passwd *pw;
+ FILE *ftp;
+ pid_t tpid;
+ int i, status;
+ char *hint;
+
+ rp = NULL;
+ /* Special tip that sysinstall left for us */
+ hint = getenv("PKG_ADD_BASE");
+ if (!isURL(spec)) {
+ if (!base && !hint)
+ return NULL;
+ /* We've been given an existing URL (that's known-good) and now we need
+ to construct a composite one out of that and the basename we were
+ handed as a dependency. */
+ if (base) {
+ strcpy(fname, base);
+ /* Advance back two slashes to get to the root of the package hierarchy */
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, spec);
+ strcat(cp, ".tgz");
+ }
+ 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);
+ }
+ }
+ else
+ strcpy(fname, spec);
+ cp = fileURLHost(fname, host, HOSTNAME_MAX);
+ if (!*cp) {
+ warnx("URL `%s' has bad host part!", fname);
+ return NULL;
+ }
+
+ cp = fileURLFilename(fname, file, FILENAME_MAX);
+ if (!*cp) {
+ warnx("URL `%s' has bad filename part!", fname);
+ return NULL;
+ }
+
+ /* Maybe change to ftp if this doesn't work */
+ uname = "anonymous";
+
+ /* Make up a convincing "password" */
+ pw = getpwuid(getuid());
+ if (!pw) {
+ warnx("can't get user name for ID %d", getuid());
+ strcpy(pword, "joe@");
+ }
+ else {
+ char me[HOSTNAME_MAX];
+
+ gethostname(me, HOSTNAME_MAX);
+ snprintf(pword, HOSTNAME_MAX + 40, "%s@%s", pw->pw_name, me);
+ }
+ ftp = ftpGetURL(fname, uname, pword, &status);
+ if (ftp) {
+ if (isatty(0) || Verbose)
+ printf("Fetching %s...", fname), fflush(stdout);
+ pen[0] = '\0';
+ if ((rp = make_playpen(pen, 0)) != NULL) {
+ tpid = fork();
+ if (!tpid) {
+ dup2(fileno(ftp), 0);
+ i = execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-", 0);
+ exit(i);
+ }
+ else {
+ int pstat;
+
+ fclose(ftp);
+ tpid = waitpid(tpid, &pstat, 0);
+ if (Verbose)
+ printf("tar command returns %d status\n", WEXITSTATUS(pstat));
+ }
+ }
+ else
+ printf("Error: Unable to construct a new playpen for FTP!\n");
+ fclose(ftp);
+ if (rp && (isatty(0) || Verbose))
+ printf(" Done.\n");
+ }
+ else
+ printf("Error: FTP Unable to get %s: %s\n",
+ fname,
+ status ? ftpErrString(status) : hstrerror(h_errno));
+ return rp;
+}
+
+char *
+fileFindByPath(char *base, char *fname)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp;
+
+ 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) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, fname);
+ strcat(cp, ".tgz");
+ if (fexists(tmp))
+ return tmp;
+ }
+ }
+
+ cp = getenv("PKG_PATH");
+ while (cp) {
+ char *cp2 = strsep(&cp, ":");
+
+ snprintf(tmp, FILENAME_MAX, "%s/%s.tgz", cp2 ? cp2 : cp, fname);
+ if (fexists(tmp) && isfile(tmp))
+ return tmp;
+ }
+ return NULL;
+}
+
+char *
+fileGetContents(char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL) {
+ cleanup(0);
+ errx(2, "can't stat '%s'", fname);
+ }
+
+ contents = (char *)malloc(sb.st_size + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL) {
+ cleanup(0);
+ errx(2, "unable to open '%s' for reading", fname);
+ }
+ if (read(fd, contents, sb.st_size) != sb.st_size) {
+ cleanup(0);
+ errx(2, "short read on '%s' - did not get %qd bytes",
+ fname, 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, char *name, 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(char *name, char *str)
+{
+ FILE *fp;
+ int len;
+
+ fp = fopen(name, "w");
+ if (!fp) {
+ cleanup(0);
+ errx(2, "cannot fopen '%s' for writing", name);
+ }
+ len = strlen(str);
+ if (fwrite(str, 1, len, fp) != len) {
+ cleanup(0);
+ errx(2, "short fwrite on '%s', tried to write %d bytes", name, len);
+ }
+ if (fclose(fp)) {
+ cleanup(0);
+ errx(2, "failure to fclose '%s'", name);
+ }
+}
+
+void
+copy_file(char *dir, char *fname, char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s/%s %s", dir, fname, to);
+ if (vsystem(cmd)) {
+ cleanup(0);
+ errx(2, "could not perform '%s'", cmd);
+ }
+}
+
+void
+move_file(char *dir, char *fname, 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, "could not perform '%s'", 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(char *dir, 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, "copy_file: could not perform '%s'", cmd);
+ }
+}
+
+/* Unpack a tar file */
+int
+unpack(char *pkg, char *flist)
+{
+ char args[10], suffix[80], *cp;
+
+ args[0] = '\0';
+ /*
+ * Figure out by a crude heuristic whether this or not this is probably
+ * compressed.
+ */
+ if (strcmp(pkg, "-")) {
+ cp = rindex(pkg, '.');
+ if (cp) {
+ strcpy(suffix, cp + 1);
+ if (index(suffix, 'z') || index(suffix, 'Z'))
+ strcpy(args, "-z");
+ }
+ }
+ else
+ strcpy(args, "-z");
+ strcat(args, " -xpf");
+ if (vsystem("tar %s %s %s", args, 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, char *fmt, char *dir, char *name)
+{
+ char *cp, scratch[FILENAME_MAX * 2];
+
+ while (*fmt) {
+ if (*fmt == '%') {
+ switch (*++fmt) {
+ case 'F':
+ strcpy(buf, name);
+ buf += strlen(name);
+ break;
+
+ case 'D':
+ strcpy(buf, dir);
+ buf += strlen(dir);
+ break;
+
+ case 'B':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *cp != '/')
+ --cp;
+ *cp = '\0';
+ strcpy(buf, scratch);
+ buf += strlen(scratch);
+ break;
+
+ case 'f':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *(cp - 1) != '/')
+ --cp;
+ strcpy(buf, cp);
+ buf += strlen(cp);
+ break;
+
+ default:
+ *buf++ = *fmt;
+ break;
+ }
+ ++fmt;
+ }
+ else
+ *buf++ = *fmt++;
+ }
+ *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..75947b4
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,35 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: global.c,v 1.5 1997/02/22 16:09:48 peter Exp $";
+#endif
+
+/*
+ * 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 "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..595b2a1
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,178 @@
+/* $Id: lib.h,v 1.26 1998/02/16 17:16:47 jkh Exp $ */
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/file.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"
+
+/* 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"
+
+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_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST,
+ PLIST_OPTION
+};
+typedef enum _plist_t plist_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;
+};
+typedef struct _pack Package;
+
+/* Prototypes */
+/* Misc */
+int vsystem(const char *, ...);
+void cleanup(int);
+char *make_playpen(char *, size_t);
+char *where_playpen(void);
+void leave_playpen(void);
+off_t min_free(char *);
+
+/* String */
+char *get_dash_string(char **);
+char *copy_string(char *);
+Boolean suffix(char *, char *);
+void nuke_suffix(char *);
+void str_lowercase(char *);
+char *basename_of(char *);
+char *strconcat(char *, char *);
+
+/* File */
+Boolean fexists(char *);
+Boolean isdir(char *);
+Boolean isemptydir(char *fname);
+Boolean isemptyfile(char *fname);
+Boolean isfile(char *);
+Boolean isempty(char *);
+Boolean isURL(char *);
+char *fileGetURL(char *, char *);
+char *fileURLFilename(char *, char *, int);
+char *fileURLHost(char *, char *, int);
+char *fileFindByPath(char *, char *);
+char *fileGetContents(char *);
+void write_file(char *, char *);
+void copy_file(char *, char *, char *);
+void move_file(char *, char *, char *);
+void copy_hierarchy(char *, char *, Boolean);
+int delete_hierarchy(char *, Boolean, Boolean);
+int unpack(char *, char *);
+void format_cmd(char *, char *, char *, 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 *, char *name);
+void plist_delete(Package *, Boolean, plist_t, char *);
+void free_plist(Package *);
+void mark_plist(Package *);
+void csum_plist_entry(char *, PackingList);
+void add_plist(Package *, plist_t, char *);
+void add_plist_top(Package *, plist_t, char *);
+void delete_plist(Package *pkg, Boolean all, plist_t type, char *name);
+void write_plist(Package *, FILE *);
+void read_plist(Package *, FILE *);
+int plist_cmd(char *, char **);
+int delete_package(Boolean, Boolean, Package *);
+
+/* For all */
+int pkg_perform(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/msg.c b/usr.sbin/pkg_install/lib/msg.c
new file mode 100644
index 0000000..a580d8a
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/msg.c
@@ -0,0 +1,77 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: msg.c,v 1.10 1997/10/13 15:03:55 jkh Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.h"
+
+/* Die a relatively simple death */
+void
+upchuck(const char *err)
+{
+ cleanup(0);
+ errx(1, "fatal error during execution: %s", err);
+}
+
+/*
+ * 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("/dev/tty", "r");
+ if (!tty) {
+ cleanup(0);
+ errx(2, "can't open /dev/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..714dbac
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,177 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pen.c,v 1.29 1998/07/28 01:18:02 nectar Exp $";
+#endif
+
+/*
+ * 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 <err.h>
+#include "lib.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, size_t sz)
+{
+ char *cp;
+ struct stat sb;
+
+ if (pen[0] && stat(pen, &sb) != FAIL && (min_free(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,
+"can't find enough temporary space to extract the files, please set your\n"
+"PKG_TMPDIR environment variable to a location with at least %d bytes\n"
+"free", sz);
+ return NULL;
+ }
+ return pen;
+}
+
+#define MAX_STACK 20
+static char *pstack[MAX_STACK];
+static int pdepth = -1;
+
+static void
+pushPen(char *pen)
+{
+ if (++pdepth == MAX_STACK)
+ errx(2, "stack overflow in pushPen().\n");
+ 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, size_t sz)
+{
+ if (!find_play_pen(pen, sz))
+ return NULL;
+
+ if (!mkdtemp(pen)) {
+ cleanup(0);
+ errx(2, "can't mktemp '%s'", pen);
+ }
+ if (chmod(pen, 0755) == FAIL) {
+ cleanup(0);
+ errx(2, "can't mkdir '%s'", pen);
+ }
+
+ if (Verbose) {
+ if (sz)
+ fprintf(stderr, "Requested space: %d bytes, free space: %qd bytes in %s\n", (int)sz, min_free(pen), pen);
+ }
+
+ if (min_free(pen) < sz) {
+ rmdir(pen);
+ cleanup(0);
+ errx(2, "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", pen);
+ }
+
+ if (!getcwd(Previous, FILENAME_MAX)) {
+ upchuck("getcwd");
+ return NULL;
+ }
+
+ if (chdir(pen) == FAIL) {
+ cleanup(0);
+ errx(2, "can't chdir to '%s'", 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, "can't chdir back to '%s'", 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(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/plist.c b/usr.sbin/pkg_install/lib/plist.c
new file mode 100644
index 0000000..d18c543
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,507 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: plist.c,v 1.26 1998/09/01 06:58:11 jkh Exp $";
+#endif
+
+/*
+ * 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 "lib.h"
+#include <err.h>
+#include <md5.h>
+
+/* Add an item to a packing list */
+void
+add_plist(Package *p, plist_t type, 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;
+ }
+}
+
+void
+add_plist_top(Package *p, plist_t type, 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, 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, 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(char *s, char **arg)
+{
+ char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */
+ char *cp, *sp;
+
+ strcpy(cmd, s);
+ str_lowercase(cmd);
+ cp = cmd;
+ sp = s;
+ while (*cp) {
+ if (isspace(*cp)) {
+ *cp = '\0';
+ while (isspace(*sp)) /* Never sure if macro, increment later */
+ ++sp;
+ break;
+ }
+ ++cp, ++sp;
+ }
+ if (arg)
+ *arg = 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"))
+ 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, "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;
+
+ 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_cmd(pline + 1, &cp);
+ if (cmd == FAIL) {
+ cleanup(0);
+ errx(2, "bad command '%s'", pline);
+ }
+ if (*cp == '\0')
+ cp = NULL;
+ }
+ else
+ cmd = PLIST_FILE;
+ 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_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;
+
+ default:
+ cleanup(0);
+ errx(2, "unknown command type %d (%s)", 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;
+ 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, 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)) {
+ 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, buf[33];
+
+ if ((cp = MD5File(tmp, buf)) != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4)) {
+ if (Verbose)
+ printf("%s fails original MD5 checksum - %s\n",
+ 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;
+ }
+ }
+ 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(char *dir, Boolean ign_err, Boolean nukedirs)
+{
+ char *cp1, *cp2;
+
+ cp1 = cp2 = 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)) {
+ if (RMDIR(dir) && !ign_err)
+ return 1;
+ }
+ else {
+ if (REMOVE(dir, ign_err))
+ return 1;
+ }
+
+ if (!nukedirs)
+ return 0;
+ while (cp2) {
+ if ((cp2 = rindex(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 = 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..cb4fb41
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/str.c
@@ -0,0 +1,111 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+/*
+ * 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 "lib.h"
+
+/* Return the filename portion of a path */
+char *
+basename_of(char *str)
+{
+ char *basename = str + strlen(str) - 1;
+
+ while (basename != str && basename[-1] != '/')
+ --basename;
+ return basename;
+}
+
+char *
+strconcat(char *s1, char *s2)
+{
+ static char tmp[FILENAME_MAX];
+
+ tmp[0] = '\0';
+ strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX);
+ 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(s + 1);
+ else
+ *str = fileGetContents(s);
+ return *str;
+}
+
+/* Rather Obvious */
+char *
+copy_string(char *str)
+{
+ char *ret;
+
+ if (!str)
+ ret = NULL;
+ else {
+ ret = (char *)malloc(strlen(str) + 1);
+ strcpy(ret, str);
+ }
+ return ret;
+}
+
+/* Return TRUE if 'str' ends in suffix 'suff' */
+Boolean
+suffix(char *str, char *suff)
+{
+ char *idx;
+ Boolean ret = FALSE;
+
+ idx = rindex(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 = rindex(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;
+ }
+}
diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg
new file mode 100755
index 0000000..7730cd7
--- /dev/null
+++ b/usr.sbin/pkg_install/tkpkg
@@ -0,0 +1,186 @@
+#!/usr/local/bin/wish -f
+#$Id$
+#
+#$Log: tkpkg,v $
+#Revision 1.3 1997/01/14 07:14:23 jkh
+#Make the long-awaited change from $Id$ to $Id$
+#
+#This will make a number of things easier in the future, as well as (finally!)
+#avoiding the Id-smashing problem which has plagued developers for so long.
+#
+#Boy, I'm glad we're not using sup anymore. This update would have been
+#insane otherwise.
+#
+#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/pnpinfo/Makefile b/usr.sbin/pnpinfo/Makefile
new file mode 100644
index 0000000..3b87871
--- /dev/null
+++ b/usr.sbin/pnpinfo/Makefile
@@ -0,0 +1,11 @@
+# $Id: Makefile,v 1.1 1997/09/19 15:41:57 jmg Exp $
+
+PROG= pnpinfo
+
+SRCS= pnpinfo.c
+CFLAGS+=-I${.CURDIR}/../../sys
+MAN8= pnpinfo.8
+
+.PATH: ${.CURDIR}/../../contrib/pnpinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/Makefile b/usr.sbin/portmap/Makefile
new file mode 100644
index 0000000..e6c32a3
--- /dev/null
+++ b/usr.sbin/portmap/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id: Makefile,v 1.5 1999/03/28 10:55:03 markm Exp $
+
+PROG= portmap
+MAN8= portmap.8
+SRCS= portmap.c from_local.c pmap_check.c
+SUBDIR= pmap_set pmap_dump
+
+CFLAGS+=-DCHECK_PORT -DHOSTS_ACCESS
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/from_local.c b/usr.sbin/portmap/from_local.c
new file mode 100644
index 0000000..ffc04bb
--- /dev/null
+++ b/usr.sbin/portmap/from_local.c
@@ -0,0 +1,191 @@
+ /*
+ * Check if an address belongs to the local system. Adapted from:
+ *
+ * @(#)pmap_svc.c 1.32 91/03/11 Copyright 1984,1990 Sun Microsystems, Inc.
+ * @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC.
+ */
+
+/*
+ * 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[] = "@(#) from_local.c 1.2 93/11/16 21:50:02";
+#endif
+static const char rcsid[] =
+ "$Id: from_local.c,v 1.6 1998/01/21 10:55:39 brian Exp $";
+#endif
+
+#ifdef TEST
+#undef perror
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <netdb.h>
+#include <stdlib.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* How many interfaces could there be on a computer? */
+
+#define ESTIMATED_LOCAL 20
+static int num_local = -1;
+static struct in_addr *addrs;
+
+/* find_local - find all IP addresses for this host */
+
+int
+find_local()
+{
+ int mib[6], n, s, alloced;
+ size_t needed;
+ char *buf, *end, *ptr;
+ struct if_msghdr *ifm;
+ struct ifreq ifr;
+ struct sockaddr_dl *dl;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[4] = NET_RT_IFLIST;
+ mib[2] = mib[3] = mib[5] = 0;
+
+ if ((s = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (0);
+ }
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ close(s);
+ perror("sysctl(NET_RT_IFLIST)");
+ return 0;
+ }
+ if ((buf = (char *)malloc(needed)) == NULL) {
+ close(s);
+ perror("malloc");
+ return 0;
+ }
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ close(s);
+ free(buf);
+ perror("sysctl(NET_RT_IFLIST)(after malloc)");
+ return 0;
+ }
+
+ if (addrs) {
+ free(addrs);
+ addrs = NULL;
+ }
+ num_local = 0;
+ alloced = 0;
+ end = buf + needed;
+
+ for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)ptr;
+ dl = (struct sockaddr_dl *)(ifm + 1);
+ n = dl->sdl_nlen > sizeof ifr.ifr_name ?
+ sizeof ifr.ifr_name : dl->sdl_nlen;
+ if (n == 0)
+ continue;
+ strncpy(ifr.ifr_name, dl->sdl_data, n);
+ if (n < sizeof ifr.ifr_name)
+ ifr.ifr_name[n] = '\0';
+ /* we only want the first address from each interface */
+ if (ioctl(s, SIOCGIFFLAGS, &ifr) < 0)
+ perror("SIOCGIFFLAGS");
+ else if (ifr.ifr_flags & IFF_UP) /* active interface */
+ if (ioctl(s, SIOCGIFADDR, &ifr) < 0)
+ perror("SIOCGIFADDR");
+ else {
+ if (alloced < num_local + 1) {
+ alloced += ESTIMATED_LOCAL;
+ if (addrs)
+ addrs = (struct in_addr *)realloc(addrs, alloced * sizeof addrs[0]);
+ else
+ addrs = (struct in_addr *)malloc(alloced * sizeof addrs[0]);
+ if (addrs == NULL) {
+ perror("malloc/realloc");
+ num_local = 0;
+ break;
+ }
+ }
+ addrs[num_local++] = ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr;
+ }
+ }
+ free(buf);
+ close(s);
+
+ return num_local;
+}
+
+/* from_local - determine whether request comes from the local system */
+
+int
+from_local(addr)
+struct sockaddr_in *addr;
+{
+ int i;
+
+ if (num_local == -1 && find_local() == 0)
+ syslog(LOG_ERR, "cannot find any active local network interfaces");
+
+ for (i = 0; i < num_local; i++) {
+ if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
+ sizeof(struct in_addr)) == 0)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+#ifdef TEST
+
+main()
+{
+ char *inet_ntoa();
+ int i;
+
+ find_local();
+ for (i = 0; i < num_local; i++)
+ printf("%s\n", inet_ntoa(addrs[i]));
+}
+
+#endif
diff --git a/usr.sbin/portmap/pmap_check.c b/usr.sbin/portmap/pmap_check.c
new file mode 100644
index 0000000..c5aef7c
--- /dev/null
+++ b/usr.sbin/portmap/pmap_check.c
@@ -0,0 +1,264 @@
+ /*
+ * pmap_check - additional portmap security.
+ *
+ * Always reject non-local requests to update the portmapper tables.
+ *
+ * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the
+ * requests would appear to come from the local system, and nfs export
+ * restrictions could be bypassed.
+ *
+ * Refuse to forward requests to the nfsd process.
+ *
+ * Refuse to forward requests to NIS (YP) daemons; The only exception is the
+ * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial
+ * contact with the NIS server.
+ *
+ * Always allocate an unprivileged port when forwarding a request.
+ *
+ * If compiled with -DCHECK_PORT, require that requests to register or
+ * unregister a privileged port come from a privileged port. This makes it
+ * more difficult to replace a critical service by a trojan.
+ *
+ * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
+ * authorized by the /etc/hosts.{allow,deny} files. The local system is
+ * always treated as an authorized host. The access control tables are never
+ * consulted for requests from the local system, and are always consulted
+ * for requests from other hosts. Access control is based on IP addresses
+ * only; attempts to map an address to a host name might cause the
+ * portmapper to hang.
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_check.c 1.6 93/11/21 20:58:59";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/signal.h>
+
+#include "pmap_check.h"
+
+/* Explicit #defines in case the include files are not available. */
+
+#define NFSPROG ((u_long) 100003)
+#define MOUNTPROG ((u_long) 100005)
+#define YPXPROG ((u_long) 100069)
+#define YPPROG ((u_long) 100004)
+#define YPPROC_DOMAIN_NONACK ((u_long) 2)
+#define MOUNTPROC_MNT ((u_long) 1)
+
+static void logit();
+static void toggle_verboselog();
+int verboselog = 0;
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+
+/* A handful of macros for "readability". */
+
+#define good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "")
+
+#define legal_port(a,p) \
+ (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
+
+#define log_bad_port(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
+
+#define log_bad_host(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
+
+#define log_bad_owner(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from non-local host")
+
+#define log_no_forward(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request not forwarded")
+
+#define log_client(addr, proc, prog) \
+ logit(allow_severity, addr, proc, prog, "")
+
+/* check_startup - additional startup code */
+
+void check_startup()
+{
+
+ /*
+ * Give up root privileges so that we can never allocate a privileged
+ * port when forwarding an rpc request.
+ */
+ if (setuid(1) == -1) {
+ syslog(LOG_ERR, "setuid(1) failed: %m");
+ exit(1);
+ }
+ (void) signal(SIGINT, toggle_verboselog);
+}
+
+/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
+
+int
+check_default(addr, proc, prog)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+{
+#ifdef HOSTS_ACCESS
+ if (!(from_local(addr) || good_client(addr))) {
+ log_bad_host(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* check_privileged_port - additional checks for privileged-port updates */
+
+int
+check_privileged_port(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long port;
+{
+#ifdef CHECK_PORT
+ if (!legal_port(addr, port)) {
+ log_bad_port(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ return (TRUE);
+}
+
+/* check_setunset - additional checks for update requests */
+
+int
+check_setunset(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long port;
+{
+ if (!from_local(addr)) {
+#ifdef HOSTS_ACCESS
+ (void) good_client(addr); /* because of side effects */
+#endif
+ log_bad_owner(addr, proc, prog);
+ return (FALSE);
+ }
+ if (port && !check_privileged_port(addr, proc, prog, port))
+ return (FALSE);
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* check_callit - additional checks for forwarded requests */
+
+int
+check_callit(addr, proc, prog, aproc)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long aproc;
+{
+#ifdef HOSTS_ACCESS
+ if (!(from_local(addr) || good_client(addr))) {
+ log_bad_host(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
+ (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
+ (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
+ log_no_forward(addr, proc, prog);
+ return (FALSE);
+ }
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* toggle_verboselog - toggle verbose logging flag */
+
+static void toggle_verboselog(sig)
+int sig;
+{
+ (void) signal(sig, toggle_verboselog);
+ verboselog = !verboselog;
+}
+
+/* logit - report events of interest via the syslog daemon */
+
+static void logit(severity, addr, procnum, prognum, text)
+int severity;
+struct sockaddr_in *addr;
+u_long procnum;
+u_long prognum;
+char *text;
+{
+ char *procname;
+ char procbuf[4 * sizeof(u_long)];
+ char *progname;
+ char progbuf[4 * sizeof(u_long)];
+ struct rpcent *rpc;
+ struct proc_map {
+ u_long code;
+ char *proc;
+ };
+ struct proc_map *procp;
+ static struct proc_map procmap[] = {
+ {PMAPPROC_CALLIT, "callit"},
+ {PMAPPROC_DUMP, "dump"},
+ {PMAPPROC_GETPORT, "getport"},
+ {PMAPPROC_NULL, "null"},
+ {PMAPPROC_SET, "set"},
+ {PMAPPROC_UNSET, "unset"},
+ {0, 0},
+ };
+
+ /*
+ * Fork off a process or the portmap daemon might hang while
+ * getrpcbynumber() or syslog() does its thing.
+ */
+
+ if (fork() == 0) {
+
+ /* Try to map program number to name. */
+
+ if (prognum == 0) {
+ progname = "";
+ } else if ((rpc = getrpcbynumber((int) prognum))) {
+ progname = rpc->r_name;
+ } else {
+ sprintf(progname = progbuf, "%lu", prognum);
+ }
+
+ /* Try to map procedure number to name. */
+
+ for (procp = procmap; procp->proc && procp->code != procnum; procp++)
+ /* void */ ;
+ if ((procname = procp->proc) == 0)
+ sprintf(procname = procbuf, "%lu", (u_long) procnum);
+
+ /* Write syslog record. */
+
+ syslog(severity, "connect from %s to %s(%s)%s",
+ inet_ntoa(addr->sin_addr), procname, progname, text);
+ exit(0);
+ }
+}
diff --git a/usr.sbin/portmap/pmap_check.h b/usr.sbin/portmap/pmap_check.h
new file mode 100644
index 0000000..2c240df
--- /dev/null
+++ b/usr.sbin/portmap/pmap_check.h
@@ -0,0 +1,11 @@
+/* @(#) pmap_check.h 1.3 93/11/21 16:18:53 */
+
+extern int from_local();
+extern void check_startup();
+extern int check_default();
+extern int check_setunset();
+extern int check_privileged_port();
+extern int check_callit();
+extern int verboselog;
+extern int allow_severity;
+extern int deny_severity;
diff --git a/usr.sbin/portmap/pmap_dump/Makefile b/usr.sbin/portmap/pmap_dump/Makefile
new file mode 100644
index 0000000..0064f28
--- /dev/null
+++ b/usr.sbin/portmap/pmap_dump/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pmap_dump
+NOMAN= noman
+
+.include "${.CURDIR}/../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/pmap_dump/pmap_dump.c b/usr.sbin/portmap/pmap_dump/pmap_dump.c
new file mode 100644
index 0000000..409bf8b
--- /dev/null
+++ b/usr.sbin/portmap/pmap_dump/pmap_dump.c
@@ -0,0 +1,68 @@
+ /*
+ * pmap_dump - dump portmapper table in format readable by pmap_set
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_dump.c 1.1 92/06/11 22:53:15";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#include <rpc/rpcent.h>
+#else
+#include <netdb.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+static char *protoname();
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ struct sockaddr_in addr;
+ register struct pmaplist *list;
+ register struct rpcent *rpc;
+
+ get_myaddress(&addr);
+
+ for (list = pmap_getmaps(&addr); list; list = list->pml_next) {
+ rpc = getrpcbynumber((int) list->pml_map.pm_prog);
+ printf("%10lu %4lu %5s %6lu %s\n",
+ list->pml_map.pm_prog,
+ list->pml_map.pm_vers,
+ protoname(list->pml_map.pm_prot),
+ list->pml_map.pm_port,
+ rpc ? rpc->r_name : "");
+ }
+#undef perror
+ return (fclose(stdout) ? (perror(argv[0]), 1) : 0);
+}
+
+static char *protoname(proto)
+u_long proto;
+{
+ static char buf[BUFSIZ];
+
+ switch (proto) {
+ case IPPROTO_UDP:
+ return ("udp");
+ case IPPROTO_TCP:
+ return ("tcp");
+ default:
+ sprintf(buf, "%lu", proto);
+ return (buf);
+ }
+}
diff --git a/usr.sbin/portmap/pmap_set/Makefile b/usr.sbin/portmap/pmap_set/Makefile
new file mode 100644
index 0000000..987e320
--- /dev/null
+++ b/usr.sbin/portmap/pmap_set/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pmap_set
+NOMAN= noman
+
+.include "${.CURDIR}/../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/pmap_set/pmap_set.c b/usr.sbin/portmap/pmap_set/pmap_set.c
new file mode 100644
index 0000000..3ba94fc
--- /dev/null
+++ b/usr.sbin/portmap/pmap_set/pmap_set.c
@@ -0,0 +1,79 @@
+ /*
+ * pmap_set - set portmapper table from data produced by pmap_dump
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_set.c 1.1 92/06/11 22:53:16";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+int parse_line __P((char *, u_long *, u_long *, int *, unsigned *));
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ struct sockaddr_in addr;
+ char buf[BUFSIZ];
+ u_long prog;
+ u_long vers;
+ int prot;
+ unsigned port;
+
+ get_myaddress(&addr);
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ if (parse_line(buf, &prog, &vers, &prot, &port) == 0) {
+ warnx("malformed line: %s", buf);
+ return (1);
+ }
+ if (pmap_set(prog, vers, prot, (unsigned short) port) == 0)
+ warnx("not registered: %s", buf);
+ }
+ return (0);
+}
+
+/* parse_line - convert line to numbers */
+
+int
+parse_line(buf, prog, vers, prot, port)
+char *buf;
+u_long *prog;
+u_long *vers;
+int *prot;
+unsigned *port;
+{
+ char proto_name[BUFSIZ];
+
+ if (sscanf(buf, "%lu %lu %s %u", prog, vers, proto_name, port) != 4) {
+ return (0);
+ }
+ if (strcmp(proto_name, "tcp") == 0) {
+ *prot = IPPROTO_TCP;
+ return (1);
+ }
+ if (strcmp(proto_name, "udp") == 0) {
+ *prot = IPPROTO_UDP;
+ return (1);
+ }
+ if (sscanf(proto_name, "%d", prot) == 1) {
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/portmap/portmap.8 b/usr.sbin/portmap/portmap.8
new file mode 100644
index 0000000..5635356
--- /dev/null
+++ b/usr.sbin/portmap/portmap.8
@@ -0,0 +1,114 @@
+.\" Copyright (c) 1987 Sun Microsystems
+.\" 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.
+.\"
+.\" @(#)portmap.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PORTMAP 8
+.Os BSD 4.3
+.Sh NAME
+.Nm portmap
+.Nd
+.Tn RPC
+program,version
+to
+.Tn DARPA
+port mapper
+.Sh SYNOPSIS
+.Nm portmap
+.Op Fl d
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Portmap
+is a server that converts
+.Tn RPC
+program numbers into
+.Tn DARPA
+protocol port numbers.
+It must be running in order to make
+.Tn RPC
+calls.
+.Pp
+When an
+.Tn RPC
+server is started, it will tell
+.Nm
+what port number it is listening to, and what
+.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 will first contact
+.Nm
+on the server machine to determine
+the port number where
+.Tn RPC
+packets should be sent.
+.Pp
+.Nm Portmap
+must be started before any
+.Tn RPC
+servers are invoked.
+.Pp
+Normally
+.Nm
+forks and dissociates itself from the terminal
+like any other daemon.
+.Nm Portmap
+then logs errors using
+.Xr syslog 3 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Prevent
+.Nm
+from running as a daemon,
+and causes errors and debugging information
+to be printed to the standard error output.
+.It Fl v
+Enable verbose logging access control checks.
+.El
+.Sh SEE ALSO
+.Xr inetd.conf 5 ,
+.Xr inetd 8 ,
+.Xr rpcinfo 8
+.Sh BUGS
+If
+.Nm
+crashes, all servers must be restarted.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3
diff --git a/usr.sbin/portmap/portmap.c b/usr.sbin/portmap/portmap.c
new file mode 100644
index 0000000..c77f76a
--- /dev/null
+++ b/usr.sbin/portmap/portmap.c
@@ -0,0 +1,613 @@
+/*-
+ * 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 const 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[] = "@(#)portmap.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: portmap.c,v 1.6 1997/10/09 07:17:15 charnier Exp $";
+#endif /* not lint */
+
+/*
+@(#)portmap.c 2.3 88/08/11 4.0 RPCSRC
+static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
+*/
+
+/*
+ * portmap.c, Implements the program,version to port number mapping for
+ * rpc.
+ */
+
+/*
+ * 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
+ */
+
+#include <err.h>
+#include <errno.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_prot.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+
+#include "pmap_check.h"
+
+void reg_service();
+void reap();
+static void callit();
+static void usage __P((void));
+
+struct pmaplist *pmaplist;
+int debugging = 0;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *xprt;
+ int sock, c;
+ struct sockaddr_in addr;
+ int len = sizeof(struct sockaddr_in);
+ register struct pmaplist *pml;
+
+ while ((c = getopt(argc, argv, "dv")) != -1) {
+ switch (c) {
+
+ case 'd':
+ debugging = 1;
+ break;
+
+ case 'v':
+ verboselog = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (!debugging && daemon(0, 0))
+ err(1, "fork");
+
+ openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID,
+ LOG_DAEMON);
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ syslog(LOG_ERR, "cannot create udp socket: %m");
+ exit(1);
+ }
+
+ addr.sin_addr.s_addr = 0;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PMAPPORT);
+ if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+ syslog(LOG_ERR, "cannot bind udp: %m");
+ exit(1);
+ }
+
+ if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "couldn't do udp_create");
+ exit(1);
+ }
+ /* make an entry for ourself */
+ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_next = 0;
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_prot = IPPROTO_UDP;
+ pml->pml_map.pm_port = PMAPPORT;
+ pmaplist = pml;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ syslog(LOG_ERR, "cannot create tcp socket: %m");
+ exit(1);
+ }
+ if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+ syslog(LOG_ERR, "cannot bind tcp: %m");
+ exit(1);
+ }
+ if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
+ == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "couldn't do tcp_create");
+ exit(1);
+ }
+ /* make an entry for ourself */
+ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_prot = IPPROTO_TCP;
+ pml->pml_map.pm_port = PMAPPORT;
+ pml->pml_next = pmaplist;
+ pmaplist = pml;
+
+ (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
+
+ /* additional initializations */
+ check_startup();
+ (void)signal(SIGCHLD, reap);
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned unexpectedly");
+ abort();
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: portmap [-dv]\n");
+ exit(1);
+}
+
+#ifndef lint
+/* need to override perror calls in rpc library */
+void
+perror(what)
+ const char *what;
+{
+
+ syslog(LOG_ERR, "%s: %m", what);
+}
+#endif
+
+static struct pmaplist *
+find_service(prog, vers, prot)
+ u_long prog, vers, prot;
+{
+ register struct pmaplist *hit = NULL;
+ register struct pmaplist *pml;
+
+ for (pml = pmaplist; 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);
+}
+
+/*
+ * 1 OK, 0 not
+ */
+void
+reg_service(rqstp, xprt)
+ struct svc_req *rqstp;
+ SVCXPRT *xprt;
+{
+ struct pmap reg;
+ struct pmaplist *pml, *prevpml, *fnd;
+ int ans, port;
+ caddr_t t;
+
+ /*
+ * Later wrappers change the logging severity on the fly. Reset to
+ * defaults before handling the next request.
+ */
+ allow_severity = LOG_INFO;
+ deny_severity = LOG_WARNING;
+
+ if (debugging)
+ (void) fprintf(stderr, "server: about to do a switch\n");
+ switch (rqstp->rq_proc) {
+
+ case PMAPPROC_NULL:
+ /*
+ * Null proc call
+ */
+ /* remote host authorization check */
+ check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+ if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
+ abort();
+ }
+ break;
+
+ case PMAPPROC_SET:
+ /*
+ * Set a program,version to port mapping
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ /* reject non-local requests, protect priv. ports */
+ if (!check_setunset(svc_getcaller(xprt),
+ rqstp->rq_proc, reg.pm_prog, reg.pm_port)) {
+ ans = 0;
+ goto done;
+ }
+ /*
+ * 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.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
+ if (fnd->pml_map.pm_port == reg.pm_port) {
+ ans = 1;
+ goto done;
+ }
+ else {
+ ans = 0;
+ goto done;
+ }
+ } else {
+ /*
+ * add to END of list
+ */
+ pml = (struct pmaplist *)
+ malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_map = reg;
+ pml->pml_next = 0;
+ if (pmaplist == 0) {
+ pmaplist = pml;
+ } else {
+ for (fnd= pmaplist; fnd->pml_next != 0;
+ fnd = fnd->pml_next);
+ fnd->pml_next = pml;
+ }
+ ans = 1;
+ }
+ done:
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_UNSET:
+ /*
+ * Remove a program,version to port mapping.
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ ans = 0;
+ /* reject non-local requests */
+ if (!check_setunset(svc_getcaller(xprt),
+ rqstp->rq_proc, reg.pm_prog, (u_long) 0))
+ goto done;
+ for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
+ if ((pml->pml_map.pm_prog != reg.pm_prog) ||
+ (pml->pml_map.pm_vers != reg.pm_vers)) {
+ /* both pml & prevpml move forwards */
+ prevpml = pml;
+ pml = pml->pml_next;
+ continue;
+ }
+ /* found it; pml moves forward, prevpml stays */
+ /* privileged port check */
+ if (!check_privileged_port(svc_getcaller(xprt),
+ rqstp->rq_proc,
+ reg.pm_prog,
+ pml->pml_map.pm_port)) {
+ ans = 0;
+ break;
+ }
+ ans = 1;
+ t = (caddr_t)pml;
+ pml = pml->pml_next;
+ if (prevpml == NULL)
+ pmaplist = pml;
+ else
+ prevpml->pml_next = pml;
+ free(t);
+ }
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_GETPORT:
+ /*
+ * Lookup the mapping for a program,version and return its port
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ /* remote host authorization check */
+ if (!check_default(svc_getcaller(xprt),
+ rqstp->rq_proc,
+ reg.pm_prog)) {
+ ans = 0;
+ goto done;
+ }
+ fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd)
+ port = fnd->pml_map.pm_port;
+ else
+ port = 0;
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_DUMP:
+ /*
+ * Return the current set of mapped program,version
+ */
+ if (!svc_getargs(xprt, xdr_void, NULL))
+ svcerr_decode(xprt);
+ else {
+ /* remote host authorization check */
+ struct pmaplist *p;
+ if (!check_default(svc_getcaller(xprt),
+ rqstp->rq_proc, (u_long) 0)) {
+ p = 0; /* send empty list */
+ } else {
+ p = pmaplist;
+ }
+ if ((!svc_sendreply(xprt, xdr_pmaplist,
+ (caddr_t)&p)) && debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ 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.
+ */
+ callit(rqstp, xprt);
+ break;
+
+ default:
+ /* remote host authorization check */
+ check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+ svcerr_noproc(xprt);
+ break;
+ }
+}
+
+
+/*
+ * Stuff for the rmtcall service
+ */
+#define ARGSIZE 9000
+
+struct encap_parms {
+ u_int arglen;
+ char *args;
+};
+
+static bool_t
+xdr_encap_parms(xdrs, epp)
+ XDR *xdrs;
+ struct encap_parms *epp;
+{
+
+ return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
+}
+
+struct rmtcallargs {
+ u_long rmt_prog;
+ u_long rmt_vers;
+ u_long rmt_port;
+ u_long rmt_proc;
+ struct encap_parms rmt_args;
+};
+
+static bool_t
+xdr_rmtcall_args(xdrs, cap)
+ register XDR *xdrs;
+ register struct rmtcallargs *cap;
+{
+
+ /* does not get a port number */
+ if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
+ xdr_u_long(xdrs, &(cap->rmt_vers)) &&
+ xdr_u_long(xdrs, &(cap->rmt_proc))) {
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ }
+ return (FALSE);
+}
+
+static bool_t
+xdr_rmtcall_result(xdrs, cap)
+ register XDR *xdrs;
+ register struct rmtcallargs *cap;
+{
+ if (xdr_u_long(xdrs, &(cap->rmt_port)))
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ return (FALSE);
+}
+
+/*
+ * only worries about the struct encap_parms part of struct rmtcallargs.
+ * The arglen must already be set!!
+ */
+static bool_t
+xdr_opaque_parms(xdrs, cap)
+ XDR *xdrs;
+ struct rmtcallargs *cap;
+{
+
+ return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
+}
+
+/*
+ * This routine finds and sets the length of incoming opaque paraters
+ * and then calls xdr_opaque_parms.
+ */
+static bool_t
+xdr_len_opaque_parms(xdrs, cap)
+ register XDR *xdrs;
+ struct rmtcallargs *cap;
+{
+ register u_int beginpos, lowpos, highpos, currpos, pos;
+
+ beginpos = lowpos = pos = xdr_getpos(xdrs);
+ highpos = lowpos + ARGSIZE;
+ while ((int)(highpos - lowpos) >= 0) {
+ currpos = (lowpos + highpos) / 2;
+ if (xdr_setpos(xdrs, currpos)) {
+ pos = currpos;
+ lowpos = currpos + 1;
+ } else {
+ highpos = currpos - 1;
+ }
+ }
+ xdr_setpos(xdrs, beginpos);
+ cap->rmt_args.arglen = pos - beginpos;
+ return (xdr_opaque_parms(xdrs, cap));
+}
+
+/*
+ * 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, less the requestor be
+ * overrun with complaints at the expense of not hearing a valid reply ...
+ *
+ * This now forks so that the program & process that it calls can call
+ * back to the portmapper.
+ */
+static void
+callit(rqstp, xprt)
+ struct svc_req *rqstp;
+ SVCXPRT *xprt;
+{
+ struct rmtcallargs a;
+ struct pmaplist *pml;
+ u_short port;
+ struct sockaddr_in me;
+ int pid, so = -1;
+ CLIENT *client;
+ struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
+ struct timeval timeout;
+ char buf[ARGSIZE];
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ a.rmt_args.args = buf;
+ if (!svc_getargs(xprt, xdr_rmtcall_args, (caddr_t)&a))
+ return;
+ /* host and service access control */
+ if (!check_callit(svc_getcaller(xprt),
+ rqstp->rq_proc, a.rmt_prog, a.rmt_proc))
+ return;
+ if ((pml = find_service(a.rmt_prog, a.rmt_vers,
+ (u_long)IPPROTO_UDP)) == NULL)
+ return;
+ /*
+ * fork a child to do the work. Parent immediately returns.
+ * Child exits upon completion.
+ */
+ if ((pid = fork()) != 0) {
+ if (pid < 0)
+ syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
+ a.rmt_prog);
+ return;
+ }
+ port = pml->pml_map.pm_port;
+ get_myaddress(&me);
+ me.sin_port = htons(port);
+ client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
+ if (client != (CLIENT *)NULL) {
+ if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
+ client->cl_auth = authunix_create(au->aup_machname,
+ au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
+ }
+ a.rmt_port = (u_long)port;
+ if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
+ xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
+ svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a);
+ }
+ AUTH_DESTROY(client->cl_auth);
+ clnt_destroy(client);
+ }
+ (void)close(so);
+ exit(0);
+}
+
+void
+reap()
+{
+ int save_errno;
+
+ save_errno = errno;
+ while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
+ errno = save_errno;
+}
diff --git a/usr.sbin/ppp/Makefile b/usr.sbin/ppp/Makefile
new file mode 100644
index 0000000..4211d4c
--- /dev/null
+++ b/usr.sbin/ppp/Makefile
@@ -0,0 +1,61 @@
+# $Id: Makefile,v 1.51 1999/01/28 01:56:30 brian Exp $
+
+MAINTAINER=brian@FreeBSD.org
+
+PROG= ppp
+SRCS= arp.c async.c auth.c bundle.c cbcp.c ccp.c chap.c chat.c command.c \
+ datalink.c deflate.c defs.c filter.c fsm.c hdlc.c id.c iface.c ip.c \
+ ipcp.c iplist.c lcp.c link.c log.c lqr.c main.c mbuf.c modem.c \
+ mp.c pap.c physical.c pred.c probe.c prompt.c route.c server.c \
+ sig.c slcompress.c systems.c throughput.c timer.c tun.c vjcomp.c
+CFLAGS+=-Wall
+LDADD+= -lcrypt -lmd -lutil -lz
+DPADD+= ${LIBCRYPT} ${LIBMD} ${LIBUTIL} ${LIBZ}
+BINMODE=4554
+BINOWN= root
+BINGRP= network
+MAN8= ppp.8
+
+.if defined(RELEASE_CRUNCH)
+CFLAGS+=-DRELEASE_CRUNCH
+.endif
+
+.if defined(NOALIAS)
+CFLAGS+=-DNOALIAS
+.else
+.if !defined(RELEASE_CRUNCH)
+SRCS+= alias_cmd.c
+LDADD+= -lalias
+DPADD+= ${LIBALIAS}
+.endif
+.endif
+
+.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && !defined(NOSECURE) && !defined(RELEASE_CRUNCH)
+DISTRIBUTION=des
+CFLAGS+=-DHAVE_DES
+SRCS+= chap_ms.c
+LDADD+= -ldes
+DPADD+= ${LIBDES}
+.endif
+
+.if defined(NORADIUS)
+CFLAGS+=-DNORADIUS
+.else
+.if !defined(RELEASE_CRUNCH)
+SRCS+= radius.c
+LDADD+= -lradius
+DPADD+= ${LIBRADIUS}
+.endif
+.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.
+CFLAGS+=-DNOALIAS -DNORADIUS
+SRCS+= alias_cmd.c chap_ms.c radius.c
+chap_ms.o alias_cmd.o radius.o:
+ >null_${.PREFIX}.c
+ cc -c -o ${.TARGET} null_${.PREFIX}.c
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/README.alias b/usr.sbin/ppp/README.alias
new file mode 100644
index 0000000..5d29773
--- /dev/null
+++ b/usr.sbin/ppp/README.alias
@@ -0,0 +1,352 @@
+User PPP 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 packet aliasing (IP masquerading) code.
+Enabling this, either by the "-alias" command line option or the
+"alias enable yes" command in a ppp.conf file, makes the ppp host
+automatically alias 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-aliased.
+
+The process of aliasing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are aliased 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 packet aliasing 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 packet aliasing code also handle 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
+packet aliasing 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 "-alias" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in packet aliasing
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The masquerading software aliases 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 aliasing behaviour in a simple manner (no need for
+recompilation), a new command has been added to ppp: alias. This
+is in addition to the -alias command line option. System managers and
+more experienced users may prefer to use the ppp command syntax
+within the ppp.conf file. The alias command also allows packet aliasing
+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 aliasing is enabled.
+
+The syntax for 'alias' is
+
+ ppp> alias option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - alias enable [yes|no] (default no)
+
+Enable packet aliasing functionality. If disabled, no other alias
+options will have any effect. You should usually enable aliasing
+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 aliasing, consider using the -alias option to ppp instead of this
+command.
+
+
+ - alias 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 aliasing
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the aliasing software a
+fairly efficient one-way firewall. The default is no, which will allow
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - alias log [yes|no]
+
+Controls logging of alias 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 aliasing. The debugging information is fairly limited, listing
+the number of aliasing links open for different protocols.
+
+
+ - alias same_ports [yes|no] (default yes)
+
+When a connection is being established going through the aliasing
+routines, it will normally have its port number changed to allow the
+aliasing code to track it. If same_ports is enabled, the alias
+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
+aliasing has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - alias use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the packet aliasing
+software does not have to allocate system sockets when it chooses an
+aliasing 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.
+
+
+ - alias unregistered_only [yes|no] (default no)
+
+Packet aliasing 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 aliased.
+
+
+- alias port <proto> <local addr>:<port> <alias port>
+
+This command allows incoming traffic to <alias 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:
+
+ alias 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.
+
+
+- alias 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:
+
+ alias addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address aliases specify the same public address
+as follows
+
+ alias addr 192.168.0.2 public_addr
+ alias addr 192.168.0.3 public_addr
+ alias 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 aliased
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called packet aliasing here has been variously called masquerading,
+network address translation (NAT) 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 alias 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 packet aliasing 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 aliasing and incoming de-aliasing).
+
+
+
+5. Authors / Acknowledgements
+
+Charles Mott (cmott@srv.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/README.changes b/usr.sbin/ppp/README.changes
new file mode 100644
index 0000000..c5b4a1c
--- /dev/null
+++ b/usr.sbin/ppp/README.changes
@@ -0,0 +1,82 @@
+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 parameter. Use `set lqrperiod' and `set
+ {lcp,ccp,ipcp,chap,pap}retry' for the other timers. `show timeout'
+ is also now available 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''.
diff --git a/usr.sbin/ppp/README.devel b/usr.sbin/ppp/README.devel
new file mode 100644
index 0000000..acafb46
--- /dev/null
+++ b/usr.sbin/ppp/README.devel
@@ -0,0 +1,19 @@
+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/FAQ/userppp.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..5d29773
--- /dev/null
+++ b/usr.sbin/ppp/README.nat
@@ -0,0 +1,352 @@
+User PPP 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 packet aliasing (IP masquerading) code.
+Enabling this, either by the "-alias" command line option or the
+"alias enable yes" command in a ppp.conf file, makes the ppp host
+automatically alias 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-aliased.
+
+The process of aliasing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are aliased 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 packet aliasing 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 packet aliasing code also handle 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
+packet aliasing 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 "-alias" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in packet aliasing
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The masquerading software aliases 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 aliasing behaviour in a simple manner (no need for
+recompilation), a new command has been added to ppp: alias. This
+is in addition to the -alias command line option. System managers and
+more experienced users may prefer to use the ppp command syntax
+within the ppp.conf file. The alias command also allows packet aliasing
+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 aliasing is enabled.
+
+The syntax for 'alias' is
+
+ ppp> alias option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - alias enable [yes|no] (default no)
+
+Enable packet aliasing functionality. If disabled, no other alias
+options will have any effect. You should usually enable aliasing
+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 aliasing, consider using the -alias option to ppp instead of this
+command.
+
+
+ - alias 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 aliasing
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the aliasing software a
+fairly efficient one-way firewall. The default is no, which will allow
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - alias log [yes|no]
+
+Controls logging of alias 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 aliasing. The debugging information is fairly limited, listing
+the number of aliasing links open for different protocols.
+
+
+ - alias same_ports [yes|no] (default yes)
+
+When a connection is being established going through the aliasing
+routines, it will normally have its port number changed to allow the
+aliasing code to track it. If same_ports is enabled, the alias
+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
+aliasing has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - alias use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the packet aliasing
+software does not have to allocate system sockets when it chooses an
+aliasing 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.
+
+
+ - alias unregistered_only [yes|no] (default no)
+
+Packet aliasing 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 aliased.
+
+
+- alias port <proto> <local addr>:<port> <alias port>
+
+This command allows incoming traffic to <alias 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:
+
+ alias 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.
+
+
+- alias 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:
+
+ alias addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address aliases specify the same public address
+as follows
+
+ alias addr 192.168.0.2 public_addr
+ alias addr 192.168.0.3 public_addr
+ alias 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 aliased
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called packet aliasing here has been variously called masquerading,
+network address translation (NAT) 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 alias 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 packet aliasing 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 aliasing and incoming de-aliasing).
+
+
+
+5. Authors / Acknowledgements
+
+Charles Mott (cmott@srv.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/alias_cmd.c b/usr.sbin/ppp/alias_cmd.c
new file mode 100644
index 0000000..430ab47
--- /dev/null
+++ b/usr.sbin/ppp/alias_cmd.c
@@ -0,0 +1,306 @@
+/*-
+ * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
+ * who places it in the public domain without restriction.
+ *
+ * $Id: alias_cmd.c,v 1.21 1999/03/07 18:13:44 brian Exp $
+ */
+
+#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/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "alias_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 "ipcp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+
+
+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 *);
+
+
+int
+alias_RedirectPort(struct cmdargs const *arg)
+{
+ if (!arg->bundle->AliasEnabled) {
+ prompt_Printf(arg->prompt, "Alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn + 3) {
+ char proto_constant;
+ const char *proto;
+ u_short hlocalport;
+ u_short llocalport;
+ u_short haliasport;
+ u_short laliasport;
+ u_short port;
+ int error;
+ struct in_addr local_addr;
+ struct in_addr null_addr;
+ struct alias_link *link;
+
+ 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], &local_addr, &llocalport,
+ &hlocalport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "alias port: error reading localaddr:port\n");
+ return -1;
+ }
+ error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
+ proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "alias port: error reading alias port\n");
+ return -1;
+ }
+ null_addr.s_addr = INADDR_ANY;
+
+ if (llocalport > hlocalport) {
+ port = llocalport;
+ llocalport = hlocalport;
+ hlocalport = port;
+ }
+
+ if (laliasport > haliasport) {
+ port = laliasport;
+ laliasport = haliasport;
+ haliasport = port;
+ }
+
+ if (haliasport - laliasport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "alias port: Port ranges must be equal\n");
+ return -1;
+ }
+
+ for (port = laliasport; port <= haliasport; port++) {
+ link = PacketAliasRedirectPort(local_addr,
+ htons(llocalport + (port - laliasport)),
+ null_addr, 0, null_addr, htons(port),
+ proto_constant);
+
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "alias port: %d: error %d\n", port, error);
+ return 1;
+ }
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+int
+alias_RedirectAddr(struct cmdargs const *arg)
+{
+ if (!arg->bundle->AliasEnabled) {
+ prompt_Printf(arg->prompt, "alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn+2) {
+ int error;
+ struct in_addr local_addr;
+ struct in_addr alias_addr;
+ struct alias_link *link;
+
+ error = StrToAddr(arg->argv[arg->argn], &local_addr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(arg->argv[arg->argn+1], &alias_addr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "Usage: alias %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ link = PacketAliasRedirectAddr(local_addr, alias_addr);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "address redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "Usage: alias %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
+alias_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 + (f ? 1 : 0))
+ break;
+ if (f)
+ cmd[pos++] = ' ';
+ strcpy(cmd + pos, arg->argv[f]);
+ pos += len;
+ }
+
+ return PacketAliasProxyRule(cmd);
+}
+
+int
+alias_Pptp(struct cmdargs const *arg)
+{
+ struct in_addr addr;
+
+ if (arg->argc == arg->argn) {
+ addr.s_addr = INADDR_NONE;
+ PacketAliasPptp(addr);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ 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;
+ }
+
+ PacketAliasPptp(addr);
+ return 0;
+}
diff --git a/usr.sbin/ppp/alias_cmd.h b/usr.sbin/ppp/alias_cmd.h
new file mode 100644
index 0000000..cf96bcf
--- /dev/null
+++ b/usr.sbin/ppp/alias_cmd.h
@@ -0,0 +1,13 @@
+/*-
+ * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
+ * who places it in the public domain without restriction.
+ *
+ * $Id: alias_cmd.h,v 1.9 1999/03/07 15:02:37 brian Exp $
+ */
+
+struct cmdargs;
+
+extern int alias_RedirectPort(struct cmdargs const *);
+extern int alias_RedirectAddr(struct cmdargs const *);
+extern int alias_ProxyRule(struct cmdargs const *);
+extern int alias_Pptp(struct cmdargs const *);
diff --git a/usr.sbin/ppp/arp.c b/usr.sbin/ppp/arp.c
new file mode 100644
index 0000000..892228c
--- /dev/null
+++ b/usr.sbin/ppp/arp.c
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ *
+ * $Id: arp.c,v 1.31 1998/10/26 19:07:38 brian Exp $
+ *
+ */
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <unistd.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 "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 "bundle.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 (!get_ether_addr(s, addr, &arpmsg.hwa)) {
+ 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 (write(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 (!get_ether_addr(s, addr, &dls.sdl)) {
+ 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 */
+
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+
+int
+get_ether_addr(int s, struct in_addr ipaddr, struct sockaddr_dl *hwaddr)
+{
+ int mib[6], sa_len, skip, b;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr *sa;
+ struct sockaddr_dl *dl;
+ struct sockaddr_in *ifa, *mask;
+
+ 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, "get_ether_addr: 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;
+ sa = (struct sockaddr *)(ifam+1); /* pile of sa's at end */
+ 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);
+ b = 1;
+ ifa = mask = NULL;
+ while (b < (RTA_NETMASK|RTA_IFA) && sa < (struct sockaddr *)ptr) {
+ switch (b) {
+ case RTA_IFA:
+ ifa = (struct sockaddr_in *)sa;
+ break;
+ case RTA_NETMASK:
+ /*
+ * Careful here ! this sockaddr doesn't have sa_family set to
+ * AF_INET, and is only 8 bytes big ! I have no idea why !
+ */
+ mask = (struct sockaddr_in *)sa;
+ break;
+ }
+ if (ifam->ifam_addrs & b) {
+#define ALN sizeof(ifa->sin_addr.s_addr)
+ sa_len = sa->sa_len > 0 ? ((sa->sa_len-1)|(ALN-1))+1 : ALN;
+ sa = (struct sockaddr *)((char *)sa + sa_len);
+ }
+ b <<= 1;
+ }
+ if (log_IsKept(LogDEBUG)) {
+ char a[16];
+ strncpy(a, inet_ntoa(mask->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_family == AF_INET &&
+ (ifa->sin_addr.s_addr & mask->sin_addr.s_addr) ==
+ (ipaddr.s_addr & mask->sin_addr.s_addr)) {
+ log_Printf(LogPHASE, "Found interface %.*s for %s\n",
+ dl->sdl_alen, 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..25e4270
--- /dev/null
+++ b/usr.sbin/ppp/arp.h
@@ -0,0 +1,29 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: arp.h,v 1.7.2.7 1998/05/01 19:23:50 brian Exp $
+ *
+ */
+
+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 get_ether_addr(int, struct in_addr, struct sockaddr_dl *);
diff --git a/usr.sbin/ppp/async.c b/usr.sbin/ppp/async.c
new file mode 100644
index 0000000..ef59427
--- /dev/null
+++ b/usr.sbin/ppp/async.c
@@ -0,0 +1,184 @@
+/*
+ * PPP Async HDLC Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: async.c,v 1.16 1998/05/21 21:43:55 brian Exp $
+ *
+ */
+#include <sys/types.h>
+
+#include <string.h>
+#include <termios.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 "lcpproto.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->mode = MODE_HUNT;
+ async->length = 0;
+ async->my_accmap = async->his_accmap = 0xffffffff;
+ memset(async->cfg.EscMap, '\0', sizeof async->cfg.EscMap);
+}
+
+void
+async_SetLinkParams(struct async *async, struct lcp *lcp)
+{
+ async->my_accmap = lcp->want_accmap;
+ async->his_accmap = lcp->his_accmap;
+}
+
+/*
+ * Encode into async HDLC byte code if necessary
+ */
+static void
+HdlcPutByte(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;
+}
+
+void
+async_Output(int pri, struct mbuf *bp, int proto, struct physical *physical)
+{
+ u_char *cp, *sp, *ep;
+ struct mbuf *wp;
+ int cnt;
+
+ if (mbuf_Length(bp) > HDLCSIZE) {
+ mbuf_Free(bp);
+ return;
+ }
+ cp = physical->async.xbuff;
+ ep = cp + HDLCSIZE - 10;
+ wp = bp;
+ *cp++ = HDLC_SYN;
+ while (wp) {
+ sp = MBUF_CTOP(wp);
+ for (cnt = wp->cnt; cnt > 0; cnt--) {
+ HdlcPutByte(&physical->async, &cp, *sp++, proto);
+ if (cp >= ep) {
+ mbuf_Free(bp);
+ return;
+ }
+ }
+ wp = wp->next;
+ }
+ *cp++ = HDLC_SYN;
+
+ cnt = cp - physical->async.xbuff;
+ log_DumpBuff(LogASYNC, "WriteModem", physical->async.xbuff, cnt);
+ link_Write(&physical->link, pri, (char *)physical->async.xbuff, cnt);
+ link_AddOutOctets(&physical->link, cnt);
+ mbuf_Free(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 = mbuf_Alloc(async->length, MB_ASYNC);
+ 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;
+ }
+ /* Fall into ... */
+ 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;
+}
+
+void
+async_Input(struct bundle *bundle, u_char *buff, int cnt,
+ struct physical *physical)
+{
+ struct mbuf *bp;
+
+ link_AddInOctets(&physical->link, cnt);
+
+ if (physical_IsSync(physical)) {
+ bp = mbuf_Alloc(cnt, MB_ASYNC);
+ memcpy(MBUF_CTOP(bp), buff, cnt);
+ bp->cnt = cnt;
+ hdlc_Input(bundle, bp, physical);
+ } else {
+ while (cnt > 0) {
+ bp = async_Decode(&physical->async, *buff++);
+ if (bp)
+ hdlc_Input(bundle, bp, physical);
+ cnt--;
+ }
+ }
+}
diff --git a/usr.sbin/ppp/async.h b/usr.sbin/ppp/async.h
new file mode 100644
index 0000000..4bd30a9
--- /dev/null
+++ b/usr.sbin/ppp/async.h
@@ -0,0 +1,52 @@
+/*-
+ * 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.
+ *
+ * $Id: async.h,v 1.3 1998/05/21 21:43:57 brian Exp $
+ */
+
+#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_SetLinkParams(struct async *, struct lcp *);
+extern void async_Output(int, struct mbuf *, int, struct physical *);
+extern void async_Input(struct bundle *, u_char *, int, struct physical *);
diff --git a/usr.sbin/ppp/auth.c b/usr.sbin/ppp/auth.c
new file mode 100644
index 0000000..a60b525
--- /dev/null
+++ b/usr.sbin/ppp/auth.c
@@ -0,0 +1,366 @@
+/*
+ * PPP Secret Key Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: auth.c,v 1.42 1999/02/26 21:28:06 brian Exp $
+ *
+ * TODO:
+ * o Implement check against with registered IP addresses.
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.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 "ipcp.h"
+#include "auth.h"
+#include "systems.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "chat.h"
+#include "lcpproto.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 "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";
+}
+
+static int
+auth_CheckPasswd(const char *name, const char *data, const char *key)
+{
+ if (!strcmp(data, "*")) {
+ /* 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;
+ }
+
+ return !strcmp(data, key);
+}
+
+int
+auth_SetPhoneList(const char *name, char *phone, int phonelen)
+{
+ FILE *fp;
+ int n;
+ char *vector[6];
+ char buff[LINE_LEN];
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+ while (fgets(buff, sizeof buff, fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ 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 */
+ }
+ }
+ CloseSecret(fp);
+ }
+ *phone = '\0';
+ return 0;
+}
+
+int
+auth_Select(struct bundle *bundle, const char *name)
+{
+ FILE *fp;
+ int n;
+ char *vector[5];
+ char buff[LINE_LEN];
+
+ 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) {
+ /* 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) {
+ while (fgets(buff, sizeof buff, fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = '\0';
+ memset(vector, '\0', sizeof vector);
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ 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 */
+ }
+ }
+ 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;
+ char *vector[5];
+ char buff[LINE_LEN];
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp != NULL) {
+ while (fgets(buff, sizeof buff, fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof vector);
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], name) == 0) {
+ CloseSecret(fp);
+ return auth_CheckPasswd(name, vector[1], key);
+ }
+ }
+ 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;
+ char *vector[5];
+ static char buff[LINE_LEN];
+
+ fp = OpenSecret(SECRETFILE);
+ if (fp == NULL)
+ return (NULL);
+
+ while (fgets(buff, sizeof buff, fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof vector);
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ if (n < 2)
+ continue;
+ if (strlen(vector[0]) == len && strncmp(vector[0], name, len) == 0) {
+ CloseSecret(fp);
+ return vector[1];
+ }
+ }
+ 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 = mbuf_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);
+ }
+
+ mbuf_Free(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 = mbuf_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';
+ mbuf_Free(bp);
+ return NULL;
+}
diff --git a/usr.sbin/ppp/auth.h b/usr.sbin/ppp/auth.h
new file mode 100644
index 0000000..a4224ba
--- /dev/null
+++ b/usr.sbin/ppp/auth.h
@@ -0,0 +1,62 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: auth.h,v 1.16 1999/02/18 00:52:12 brian Exp $
+ *
+ * TODO:
+ */
+
+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..efab874
--- /dev/null
+++ b/usr.sbin/ppp/bundle.c
@@ -0,0 +1,1786 @@
+/*-
+ * 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.
+ *
+ * $Id: bundle.c,v 1.49 1999/03/07 01:41:40 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_tun.h>
+#include <arpa/inet.h>
+#include <net/route.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.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/ioctl.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NOALIAS
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+#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 "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 "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "modem.h"
+#include "auth.h"
+#include "lcpproto.h"
+#include "chap.h"
+#include "tun.h"
+#include "prompt.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "ip.h"
+#include "iface.h"
+
+#define SCATTER_SEGMENTS 4 /* version, datalink, name, physical */
+#define SOCKET_OVERHEAD 100 /* additional buffer space for large */
+ /* {recv,send}msg() calls */
+
+static int bundle_RemainingIdleTime(struct bundle *);
+static int bundle_RemainingAutoLoadTime(struct bundle *);
+
+static const char *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:
+ log_DisplayPrompts();
+ bundle->phase = new;
+ break;
+
+ case PHASE_ESTABLISH:
+ bundle->phase = new;
+ break;
+
+ case PHASE_AUTHENTICATE:
+ bundle->phase = new;
+ log_DisplayPrompts();
+ break;
+
+ case PHASE_NETWORK:
+ fsm_Up(&bundle->ncp.ipcp.fsm);
+ fsm_Open(&bundle->ncp.ipcp.fsm);
+ bundle->phase = new;
+ log_DisplayPrompts();
+ 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 ! */
+}
+
+
+static void
+bundle_Notify(struct bundle *bundle, char c)
+{
+ if (bundle->notify.fd != -1) {
+ if (write(bundle->notify.fd, &c, 1) == 1)
+ log_Printf(LogPHASE, "Parent notified of success.\n");
+ else
+ log_Printf(LogPHASE, "Failed to notify parent of success.\n");
+ close(bundle->notify.fd);
+ bundle->notify.fd = -1;
+ }
+}
+
+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).
+ */
+
+ ip_DeleteQueue(&bundle->ncp.ipcp);
+ mp_DeleteQueue(&bundle->ncp.mp);
+ for (dl = bundle->links; dl; dl = dl->next)
+ physical_DeleteQueue(dl->physical);
+}
+
+static void
+bundle_AutoLoadTimeout(void *v)
+{
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (bundle->autoload.comingup) {
+ log_Printf(LogPHASE, "autoload: Another link is required\n");
+ /* bundle_Open() stops the timer */
+ bundle_Open(bundle, NULL, PHYS_AUTO, 0);
+ } else {
+ struct datalink *dl, *last;
+
+ timer_Stop(&bundle->autoload.timer);
+ for (last = NULL, dl = bundle->links; dl; dl = dl->next)
+ if (dl->physical->type == PHYS_AUTO && dl->state == DATALINK_OPEN)
+ last = dl;
+
+ if (last)
+ datalink_Close(last, CLOSE_STAYDOWN);
+ }
+}
+
+static void
+bundle_StartAutoLoadTimer(struct bundle *bundle, int up)
+{
+ struct datalink *dl;
+
+ timer_Stop(&bundle->autoload.timer);
+ bundle->autoload.comingup = up ? 1 : 0;
+
+ if (bundle->CleaningUp || bundle->phase != PHASE_NETWORK) {
+ dl = NULL;
+ bundle->autoload.running = 0;
+ } else if (up) {
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_CLOSED && dl->physical->type == PHYS_AUTO) {
+ if (bundle->cfg.autoload.max.timeout) {
+ bundle->autoload.timer.func = bundle_AutoLoadTimeout;
+ bundle->autoload.timer.name = "autoload up";
+ bundle->autoload.timer.load =
+ bundle->cfg.autoload.max.timeout * SECTICKS;
+ bundle->autoload.timer.arg = bundle;
+ timer_Start(&bundle->autoload.timer);
+ bundle->autoload.done = time(NULL) + bundle->cfg.autoload.max.timeout;
+ } else
+ bundle_AutoLoadTimeout(bundle);
+ break;
+ }
+ bundle->autoload.running = (dl || bundle->cfg.autoload.min.timeout) ? 1 : 0;
+ } else {
+ int nlinks;
+ struct datalink *adl;
+
+ for (nlinks = 0, adl = NULL, dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN) {
+ if (dl->physical->type == PHYS_AUTO)
+ adl = dl;
+ if (++nlinks > 1 && adl) {
+ if (bundle->cfg.autoload.min.timeout) {
+ bundle->autoload.timer.func = bundle_AutoLoadTimeout;
+ bundle->autoload.timer.name = "autoload down";
+ bundle->autoload.timer.load =
+ bundle->cfg.autoload.min.timeout * SECTICKS;
+ bundle->autoload.timer.arg = bundle;
+ timer_Start(&bundle->autoload.timer);
+ bundle->autoload.done =
+ time(NULL) + bundle->cfg.autoload.min.timeout;
+ }
+ break;
+ }
+ }
+
+ bundle->autoload.running = 1;
+ }
+}
+
+static void
+bundle_StopAutoLoadTimer(struct bundle *bundle)
+{
+ timer_Stop(&bundle->autoload.timer);
+ bundle->autoload.done = 0;
+}
+
+static int
+bundle_RemainingAutoLoadTime(struct bundle *bundle)
+{
+ if (bundle->autoload.done)
+ return bundle->autoload.done - time(NULL);
+ return -1;
+}
+
+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;
+
+ /* Note: We only re-add links that are DATALINK_OPEN */
+ if (dl->physical->type == PHYS_AUTO &&
+ bundle->autoload.timer.state == TIMER_STOPPED &&
+ dl->state != DATALINK_OPEN &&
+ bundle->phase == PHASE_NETWORK)
+ bundle->autoload.running = 1;
+
+ 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);
+}
+
+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);
+
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL))
+ == bundle->phys_type.open)
+ 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.
+ * If it's an LCP set our mtu (if we're multilink, add up the link
+ * speeds and set the MRRU) and start our autoload timer.
+ * If it's an NCP, tell our -background parent to go away.
+ * If it's the first NCP, start the idle timer.
+ */
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (fp->proto == PROTO_LCP) {
+ struct physical *p = link2physical(fp->link);
+
+ bundle_LinkAdded(bundle, p->dl);
+ if (bundle->ncp.mp.active) {
+ struct datalink *dl;
+
+ bundle->ifSpeed = 0;
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state == DATALINK_OPEN)
+ bundle->ifSpeed += modem_Speed(dl->physical);
+ tun_configure(bundle, bundle->ncp.mp.peer_mrru);
+ bundle->autoload.running = 1;
+ } else {
+ bundle->ifSpeed = modem_Speed(p);
+ tun_configure(bundle, fsm2lcp(fp)->his_mru);
+ }
+ } else if (fp->proto == PROTO_IPCP) {
+ bundle_StartIdleTimer(bundle);
+ bundle_Notify(bundle, EX_NORMAL);
+ }
+}
+
+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 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
+ * speed and make sure our minimum sequence number is adjusted.
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+
+ if (fp->proto == PROTO_IPCP)
+ bundle_StopIdleTimer(bundle);
+ else if (fp->proto == PROTO_LCP) {
+ bundle_LinksRemoved(bundle); /* adjust timers & phys_type values */
+ if (bundle->ncp.mp.active) {
+ struct datalink *dl;
+ struct datalink *lost;
+
+ bundle->ifSpeed = 0;
+ lost = NULL;
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (fp == &dl->physical->link.lcp.fsm)
+ lost = dl;
+ else if (dl->state == DATALINK_OPEN)
+ bundle->ifSpeed += modem_Speed(dl->physical);
+
+ if (bundle->ifSpeed)
+ /* Don't configure down to a speed of 0 */
+ tun_configure(bundle, bundle->ncp.mp.link.lcp.his_mru);
+
+ if (lost)
+ mp_LinkLost(&bundle->ncp.mp, lost);
+ else
+ log_Printf(LogALERT, "Oops, lost an unrecognised datalink (%s) !\n",
+ fp->link->name);
+ }
+ }
+}
+
+static void
+bundle_LayerFinish(void *v, struct fsm *fp)
+{
+ /* The given fsm is now down (fp cannot be NULL)
+ *
+ * If it's the last LCP, fsm_Down all NCPs
+ * If it's the last NCP, fsm_Close all LCPs
+ */
+
+ struct bundle *bundle = (struct bundle *)v;
+ struct datalink *dl;
+
+ if (fp->proto == PROTO_IPCP) {
+ if (bundle_Phase(bundle) != PHASE_DEAD)
+ bundle_NewPhase(bundle, PHASE_TERMINATE);
+ for (dl = bundle->links; dl; dl = dl->next)
+ datalink_Close(dl, CLOSE_NORMAL);
+ fsm2initial(fp);
+ } else if (fp->proto == PROTO_LCP) {
+ int others_active;
+
+ others_active = 0;
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (fp != &dl->physical->link.lcp.fsm &&
+ dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP)
+ others_active++;
+
+ if (!others_active)
+ fsm2initial(&bundle->ncp.ipcp.fsm);
+ }
+}
+
+int
+bundle_LinkIsUp(const struct bundle *bundle)
+{
+ return bundle->ncp.ipcp.fsm.state == ST_OPENED;
+}
+
+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);
+ /* fall through */
+ 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) {
+ bundle_StopIdleTimer(bundle);
+ bundle_StopAutoLoadTimer(bundle);
+ if (bundle->ncp.ipcp.fsm.state > ST_CLOSED ||
+ bundle->ncp.ipcp.fsm.state == ST_STARTING)
+ fsm_Close(&bundle->ncp.ipcp.fsm);
+ else {
+ fsm2initial(&bundle->ncp.ipcp.fsm);
+ 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 descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ struct bundle *bundle = descriptor2bundle(d);
+ struct datalink *dl;
+ int result, want, queued, nlinks;
+
+ 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 ? bundle_FillQueues(bundle) : ip_QueueLen(&bundle->ncp.ipcp);
+ if (bundle->autoload.running) {
+ if (queued < bundle->cfg.autoload.max.packets) {
+ if (queued > bundle->cfg.autoload.min.packets)
+ bundle_StopAutoLoadTimer(bundle);
+ else if (bundle->autoload.timer.state != TIMER_RUNNING ||
+ bundle->autoload.comingup)
+ bundle_StartAutoLoadTimer(bundle, 0);
+ } else if ((bundle_Phase(bundle) == PHASE_NETWORK || queued) &&
+ (bundle->autoload.timer.state != TIMER_RUNNING ||
+ !bundle->autoload.comingup))
+ bundle_StartAutoLoadTimer(bundle, 1);
+ }
+
+ if (r && (bundle->phase == PHASE_NETWORK ||
+ bundle->phys_type.all & PHYS_AUTO)) {
+ /* enough surplus so that we can tell if we're getting swamped */
+ want = bundle->cfg.autoload.max.packets + nlinks * 2;
+ /* but at least 20 packets ! */
+ if (want < 20)
+ want = 20;
+ if (queued < want) {
+ /* 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 descriptor *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 descriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct datalink *dl;
+
+ 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;
+
+ /* something to read from tun */
+ n = read(bundle->dev.fd, &tun, sizeof tun);
+ if (n < 0) {
+ log_Printf(LogWARN, "read from %s: %s\n", TUN_NAME, strerror(errno));
+ return;
+ }
+ n -= sizeof tun - sizeof tun.data;
+ if (n <= 0) {
+ log_Printf(LogERROR, "read from %s: Only %d bytes read ?\n", TUN_NAME, n);
+ return;
+ }
+ if (!tun_check_header(tun, AF_INET))
+ return;
+
+ if (((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, tun.data, n, &bundle->filter.in);
+ if (pri >= 0) {
+ struct mbuf *bp;
+
+ bp = mbuf_Alloc(n, MB_IPIN);
+ memcpy(MBUF_CTOP(bp), tun.data, n);
+ ip_Input(bundle, bp);
+ 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 tunnel
+ * device until IPCP 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
+ */
+ if ((pri = PacketCheck(bundle, tun.data, n, &bundle->filter.dial)) >= 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;
+ }
+
+ pri = PacketCheck(bundle, tun.data, n, &bundle->filter.out);
+ if (pri >= 0) {
+#ifndef NOALIAS
+ if (bundle->AliasEnabled) {
+ PacketAliasOut(tun.data, sizeof tun.data);
+ n = ntohs(((struct ip *)tun.data)->ip_len);
+ }
+#endif
+ ip_Enqueue(&bundle->ncp.ipcp, pri, tun.data, n);
+ }
+ }
+}
+
+static int
+bundle_DescriptorWrite(struct descriptor *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))
+ descriptor_Write(&bundle->ncp.mp.server.desc, bundle, fdset);
+
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (descriptor_IsSet(&dl->desc, fdset))
+ result += descriptor_Write(&dl->desc, bundle, fdset);
+
+ return result;
+}
+
+void
+bundle_LockTun(struct bundle *bundle)
+{
+ FILE *lockfile;
+ char pidfile[MAXPATHLEN];
+
+ 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[MAXPATHLEN];
+
+ snprintf(pidfile, sizeof pidfile, "%stun%d.pid", _PATH_VARRUN, bundle->unit);
+ ID0unlink(pidfile);
+}
+
+struct bundle *
+bundle_Create(const char *prefix, int type, const char **argv)
+{
+ int s, enoentcount, err;
+ const char *ifname;
+ struct ifreq ifrq;
+ static struct bundle bundle; /* there can be only one */
+#ifdef TUNSIFMODE
+ int iff;
+#endif
+
+ if (bundle.iface != NULL) { /* Already allocated ! */
+ log_Printf(LogALERT, "bundle_Create: There's only one BUNDLE !\n");
+ return NULL;
+ }
+
+ err = ENOENT;
+ enoentcount = 0;
+ for (bundle.unit = 0; ; 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) {
+ err = errno;
+ break;
+ } else if (errno == ENOENT) {
+ if (++enoentcount > 2)
+ break;
+ } else
+ err = errno;
+ }
+
+ if (bundle.dev.fd < 0) {
+ log_Printf(LogWARN, "No available tunnel devices found (%s).\n",
+ strerror(err));
+ return NULL;
+ }
+
+ log_SetTun(bundle.unit);
+ bundle.argv = argv;
+ bundle.argv0 = argv[0];
+ bundle.argv1 = argv[1];
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "bundle_Create: socket(): %s\n", strerror(errno));
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+ ifname = strrchr(bundle.dev.Name, '/');
+ if (ifname == NULL)
+ ifname = bundle.dev.Name;
+ else
+ ifname++;
+
+ bundle.iface = iface_Create(ifname);
+ if (bundle.iface == NULL) {
+ close(s);
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+#ifdef TUNSIFMODE
+ /* Make sure we're POINTOPOINT */
+ iff = IFF_POINTOPOINT;
+ if (ID0ioctl(bundle.dev.fd, TUNSIFMODE, &iff) < 0)
+ log_Printf(LogERROR, "bundle_Create: ioctl(TUNSIFMODE): %s\n",
+ strerror(errno));
+#endif
+
+ /*
+ * Now, bring up the interface.
+ */
+ 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, "bundle_Create: ioctl(SIOCGIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ iface_Destroy(bundle.iface);
+ bundle.iface = NULL;
+ close(bundle.dev.fd);
+ return NULL;
+ }
+ ifrq.ifr_flags |= IFF_UP;
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "bundle_Create: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ iface_Destroy(bundle.iface);
+ bundle.iface = NULL;
+ close(bundle.dev.fd);
+ return NULL;
+ }
+
+ close(s);
+
+ log_Printf(LogPHASE, "Using interface: %s\n", ifname);
+
+ bundle.ifSpeed = 0;
+
+ bundle.routing_seq = 0;
+ bundle.phase = PHASE_DEAD;
+ bundle.CleaningUp = 0;
+ bundle.AliasEnabled = 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.auth.name = '\0';
+ *bundle.cfg.auth.key = '\0';
+ bundle.cfg.opt = OPT_SROUTES | OPT_IDCHECK | OPT_LOOPBACK |
+ OPT_THROUGHPUT | OPT_UTMP;
+ *bundle.cfg.label = '\0';
+ bundle.cfg.mtu = DEF_MTU;
+ bundle.cfg.autoload.max.packets = 0;
+ bundle.cfg.autoload.max.timeout = 0;
+ bundle.cfg.autoload.min.packets = 0;
+ bundle.cfg.autoload.min.timeout = 0;
+ bundle.cfg.choked.timeout = CHOKED_TIMEOUT;
+ bundle.phys_type.all = type;
+ bundle.phys_type.open = 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;
+
+ mp_Init(&bundle.ncp.mp, &bundle);
+
+ /* Send over the first physical link by default */
+ ipcp_Init(&bundle.ncp.ipcp, &bundle, &bundle.links->physical->link,
+ &bundle.fsm);
+
+ 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;
+ memset(&bundle.idle.timer, '\0', sizeof bundle.idle.timer);
+ bundle.idle.done = 0;
+ bundle.notify.fd = -1;
+ memset(&bundle.autoload.timer, '\0', sizeof bundle.autoload.timer);
+ bundle.autoload.done = 0;
+ bundle.autoload.running = 0;
+ 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, IFACE_CLEAR_ALL);
+
+ bundle_LockTun(&bundle);
+
+ return &bundle;
+}
+
+static void
+bundle_DownInterface(struct bundle *bundle)
+{
+ struct ifreq ifrq;
+ int s;
+
+ route_IfDelete(bundle, 1);
+
+ s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "bundle_DownInterface: socket: %s\n", strerror(errno));
+ return;
+ }
+
+ memset(&ifrq, '\0', sizeof ifrq);
+ strncpy(ifrq.ifr_name, bundle->iface->name, sizeof ifrq.ifr_name - 1);
+ ifrq.ifr_name[sizeof ifrq.ifr_name - 1] = '\0';
+ if (ID0ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "bundle_DownInterface: ioctl(SIOCGIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return;
+ }
+ ifrq.ifr_flags &= ~IFF_UP;
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ log_Printf(LogERROR, "bundle_DownInterface: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return;
+ }
+ close(s);
+}
+
+void
+bundle_Destroy(struct bundle *bundle)
+{
+ struct datalink *dl;
+
+ /*
+ * Clean up the interface. We don't need to timer_Stop()s, mp_Down(),
+ * ipcp_CleanInterface() 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);
+ timer_Stop(&bundle->autoload.timer);
+ mp_Down(&bundle->ncp.mp);
+ ipcp_CleanInterface(&bundle->ncp.ipcp);
+ 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);
+
+ 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;
+}
+
+struct rtmsg {
+ struct rt_msghdr m_rtm;
+ char m_space[64];
+};
+
+int
+bundle_SetRoute(struct bundle *bundle, int cmd, struct in_addr dst,
+ struct in_addr gateway, struct in_addr mask, int bang, int ssh)
+{
+ struct rtmsg rtmes;
+ int s, nb, wb;
+ char *cp;
+ const char *cmdstr;
+ struct sockaddr_in rtdata;
+ 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, "bundle_SetRoute: 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 || cmd == RTM_CHANGE) {
+ if (bundle->ncp.ipcp.cfg.sendpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_sendpipe = bundle->ncp.ipcp.cfg.sendpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_SPIPE;
+ }
+ if (bundle->ncp.ipcp.cfg.recvpipe > 0) {
+ rtmes.m_rtm.rtm_rmx.rmx_recvpipe = bundle->ncp.ipcp.cfg.recvpipe;
+ rtmes.m_rtm.rtm_inits |= RTV_RPIPE;
+ }
+ }
+
+ memset(&rtdata, '\0', sizeof rtdata);
+ rtdata.sin_len = sizeof rtdata;
+ rtdata.sin_family = AF_INET;
+ rtdata.sin_port = 0;
+ rtdata.sin_addr = dst;
+
+ cp = rtmes.m_space;
+ memcpy(cp, &rtdata, rtdata.sin_len);
+ cp += rtdata.sin_len;
+ if (cmd == RTM_ADD) {
+ if (gateway.s_addr == INADDR_ANY) {
+ if (!ssh)
+ log_Printf(LogERROR, "bundle_SetRoute: Cannot add a route with"
+ " destination 0.0.0.0\n");
+ close(s);
+ return result;
+ } else {
+ rtdata.sin_addr = gateway;
+ memcpy(cp, &rtdata, rtdata.sin_len);
+ cp += rtdata.sin_len;
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ }
+ }
+
+ if (dst.s_addr == INADDR_ANY)
+ mask.s_addr = INADDR_ANY;
+
+ if (cmd == RTM_ADD || dst.s_addr == INADDR_ANY) {
+ rtdata.sin_addr = mask;
+ memcpy(cp, &rtdata, rtdata.sin_len);
+ cp += rtdata.sin_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, "bundle_SetRoute failure:\n");
+ log_Printf(LogTCPIP, "bundle_SetRoute: Cmd = %s\n", cmdstr);
+ log_Printf(LogTCPIP, "bundle_SetRoute: Dst = %s\n", inet_ntoa(dst));
+ log_Printf(LogTCPIP, "bundle_SetRoute: Gateway = %s\n", inet_ntoa(gateway));
+ log_Printf(LogTCPIP, "bundle_SetRoute: Mask = %s\n", inet_ntoa(mask));
+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",
+ dst.s_addr == 0 ? "default" : inet_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",
+ inet_ntoa(dst));
+ } else if (rtmes.m_rtm.rtm_errno == 0) {
+ if (!ssh || errno != ENETUNREACH)
+ log_Printf(LogWARN, "%s route failed: %s: errno: %s\n", cmdstr,
+ inet_ntoa(dst), strerror(errno));
+ } else
+ log_Printf(LogWARN, "%s route failed: %s: %s\n",
+ cmdstr, inet_ntoa(dst), strerror(rtmes.m_rtm.rtm_errno));
+ }
+ log_Printf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
+ wb, cmdstr, (unsigned)dst.s_addr, (unsigned)gateway.s_addr);
+ close(s);
+
+ return result;
+}
+
+void
+bundle_LinkClosed(struct bundle *bundle, struct datalink *dl)
+{
+ /*
+ * Our datalink has closed.
+ * CleanDatalinks() (called from DoLoop()) will remove closed
+ * BACKGROUND 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);
+ fsm2initial(&bundle->ncp.ipcp.fsm);
+ bundle_NewPhase(bundle, PHASE_DEAD);
+ bundle_StopIdleTimer(bundle);
+ bundle_StopAutoLoadTimer(bundle);
+ bundle->autoload.running = 0;
+ } else
+ bundle->autoload.running = 1;
+}
+
+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;
+
+ timer_Stop(&bundle->autoload.timer);
+ 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))) {
+ if (force) /* Ignore redial timeout ? */
+ timer_Stop(&dl->dial.timer);
+ datalink_Up(dl, 1, 1);
+ if (mask == PHYS_AUTO)
+ /* Only one AUTO link at a time (see the AutoLoad timer) */
+ break;
+ }
+ 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_FillQueues(struct bundle *bundle)
+{
+ int total;
+
+ if (bundle->ncp.mp.active)
+ total = mp_FillQueues(bundle);
+ else {
+ struct datalink *dl;
+ int 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 = ip_FlushPacket(&dl->physical->link, bundle);
+ total += add;
+ }
+ }
+
+ return total + ip_QueueLen(&bundle->ncp.ipcp);
+}
+
+int
+bundle_ShowLinks(struct cmdargs const *arg)
+{
+ struct datalink *dl;
+
+ for (dl = arg->bundle->links; dl; dl = dl->next) {
+ prompt_Printf(arg->prompt, "Name: %s [%s, %s]",
+ dl->name, mode2Nam(dl->physical->type), datalink_State(dl));
+ if (dl->physical->link.throughput.rolling && dl->state == DATALINK_OPEN)
+ prompt_Printf(arg->prompt, " weight %d, %d bytes/sec",
+ dl->mp.weight,
+ dl->physical->link.throughput.OctetsPerSecond);
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ 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, " Title: %s\n", arg->bundle->argv[0]);
+ prompt_Printf(arg->prompt, " Device: %s\n", arg->bundle->dev.Name);
+ prompt_Printf(arg->prompt, " Interface: %s @ %lubps\n",
+ arg->bundle->iface->name, arg->bundle->ifSpeed);
+
+ 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, " Auto Load: Up after %ds of >= %d packets\n",
+ arg->bundle->cfg.autoload.max.timeout,
+ arg->bundle->cfg.autoload.max.packets);
+ prompt_Printf(arg->prompt, " Down after %ds of <= %d"
+ " packets\n", arg->bundle->cfg.autoload.min.timeout,
+ arg->bundle->cfg.autoload.min.packets);
+ if (arg->bundle->autoload.timer.state == TIMER_RUNNING)
+ prompt_Printf(arg->prompt, " %ds remaining 'till "
+ "a link comes %s\n",
+ bundle_RemainingAutoLoadTime(arg->bundle),
+ arg->bundle->autoload.comingup ? "up" : "down");
+ else
+ prompt_Printf(arg->prompt, " %srunning with %d"
+ " packets queued\n", arg->bundle->autoload.running ?
+ "" : "not ", ip_QueueLen(&arg->bundle->ncp.ipcp));
+
+ 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);
+ 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, " MTU: ");
+ if (arg->bundle->cfg.mtu)
+ prompt_Printf(arg->prompt, "%d\n", arg->bundle->cfg.mtu);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+
+ prompt_Printf(arg->prompt, " sendpipe: ");
+ if (arg->bundle->ncp.ipcp.cfg.sendpipe > 0)
+ prompt_Printf(arg->prompt, "%ld\n", arg->bundle->ncp.ipcp.cfg.sendpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+ prompt_Printf(arg->prompt, " recvpipe: ");
+ if (arg->bundle->ncp.ipcp.cfg.recvpipe > 0)
+ prompt_Printf(arg->prompt, "%ld\n", arg->bundle->ncp.ipcp.cfg.recvpipe);
+ else
+ prompt_Printf(arg->prompt, "unspecified\n");
+
+ prompt_Printf(arg->prompt, " Sticky Routes: %s\n",
+ optval(arg->bundle, OPT_SROUTES));
+ prompt_Printf(arg->prompt, " ID check: %s\n",
+ optval(arg->bundle, OPT_IDCHECK));
+ prompt_Printf(arg->prompt, " Loopback: %s\n",
+ optval(arg->bundle, OPT_LOOPBACK));
+ prompt_Printf(arg->prompt, " PasswdAuth: %s\n",
+ optval(arg->bundle, OPT_PASSWDAUTH));
+ prompt_Printf(arg->prompt, " Proxy: %s\n",
+ optval(arg->bundle, OPT_PROXY));
+ prompt_Printf(arg->prompt, " Proxyall: %s\n",
+ optval(arg->bundle, OPT_PROXYALL));
+ prompt_Printf(arg->prompt, " Throughput: %s\n",
+ optval(arg->bundle, OPT_THROUGHPUT));
+ prompt_Printf(arg->prompt, " Utmp Logging: %s\n",
+ optval(arg->bundle, OPT_UTMP));
+ prompt_Printf(arg->prompt, " Iface-Alias: %s\n",
+ optval(arg->bundle, OPT_IFACEALIAS));
+
+ 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)
+{
+ timer_Stop(&bundle->idle.timer);
+ if ((bundle->phys_type.open & (PHYS_DEDICATED|PHYS_DDIAL)) !=
+ bundle->phys_type.open && bundle->cfg.idle_timeout) {
+ bundle->idle.timer.func = bundle_IdleTimeout;
+ bundle->idle.timer.name = "idle";
+ bundle->idle.timer.load = bundle->cfg.idle_timeout * SECTICKS;
+ bundle->idle.timer.arg = bundle;
+ timer_Start(&bundle->idle.timer);
+ bundle->idle.done = time(NULL) + bundle->cfg.idle_timeout;
+ }
+}
+
+void
+bundle_SetIdleTimer(struct bundle *bundle, int value)
+{
+ bundle->cfg.idle_timeout = value;
+ if (bundle_LinkIsUp(bundle))
+ bundle_StartIdleTimer(bundle);
+}
+
+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;
+}
+
+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);
+}
+
+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)) {
+ *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;
+}
+
+void
+bundle_ReceiveDatalink(struct bundle *bundle, int s, struct sockaddr_un *sun)
+{
+ char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int)];
+ struct cmsghdr *cmsg = (struct cmsghdr *)cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov[SCATTER_SEGMENTS];
+ struct datalink *dl;
+ int niov, link_fd, expect, f;
+ pid_t pid;
+
+ log_Printf(LogPHASE, "Receiving datalink\n");
+
+ /* Create our scatter/gather array */
+ niov = 1;
+ iov[0].iov_len = strlen(Version) + 1;
+ iov[0].iov_base = (char *)malloc(iov[0].iov_len);
+ if (datalink2iov(NULL, iov, &niov, sizeof iov / sizeof *iov, 0) == -1) {
+ close(s);
+ return;
+ }
+
+ pid = getpid();
+ write(s, &pid, sizeof pid);
+
+ for (f = expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ /* Set up our message */
+ cmsg->cmsg_len = sizeof cmsgbuf;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = 0;
+
+ memset(&msg, '\0', sizeof msg);
+ msg.msg_name = (caddr_t)sun;
+ msg.msg_namelen = sizeof *sun;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = niov;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof cmsgbuf;
+
+ log_Printf(LogDEBUG, "Expecting %d scatter/gather bytes\n", expect);
+ f = expect + 100;
+ setsockopt(s, SOL_SOCKET, SO_RCVBUF, &f, sizeof f);
+ if ((f = recvmsg(s, &msg, MSG_WAITALL)) != expect) {
+ if (f == -1)
+ log_Printf(LogERROR, "Failed recvmsg: %s\n", strerror(errno));
+ else
+ log_Printf(LogERROR, "Failed recvmsg: Got %d, not %d\n", f, expect);
+ while (niov--)
+ free(iov[niov].iov_base);
+ close(s);
+ return;
+ }
+
+ write(s, "!", 1); /* ACK */
+ close(s);
+
+ if (cmsg->cmsg_type != SCM_RIGHTS) {
+ log_Printf(LogERROR, "Recvmsg: no descriptor received !\n");
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ /* We've successfully received an open file descriptor through our socket */
+ log_Printf(LogDEBUG, "Receiving device descriptor\n");
+ link_fd = *(int *)CMSG_DATA(cmsg);
+
+ 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);
+ close(link_fd);
+ while (niov--)
+ free(iov[niov].iov_base);
+ return;
+ }
+
+ niov = 1;
+ dl = iov2datalink(bundle, iov, &niov, sizeof iov / sizeof *iov, link_fd);
+ if (dl) {
+ bundle_DatalinkLinkin(bundle, dl);
+ datalink_AuthOk(dl);
+ } else
+ close(link_fd);
+
+ free(iov[0].iov_base);
+}
+
+void
+bundle_SendDatalink(struct datalink *dl, int s, struct sockaddr_un *sun)
+{
+ char cmsgbuf[sizeof(struct cmsghdr) + sizeof(int)], ack;
+ struct cmsghdr *cmsg = (struct cmsghdr *)cmsgbuf;
+ struct msghdr msg;
+ struct iovec iov[SCATTER_SEGMENTS];
+ int niov, link_fd, f, expect, newsid;
+ pid_t newpid;
+
+ log_Printf(LogPHASE, "Transmitting datalink %s\n", dl->name);
+
+ 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;
+
+ read(s, &newpid, sizeof newpid);
+ link_fd = datalink2iov(dl, iov, &niov, sizeof iov / sizeof *iov, newpid);
+
+ if (link_fd != -1) {
+ memset(&msg, '\0', sizeof msg);
+
+ msg.msg_name = (caddr_t)sun;
+ msg.msg_namelen = sizeof *sun;
+ msg.msg_iov = iov;
+ msg.msg_iovlen = niov;
+
+ cmsg->cmsg_len = sizeof cmsgbuf;
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ *(int *)CMSG_DATA(cmsg) = link_fd;
+ msg.msg_control = cmsgbuf;
+ msg.msg_controllen = sizeof cmsgbuf;
+
+ for (f = expect = 0; f < niov; f++)
+ expect += iov[f].iov_len;
+
+ log_Printf(LogDEBUG, "Sending %d bytes in scatter/gather array\n", expect);
+
+ f = expect + SOCKET_OVERHEAD;
+ setsockopt(s, SOL_SOCKET, SO_SNDBUF, &f, sizeof f);
+ if (sendmsg(s, &msg, 0) == -1)
+ log_Printf(LogERROR, "Failed sendmsg: %s\n", strerror(errno));
+ /* We must get the ACK before closing the descriptor ! */
+ read(s, &ack, 1);
+
+ newsid = tcgetpgrp(link_fd) == getpgrp();
+ close(link_fd);
+ if (newsid)
+ bundle_setsid(dl->bundle, 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 autoload & idle timers */
+ 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;
+
+ 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();
+ log_Printf(LogPHASE, "%d -> %d: %s session control\n",
+ (int)orig, (int)getpid(),
+ holdsession ? "Passed" : "Dropped");
+ timer_InitService(0); /* Start the Timer Service */
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our modem locks (to the final process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ modem_ChangedPid(dl->physical, pid);
+ write(fds[1], "!", 1); /* done */
+ close(fds[1]);
+ exit(0);
+ break;
+ }
+ break;
+ default:
+ close(fds[0]);
+ /* Give away all our modem locks (to the intermediate process) */
+ for (dl = bundle->links; dl; dl = dl->next)
+ if (dl->state != DATALINK_CLOSED)
+ modem_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);
+ setuid(geteuid());
+ /*
+ * 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.... */
+ bundle->argv[0] = "session owner";
+ bundle->argv[1] = NULL;
+ /*
+ * 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;
+}
diff --git a/usr.sbin/ppp/bundle.h b/usr.sbin/ppp/bundle.h
new file mode 100644
index 0000000..5e2866f
--- /dev/null
+++ b/usr.sbin/ppp/bundle.h
@@ -0,0 +1,191 @@
+/*-
+ * 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.
+ *
+ * $Id: bundle.h,v 1.20 1998/12/10 18:36:30 brian Exp $
+ */
+
+#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_IDCHECK 0x0001
+#define OPT_IFACEALIAS 0x0002
+#define OPT_LOOPBACK 0x0004
+#define OPT_PASSWDAUTH 0x0008
+#define OPT_PROXY 0x0010
+#define OPT_PROXYALL 0x0020
+#define OPT_SROUTES 0x0040
+#define OPT_THROUGHPUT 0x0080
+#define OPT_UTMP 0x0100
+
+#define MAX_ENDDISC_CLASS 5
+
+#define Enabled(b, o) ((b)->cfg.opt & (o))
+
+struct sockaddr_un;
+struct datalink;
+struct physical;
+struct link;
+struct server;
+struct prompt;
+struct iface;
+
+struct bundle {
+ struct descriptor desc; /* really all our datalinks */
+ int unit; /* The device/interface unit number */
+ const char **argv; /* From main() */
+ const char *argv0; /* Original */
+ const char *argv1; /* Original */
+
+ struct {
+ char Name[20]; /* The /dev/XXXX name */
+ int fd; /* The /dev/XXXX descriptor */
+ } dev;
+
+ u_long ifSpeed; /* 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 AliasEnabled : 1; /* Are we using libalias ? */
+
+ struct fsm_parent fsm; /* Our callback functions */
+ struct datalink *links; /* Our data links */
+
+ struct {
+ int idle_timeout; /* NCP Idle timeout value */
+ 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 mtu; /* Interface mtu */
+
+ struct { /* We need/don't need another link when */
+ struct { /* more/less than */
+ int packets; /* this number of packets are queued for */
+ int timeout; /* this number of seconds */
+ } max, min;
+ } autoload;
+
+ struct {
+ int timeout; /* How long to leave the output queue choked */
+ } choked;
+ } cfg;
+
+ struct {
+ struct ipcp ipcp; /* Our IPCP FSM */
+ struct mp mp; /* Our MP */
+ } 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;
+
+ struct {
+ int fd; /* write status here */
+ } notify;
+
+ struct {
+ struct pppTimer timer;
+ time_t done;
+ unsigned running : 1;
+ unsigned comingup : 1;
+ } autoload;
+
+ struct {
+ struct pppTimer timer; /* choked output queue timer */
+ } choked;
+
+#ifndef NORADIUS
+ struct radius radius; /* Info retrieved from radius server */
+#endif
+};
+
+#define descriptor2bundle(d) \
+ ((d)->type == BUNDLE_DESCRIPTOR ? (struct bundle *)(d) : NULL)
+
+extern struct bundle *bundle_Create(const char *, int, const char **);
+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 int bundle_LinkIsUp(const struct bundle *);
+extern int bundle_SetRoute(struct bundle *, int, struct in_addr,
+ struct in_addr, struct in_addr, int, int);
+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_FillQueues(struct bundle *);
+extern int bundle_ShowLinks(struct cmdargs const *);
+extern int bundle_ShowStatus(struct cmdargs const *);
+extern void bundle_StartIdleTimer(struct bundle *);
+extern void bundle_SetIdleTimer(struct bundle *, int);
+extern void bundle_StopIdleTimer(struct bundle *);
+extern int bundle_IsDead(struct bundle *);
+extern struct datalink *bundle2datalink(struct bundle *, const char *);
+
+extern void bundle_RegisterDescriptor(struct bundle *, struct descriptor *);
+extern void bundle_UnRegisterDescriptor(struct bundle *, struct descriptor *);
+
+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 void bundle_ReceiveDatalink(struct bundle *, int, struct sockaddr_un *);
+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);
diff --git a/usr.sbin/ppp/cbcp.c b/usr.sbin/ppp/cbcp.c
new file mode 100644
index 0000000..a901ef9
--- /dev/null
+++ b/usr.sbin/ppp/cbcp.c
@@ -0,0 +1,729 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: cbcp.c,v 1.10 1999/02/26 21:28:07 brian Exp $
+ */
+
+#include <sys/param.h>
+
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "log.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "mbuf.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "throughput.h"
+#include "hdlc.h"
+#include "ccp.h"
+#include "link.h"
+#include "async.h"
+#include "physical.h"
+#include "lcpproto.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 *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 "???";
+}
+
+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 = mbuf_Alloc(sizeof *head + data->length, MB_CBCP);
+ 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);
+ hdlc_Output(&cbcp->p->link, PRI_LINK, PROTO_CBCP, bp);
+}
+
+static const char *
+cbcp_data_Type(int type)
+{
+ static const char *types[] = {
+ "No callback", "User-spec", "Server-spec", "list"
+ };
+
+ if (type < 1 || type > sizeof types / sizeof types[0])
+ return "???";
+ 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 the callee offers no callback, we send our desired response
+ * anyway. This is what Win95 does - although I can't find this
+ * behaviour documented in the 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 */
+}
+
+void
+cbcp_Input(struct physical *p, struct mbuf *bp)
+{
+ struct cbcp_header *head;
+ struct cbcp_data *data;
+ struct cbcp *cbcp = &p->dl->cbcp;
+ int len;
+
+ bp = mbuf_Contiguous(bp);
+ len = mbuf_Length(bp);
+ if (len < sizeof(struct cbcp_header)) {
+ mbuf_Free(bp);
+ return;
+ }
+ 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);
+ mbuf_Free(bp);
+ return;
+ }
+
+ /* XXX check the id */
+
+ bp->offset += sizeof(struct cbcp_header);
+ bp->cnt -= 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.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;
+ }
+
+ mbuf_Free(bp);
+}
+
+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..d55800a
--- /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.
+ *
+ * $Id$
+ */
+
+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 void cbcp_Input(struct physical *, 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..85d5385
--- /dev/null
+++ b/usr.sbin/ppp/ccp.c
@@ -0,0 +1,638 @@
+/*
+ * PPP Compression Control Protocol (CCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ccp.c,v 1.44 1999/03/11 01:49:15 brian Exp $
+ *
+ * TODO:
+ * o Support other compression protocols
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "pred.h"
+#include "deflate.h"
+#include "throughput.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.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
+#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 *, int, 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 void 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 *ccp_TimerNames[] =
+ {"CCP restart", "CCP openmode", "CCP stopped"};
+
+static char const *cftypes[] = {
+ /* Check out the latest ``Compression Control Protocol'' rfc (rfc1962.txt) */
+ "OUI", /* 0: OUI */
+ "PRED1", /* 1: Predictor type 1 */
+ "PRED2", /* 2: Predictor type 2 */
+ "PUDDLE", /* 3: Puddle Jumber */
+ "???", "???", "???", "???", "???", "???",
+ "???", "???", "???", "???", "???", "???",
+ "HWPPC", /* 16: Hewlett-Packard PPC */
+ "STAC", /* 17: Stac Electronics LZS (rfc1974) */
+ "MPPC", /* 18: Microsoft PPC (rfc2118) */
+ "GAND", /* 19: Gandalf FZA (rfc1993) */
+ "V42BIS", /* 20: ARG->DATA.42bis compression */
+ "BSD", /* 21: BSD LZW Compress */
+ "???",
+ "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) */
+};
+
+#define NCFTYPES (sizeof cftypes/sizeof cftypes[0])
+
+static const char *
+protoname(int proto)
+{
+ if (proto < 0 || proto > NCFTYPES)
+ return "none";
+ return cftypes[proto];
+}
+
+/* We support these algorithms, and Req them in the given order */
+static const struct ccp_algorithm *algorithm[] = {
+ &DeflateAlgorithm,
+ &Pred1Algorithm,
+ &PppdDeflateAlgorithm
+};
+
+#define NALGORITHMS (sizeof algorithm/sizeof algorithm[0])
+
+int
+ccp_ReportStatus(struct cmdargs const *arg)
+{
+ struct link *l;
+ struct ccp *ccp;
+
+ l = command_ChooseLink(arg);
+ ccp = &l->ccp;
+
+ prompt_Printf(arg->prompt, "%s: %s [%s]\n", l->name, ccp->fsm.name,
+ State2Nam(ccp->fsm.state));
+ 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);
+
+ 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);
+ prompt_Printf(arg->prompt, " 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]));
+ 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;
+
+ 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.id = -1;
+ ccp->out.opt = NULL;
+ ccp->his_reject = ccp->my_reject = 0;
+ ccp->uncompout = ccp->compout = 0;
+ ccp->uncompin = ccp->compin = 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)) {
+
+ if (!alloc)
+ for (o = &ccp->out.opt; *o != NULL; o = &(*o)->next)
+ if ((*o)->val.id == algorithm[f]->id && (*o)->algorithm == f)
+ break;
+
+ if (alloc || *o == NULL) {
+ *o = (struct ccp_opt *)malloc(sizeof(struct ccp_opt));
+ (*o)->val.id = algorithm[f]->id;
+ (*o)->val.len = 2;
+ (*o)->next = NULL;
+ (*o)->algorithm = f;
+ (*algorithm[f]->o.OptInit)(&(*o)->val, &ccp->cfg);
+ }
+
+ if (cp + (*o)->val.len > buff + sizeof buff) {
+ log_Printf(LogERROR, "%s: CCP REQ buffer overrun !\n", fp->link->name);
+ break;
+ }
+ memcpy(cp, &(*o)->val, (*o)->val.len);
+ cp += (*o)->val.len;
+
+ ccp->my_proto = (*o)->val.id;
+ ccp->out.algorithm = f;
+
+ if (alloc)
+ o = &(*o)->next;
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, cp - buff);
+}
+
+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);
+}
+
+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);
+}
+
+static void
+CcpRecvResetReq(struct fsm *fp)
+{
+ /* Got a reset REQ, reset outgoing dictionary */
+ struct ccp *ccp = fsm2ccp(fp);
+ if (ccp->out.state != NULL)
+ (*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 */
+ log_Printf(LogCCP, "%s: LayerFinish.\n", fp->link->name);
+}
+
+/*
+ * Called when CCP has reached the OPEN state
+ */
+static int
+CcpLayerUp(struct fsm *fp)
+{
+ /* We're now up */
+ struct ccp *ccp = fsm2ccp(fp);
+
+ 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)(&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;
+ }
+ }
+
+ if (ccp->out.state == NULL && ccp->out.algorithm >= 0 &&
+ ccp->out.algorithm < NALGORITHMS) {
+ ccp->out.state = (*algorithm[ccp->out.algorithm]->o.Init)
+ (&ccp->out.opt->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, int plen, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming data */
+ struct ccp *ccp = fsm2ccp(fp);
+ int type, length;
+ int f;
+ const char *end;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ type = *cp;
+ length = cp[1];
+
+ if (length == 0) {
+ log_Printf(LogCCP, "%s: CCP size zero\n", fp->link->name);
+ break;
+ }
+
+ if (length > sizeof(struct lcp_opt)) {
+ length = sizeof(struct lcp_opt);
+ log_Printf(LogCCP, "%s: Warning: Truncating length to %d\n",
+ fp->link->name, length);
+ }
+
+ for (f = NALGORITHMS-1; f > -1; f--)
+ if (algorithm[f]->id == type)
+ break;
+
+ end = f == -1 ? "" : (*algorithm[f]->Disp)((struct lcp_opt *)cp);
+ if (end == NULL)
+ end = "";
+
+ if (type < NCFTYPES)
+ log_Printf(LogCCP, " %s[%d] %s\n", cftypes[type], length, end);
+ else
+ log_Printf(LogCCP, " ???[%d] %s\n", length, end);
+
+ if (f == -1) {
+ /* Don't understand that :-( */
+ if (mode_type == MODE_REQ) {
+ ccp->my_reject |= (1 << type);
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ }
+ } else {
+ struct ccp_opt *o;
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (IsAccepted(ccp->cfg.neg[algorithm[f]->Neg]) &&
+ ccp->in.algorithm == -1) {
+ memcpy(&ccp->in.opt, cp, length);
+ switch ((*algorithm[f]->i.Set)(&ccp->in.opt, &ccp->cfg)) {
+ case MODE_REJ:
+ memcpy(dec->rejend, &ccp->in.opt, ccp->in.opt.len);
+ dec->rejend += ccp->in.opt.len;
+ break;
+ case MODE_NAK:
+ memcpy(dec->nakend, &ccp->in.opt, ccp->in.opt.len);
+ dec->nakend += ccp->in.opt.len;
+ break;
+ case MODE_ACK:
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ ccp->his_proto = type;
+ ccp->in.algorithm = f; /* This one'll do :-) */
+ break;
+ }
+ } else {
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ }
+ break;
+ case MODE_NAK:
+ for (o = ccp->out.opt; o != NULL; o = o->next)
+ if (o->val.id == cp[0])
+ break;
+ if (o == NULL)
+ log_Printf(LogCCP, "%s: Warning: Ignoring peer NAK of unsent option\n",
+ fp->link->name);
+ else {
+ memcpy(&o->val, cp, length);
+ if ((*algorithm[f]->o.Set)(&o->val) == MODE_ACK)
+ ccp->my_proto = algorithm[f]->id;
+ else {
+ ccp->his_reject |= (1 << type);
+ ccp->my_proto = -1;
+ }
+ }
+ break;
+ case MODE_REJ:
+ ccp->his_reject |= (1 << type);
+ ccp->my_proto = -1;
+ break;
+ }
+ }
+
+ plen -= cp[1];
+ cp += cp[1];
+ }
+
+ if (mode_type != MODE_NOP) {
+ if (dec->rejend != dec->rej) {
+ /* rejects are preferred */
+ dec->ackend = dec->ack;
+ dec->nakend = dec->nak;
+ if (ccp->in.state == NULL) {
+ ccp->his_proto = -1;
+ ccp->in.algorithm = -1;
+ }
+ } else if (dec->nakend != dec->nak) {
+ /* then NAKs */
+ dec->ackend = dec->ack;
+ if (ccp->in.state == NULL) {
+ ccp->his_proto = -1;
+ ccp->in.algorithm = -1;
+ }
+ }
+ }
+}
+
+void
+ccp_Input(struct ccp *ccp, struct bundle *bundle, struct mbuf *bp)
+{
+ /* Got PROTO_CCP from link */
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&ccp->fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogCCP, "%s: Error: Unexpected CCP in phase %s (ignored)\n",
+ ccp->fsm.link->name, bundle_PhaseName(bundle));
+ mbuf_Free(bp);
+ }
+}
+
+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);
+}
+
+int
+ccp_Compress(struct ccp *ccp, struct link *l, int pri, u_short proto,
+ struct mbuf *m)
+{
+ /*
+ * Compress outgoing data. It's already deemed to be suitable Network
+ * Layer data.
+ */
+ if (ccp->fsm.state == ST_OPENED && ccp->out.state != NULL)
+ return (*algorithm[ccp->out.algorithm]->o.Write)
+ (ccp->out.state, ccp, l, pri, proto, m);
+ return 0;
+}
+
+struct mbuf *
+ccp_Decompress(struct ccp *ccp, u_short *proto, struct mbuf *bp)
+{
+ /*
+ * If proto isn't PROTO_[I]COMPD, we still want to pass it to the
+ * decompression routines so that the dictionary's updated
+ */
+ if (ccp->fsm.state == ST_OPENED) {
+ if (*proto == PROTO_COMPD || *proto == PROTO_ICOMPD) {
+ /* Decompress incoming data */
+ if (ccp->reset_sent != -1)
+ /* Send another REQ and put the packet in the bit bucket */
+ fsm_Output(&ccp->fsm, CODE_RESETREQ, ccp->reset_sent, NULL, 0);
+ else if (ccp->in.state != NULL)
+ return (*algorithm[ccp->in.algorithm]->i.Read)
+ (ccp->in.state, ccp, proto, bp);
+ mbuf_Free(bp);
+ bp = NULL;
+ } else if (PROTO_COMPRESSIBLE(*proto) && ccp->in.state != NULL)
+ /* Add incoming Network Layer traffic to our dictionary */
+ (*algorithm[ccp->in.algorithm]->i.DictSetup)
+ (ccp->in.state, 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 */
+}
diff --git a/usr.sbin/ppp/ccp.h b/usr.sbin/ppp/ccp.h
new file mode 100644
index 0000000..5cac40d
--- /dev/null
+++ b/usr.sbin/ppp/ccp.h
@@ -0,0 +1,128 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ccp.h,v 1.19 1998/06/30 23:04:12 brian Exp $
+ *
+ * TODO:
+ */
+
+#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_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
+#define CCP_NEG_TOTAL 3
+
+struct mbuf;
+struct link;
+
+struct ccp_config {
+ struct {
+ struct {
+ int winsize;
+ } in, out;
+ } deflate;
+ 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 lcp_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 lcp_opt opt; /* Set by implementations OptInit() */
+ } in;
+
+ struct {
+ int algorithm; /* Algorithm in use */
+ void *state; /* Returned by implementations Init() */
+ struct ccp_opt *opt; /* Set by implementations 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 lcp_opt *); /* Use result immediately ! */
+ struct {
+ int (*Set)(struct lcp_opt *, const struct ccp_config *);
+ void *(*Init)(struct lcp_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 {
+ void (*OptInit)(struct lcp_opt *, const struct ccp_config *);
+ int (*Set)(struct lcp_opt *);
+ void *(*Init)(struct lcp_opt *);
+ void (*Term)(void *);
+ void (*Reset)(void *);
+ int (*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 void ccp_SendResetReq(struct fsm *);
+extern void ccp_Input(struct ccp *, struct bundle *, struct mbuf *);
+extern int ccp_ReportStatus(struct cmdargs const *);
+extern int ccp_Compress(struct ccp *, struct link *, int, u_short, struct mbuf *);
+extern struct mbuf *ccp_Decompress(struct ccp *, u_short *, struct mbuf *);
+extern u_short ccp_Proto(struct ccp *);
+extern void ccp_SetupCallbacks(struct ccp *);
+extern int ccp_SetOpenMode(struct ccp *);
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
new file mode 100644
index 0000000..dde8081
--- /dev/null
+++ b/usr.sbin/ppp/chap.c
@@ -0,0 +1,760 @@
+/*
+ * PPP CHAP Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.c,v 1.47 1999/02/20 01:12:45 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_DES
+#include <md4.h>
+#endif
+#include <md5.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "auth.h"
+#include "async.h"
+#include "throughput.h"
+#include "descriptor.h"
+#include "chap.h"
+#include "iplist.h"
+#include "slcompress.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 "bundle.h"
+#include "chat.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifdef HAVE_DES
+#include "chap_ms.h"
+#endif
+
+static const char *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 = mbuf_Alloc(plen, MB_FSM);
+ 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);
+ hdlc_Output(&physical->link, PRI_LINK, PROTO_CHAP, bp);
+}
+
+static char *
+chap_BuildAnswer(char *name, char *key, u_char id, char *challenge, u_char type
+#ifdef HAVE_DES
+ , int lanman
+#endif
+ )
+{
+ char *result, *digest;
+ size_t nlen, klen;
+
+ nlen = strlen(name);
+ klen = strlen(key);
+
+#ifdef HAVE_DES
+ 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
+#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];
+
+ 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;
+ }
+
+ 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();
+ close(in[1]);
+ close(out[0]);
+ if (out[1] == STDIN_FILENO) {
+ fd = dup(out[1]);
+ close(out[1]);
+ out[1] = fd;
+ }
+ dup2(in[0], STDIN_FILENO);
+ dup2(out[1], STDOUT_FILENO);
+ if ((fd = open(_PATH_DEVNULL, O_RDWR)) == -1) {
+ log_Printf(LogALERT, "Chap: Failed to open %s: %s\n",
+ _PATH_DEVNULL, strerror(errno));
+ exit(1);
+ }
+ dup2(fd, STDERR_FILENO);
+ fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */
+
+ setuid(geteuid());
+ argc = command_Interpret(prog, strlen(prog), argv);
+ command_Expand(nargv, argc, (char const *const *)argv,
+ chap->auth.physical->dl->bundle, 0);
+ execvp(nargv[0], nargv);
+
+ log_Printf(LogWARN, "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 + 1, *chap->challenge);
+ 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 = 0;
+#ifdef HAVE_DES
+ chap->peertries = 0;
+#endif
+}
+
+static void
+chap_Respond(struct chap *chap, char *name, char *key, u_char type
+#ifdef HAVE_DES
+ , int lm
+#endif
+ )
+{
+ u_char *ans;
+
+ ans = chap_BuildAnswer(name, key, chap->auth.id, chap->challenge, type
+#ifdef HAVE_DES
+ , lm
+#endif
+ );
+
+ if (ans) {
+ ChapOutput(chap->auth.physical, CHAP_RESPONSE, chap->auth.id,
+ ans, *ans + 1 + strlen(name), name);
+#ifdef HAVE_DES
+ chap->NTRespSent = !lm;
+#endif
+ free(ans);
+ } else
+ ChapOutput(chap->auth.physical, CHAP_FAILURE, chap->auth.id,
+ "Out of memory!", 14, NULL);
+}
+
+static int
+chap_UpdateSet(struct descriptor *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 descriptor *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 descriptor *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 {
+#ifdef HAVE_DES
+ 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
+#ifdef HAVE_DES
+ , lanman
+#endif
+ );
+ chap_Cleanup(chap, 0);
+ }
+ }
+}
+
+static int
+chap_Write(struct descriptor *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_Challenge(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) {
+ randinit();
+ cp = chap->challenge;
+
+#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
+ {
+#ifdef HAVE_DES
+ if (authp->physical->link.lcp.want_authtype == 0x80)
+ *cp++ = 8; /* MS does 8 byte callenges :-/ */
+ else
+#endif
+ *cp++ = random() % (CHAPCHALLENGELEN-16) + 16;
+ for (i = 0; i < *chap->challenge; i++)
+ *cp++ = random() & 0xff;
+ }
+ memcpy(cp, authp->physical->dl->bundle->cfg.auth.name, len);
+ }
+ ChapOutput(authp->physical, CHAP_CHALLENGE, authp->id, chap->challenge,
+ 1 + *chap->challenge + len, NULL);
+}
+
+static void
+chap_Success(struct authinfo *authp)
+{
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+ ChapOutput(authp->physical, CHAP_SUCCESS, authp->id, "Welcome!!", 10, NULL);
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(authp->physical->dl->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)
+{
+ ChapOutput(authp->physical, CHAP_FAILURE, authp->id, "Invalid!!", 9, NULL);
+ datalink_AuthNotOk(authp->physical->dl);
+}
+
+static int
+chap_Cmp(u_char type, char *myans, int mylen, char *hisans, int hislen
+#ifdef HAVE_DES
+ , int lm
+#endif
+ )
+{
+ if (mylen != hislen)
+ return 0;
+#ifdef HAVE_DES
+ else if (type == 0x80) {
+ int off = lm ? 0 : 24;
+
+ if (memcmp(myans + off, hisans + off, 24))
+ return 0;
+ }
+#endif
+ else if (memcmp(myans, hisans, mylen))
+ return 0;
+
+ return 1;
+}
+
+#ifdef HAVE_DES
+static int
+chap_HaveAnotherGo(struct chap *chap)
+{
+ if (++chap->peertries < 3) {
+ /* Give the peer another shot */
+ *chap->challenge = '\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 = 0;
+#ifdef HAVE_DES
+ chap->NTRespSent = 0;
+ chap->peertries = 0;
+#endif
+}
+
+void
+chap_ReInit(struct chap *chap)
+{
+ chap_Cleanup(chap, SIGTERM);
+}
+
+void
+chap_Input(struct physical *p, struct mbuf *bp)
+{
+ struct chap *chap = &p->dl->chap;
+ char *name, *key, *ans;
+ int len, nlen;
+ u_char alen;
+#ifdef HAVE_DES
+ int lanman;
+#endif
+
+ if (bundle_Phase(p->dl->bundle) != PHASE_NETWORK &&
+ bundle_Phase(p->dl->bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected chap input - dropped !\n");
+ mbuf_Free(bp);
+ return;
+ }
+
+ 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 = mbuf_Length(bp);
+ ans = NULL;
+
+ if (chap->auth.in.hdr.code != CHAP_CHALLENGE &&
+ chap->auth.id != chap->auth.in.hdr.id &&
+ Enabled(p->dl->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);
+ mbuf_Free(bp);
+ return;
+ }
+ chap->auth.id = chap->auth.in.hdr.id; /* We respond with this id */
+
+#ifdef HAVE_DES
+ 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");
+ mbuf_Free(bp);
+ return;
+ }
+ *chap->challenge = alen;
+ bp = mbuf_Read(bp, chap->challenge + 1, alen);
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifdef HAVE_DES
+ lanman = p->link.lcp.his_authtype == 0x80 &&
+ ((chap->NTRespSent && IsAccepted(p->link.lcp.cfg.chap80lm)) ||
+ !IsAccepted(p->link.lcp.cfg.chap80nt));
+#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");
+ mbuf_Free(bp);
+ return;
+ }
+ if ((ans = malloc(alen + 2)) == NULL) {
+ log_Printf(LogERROR, "Chap Input: Out of memory !\n");
+ mbuf_Free(bp);
+ return;
+ }
+ *ans = chap->auth.id;
+ bp = mbuf_Read(bp, ans + 1, alen);
+ ans[alen+1] = '\0';
+ bp = auth_ReadName(&chap->auth, bp, len);
+#ifdef HAVE_DES
+ lanman = 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");
+ mbuf_Free(bp);
+ return;
+ }
+ 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,
+#ifdef HAVE_DES
+ 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,
+#ifdef HAVE_DES
+ 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 (*p->dl->bundle->cfg.auth.key == '!')
+ chap_StartChild(chap, p->dl->bundle->cfg.auth.key + 1,
+ p->dl->bundle->cfg.auth.name);
+ else
+ chap_Respond(chap, p->dl->bundle->cfg.auth.name,
+ p->dl->bundle->cfg.auth.key, p->link.lcp.his_authtype
+#ifdef HAVE_DES
+ , lanman
+#endif
+ );
+ break;
+
+ case CHAP_RESPONSE:
+ name = chap->auth.in.name;
+ nlen = strlen(name);
+#ifndef NORADIUS
+ if (*p->dl->bundle->radius.cfg.file) {
+ chap->challenge[*chap->challenge+1] = '\0';
+ radius_Authenticate(&p->dl->bundle->radius, &chap->auth,
+ chap->auth.in.name, ans, chap->challenge + 1);
+ } else
+#endif
+ {
+ key = auth_GetSecret(p->dl->bundle, name, nlen, p);
+ if (key) {
+ char *myans;
+#ifdef HAVE_DES
+ if (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 (!lanman && !IsEnabled(p->link.lcp.cfg.chap80nt) &&
+ p->link.lcp.want_authtype == 0x80) {
+ log_Printf(LogPHASE, "Auth failure: mschap not enabled\n");
+ if (chap_HaveAnotherGo(chap))
+ break;
+ key = NULL;
+ } else
+#endif
+ {
+ myans = chap_BuildAnswer(name, key, chap->auth.id,
+ chap->challenge,
+ p->link.lcp.want_authtype
+#ifdef HAVE_DES
+ , lanman
+#endif
+ );
+ if (myans == NULL)
+ key = NULL;
+ else {
+ if (!chap_Cmp(p->link.lcp.want_authtype, myans + 1, *myans,
+ ans + 1, alen
+#ifdef HAVE_DES
+ , 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)
+ /*
+ * 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);
+ }
+
+ mbuf_Free(bp);
+}
diff --git a/usr.sbin/ppp/chap.h b/usr.sbin/ppp/chap.h
new file mode 100644
index 0000000..46fe5f9
--- /dev/null
+++ b/usr.sbin/ppp/chap.h
@@ -0,0 +1,55 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h,v 1.13 1999/02/18 00:52:12 brian Exp $
+ *
+ * TODO:
+ */
+
+struct mbuf;
+struct physical;
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+struct chap {
+ struct descriptor 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;
+ u_char challenge[CHAPCHALLENGELEN + AUTHLEN];
+#ifdef HAVE_DES
+ unsigned NTRespSent : 1; /* Our last response */
+ int peertries;
+#endif
+};
+
+#define descriptor2chap(d) \
+ ((d)->type == CHAP_DESCRIPTOR ? (struct chap *)(d) : NULL)
+#define auth2chap(a) (struct chap *)((char *)a - (int)&((struct chap *)0)->auth)
+
+extern void chap_Init(struct chap *, struct physical *);
+extern void chap_ReInit(struct chap *);
+extern void chap_Input(struct physical *, struct mbuf *);
diff --git a/usr.sbin/ppp/chap_ms.c b/usr.sbin/ppp/chap_ms.c
new file mode 100644
index 0000000..d12cdde
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.c
@@ -0,0 +1,124 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP (NT only) 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.
+ *
+ * $Id: chap_ms.c,v 1.6 1998/05/21 21:44:30 brian Exp $
+ *
+ */
+
+#include <sys/types.h>
+
+#include <ctype.h>
+#include <des.h>
+#include <string.h>
+
+#include "chap_ms.h"
+
+/* 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);
+}
+
+/* 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..81a543d
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.h
@@ -0,0 +1,32 @@
+/*
+ * chap.h - Cryptographic 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.
+ *
+ * $Id: chap_ms.h,v 1.3 1998/05/21 21:44:32 brian Exp $
+ */
+
+/* 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
+
+extern void mschap_NT(char *, char *);
+extern void mschap_LANMan(char *, char *, char *);
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c
new file mode 100644
index 0000000..ad56a01
--- /dev/null
+++ b/usr.sbin/ppp/chat.c
@@ -0,0 +1,804 @@
+/*-
+ * 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.
+ *
+ * $Id: chat.c,v 1.53 1999/01/28 01:56:31 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.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 "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 "ipcp.h"
+#include "filter.h"
+#include "cbcp.h"
+#include "command.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+
+#define BUFLEFT(c) (sizeof (c)->buf - ((c)->bufend - (c)->buf))
+#define issep(c) ((c) == '\t' || (c) == ' ')
+
+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 descriptor *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;
+
+ c->argptr = c->argv[++c->arg];
+
+ 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 != '!';
+
+ /* We leave room for a potential HDLC header in the target string */
+ ExpandString(c, c->argptr, c->exp + 2, sizeof c->exp - 2, needcr);
+
+ 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] == '!')
+ ExecStr(c->physical, c->exp + 3, c->exp + 2, sizeof c->exp - 2);
+
+ 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 + 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_UpdateSet(&c->physical->desc, r, NULL, e, n, 1);
+ else
+ return physical_UpdateSet(&c->physical->desc, NULL, w, e, n, 1);
+}
+
+static int
+chat_IsSet(struct descriptor *d, const fd_set *fdset)
+{
+ struct chat *c = descriptor2chat(d);
+ return 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 descriptor *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 descriptor *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)) {
+ /* 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 ? 1 : 0;
+ if (wrote == -1) {
+ if (errno != EINTR)
+ log_Printf(LogERROR, "chat_Write: %s\n", strerror(errno));
+ 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, const char *data, int emptybuf,
+ const char *phone)
+{
+ 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->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));
+ }
+
+ c->arg = -1;
+ c->argptr = NULL;
+ c->nargptr = NULL;
+
+ if (emptybuf)
+ c->bufstart = c->bufend = c->buf;
+
+ c->TimeoutSec = 30;
+ c->TimedOut = 0;
+ c->phone = phone;
+ c->abort.num = 0;
+
+ memset(&c->pause, '\0', sizeof c->pause);
+ memset(&c->timeout, '\0', sizeof c->timeout);
+}
+
+void
+chat_Destroy(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;
+}
+
+static char *
+findblank(char *p, int instring)
+{
+ if (instring) {
+ while (*p) {
+ if (*p == '\\') {
+ strcpy(p, p + 1);
+ if (!*p)
+ break;
+ } else if (*p == '"')
+ return (p);
+ p++;
+ }
+ } else {
+ while (*p) {
+ if (issep(*p))
+ return (p);
+ p++;
+ }
+ }
+ return p;
+}
+
+int
+MakeArgs(char *script, char **pvect, int maxargs)
+{
+ int nargs, nb;
+ int instring;
+
+ nargs = 0;
+ while (*script) {
+ nb = strspn(script, " \t");
+ script += nb;
+ if (*script) {
+ if (*script == '"') {
+ instring = 1;
+ script++;
+ if (*script == '\0')
+ break; /* Shouldn't return here. Need to null
+ * terminate below */
+ } else
+ instring = 0;
+ if (nargs >= maxargs - 1)
+ break;
+ *pvect++ = script;
+ nargs++;
+ script = findblank(script, instring);
+ if (*script)
+ *script++ = '\0';
+ }
+ }
+ *pvect = NULL;
+ return nargs;
+}
+
+/*
+ * \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 sendmode)
+{
+ int addcr = 0;
+
+ result[--reslen] = '\0';
+ if (sendmode)
+ addcr = 1;
+ while (*str && reslen > 0) {
+ switch (*str) {
+ case '\\':
+ str++;
+ switch (*str) {
+ case 'c':
+ if (sendmode)
+ addcr = 0;
+ break;
+ case 'd': /* Delay 2 seconds */
+ chat_Pause(c, 2 * SECTICKS);
+ break;
+ case 'p':
+ chat_Pause(c, SECTICKS / 4);
+ break; /* Pause 0.25 sec */
+ 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);
+ reslen -= strlen(result);
+ result += strlen(result);
+ break;
+ case 'T':
+ if (c->phone) {
+ strncpy(result, c->phone, reslen);
+ reslen -= strlen(result);
+ result += strlen(result);
+ }
+ break;
+ case 'U':
+ strncpy(result, c->physical->dl->bundle->cfg.auth.name, reslen);
+ reslen -= strlen(result);
+ result += strlen(result);
+ 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 (addcr)
+ *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;
+
+ log_Printf(LogCHAT, "Exec: %s\n", command);
+ argc = MakeArgs(command, vector, VECSIZE(vector));
+ command_Expand(argv, argc, (char const *const *)vector,
+ physical->dl->bundle, 0);
+
+ if (pipe(fids) < 0) {
+ log_Printf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
+ strerror(errno));
+ *out = '\0';
+ return;
+ }
+ if ((pid = fork()) == 0) {
+ close(fids[0]);
+ timer_TermService();
+ fids[1] = fcntl(fids[1], F_DUPFD, 4);
+ dup2(physical_GetFD(physical), STDIN_FILENO);
+ dup2(STDIN_FILENO, STDOUT_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ close(3);
+ if (open(_PATH_TTY, O_RDWR) == 3)
+ fcntl(3, F_SETFD, 0); /* Clear close-on-exec flag */
+ else
+ fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */
+ setuid(geteuid());
+ 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..e57f32e
--- /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.
+ *
+ * $Id: chat.h,v 1.9.2.8 1998/05/01 19:24:13 brian Exp $
+ */
+
+#define CHAT_EXPECT 0
+#define CHAT_SEND 1
+#define CHAT_DONE 2
+#define CHAT_FAILED 3
+
+#define MAXABORTS 50
+
+struct physical;
+
+struct chat {
+ struct descriptor 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 *, const char *, int,
+ const char *);
+extern void chat_Destroy(struct chat *);
+extern int MakeArgs(char *, char **, int); /* Mangles the first arg ! */
diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c
new file mode 100644
index 0000000..830e95c
--- /dev/null
+++ b/usr.sbin/ppp/command.c
@@ -0,0 +1,2547 @@
+/*
+ * PPP User command processing module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: command.c,v 1.189 1999/03/19 00:05:32 brian Exp $
+ *
+ */
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NOALIAS
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ipcp.h"
+#include "modem.h"
+#ifndef NOALIAS
+#include "alias_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 "bundle.h"
+#include "server.h"
+#include "prompt.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#include "iface.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
+
+/* ``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_LQR 45
+#define NEG_PAP 46
+#define NEG_PPPDDEFLATE 47
+#define NEG_PRED1 48
+#define NEG_PROTOCOMP 49
+#define NEG_SHORTSEQ 50
+#define NEG_VJCOMP 51
+#define NEG_DNS 52
+
+const char Version[] = "2.11";
+const char VersionDate[] = "$Date: 1999/03/19 00:05:32 $";
+
+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 NOALIAS
+static int AliasEnable(struct cmdargs const *);
+static int AliasOption(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
+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;
+}
+
+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;
+}
+
+int
+SaveCommand(struct cmdargs const *arg)
+{
+ log_Printf(LogWARN, "save command is not implemented (yet).\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]))
+ break;
+ else
+ pos++;
+
+ 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;
+}
+
+void
+command_Expand(char **nargv, int argc, char const *const *oargv,
+ struct bundle *bundle, int inc0)
+{
+ int arg;
+ char pid[12];
+
+ if (inc0)
+ arg = 0; /* Start at arg 0 */
+ else {
+ nargv[0] = strdup(oargv[0]);
+ arg = 1;
+ }
+ snprintf(pid, sizeof pid, "%d", getpid());
+ for (; arg < argc; arg++) {
+ nargv[arg] = strdup(oargv[arg]);
+ nargv[arg] = subst(nargv[arg], "HISADDR",
+ inet_ntoa(bundle->ncp.ipcp.peer_ip));
+ nargv[arg] = subst(nargv[arg], "AUTHNAME", bundle->cfg.auth.name);
+ nargv[arg] = subst(nargv[arg], "INTERFACE", bundle->iface->name);
+ nargv[arg] = subst(nargv[arg], "MYADDR", inet_ntoa(bundle->ncp.ipcp.my_ip));
+ nargv[arg] = subst(nargv[arg], "USER", bundle->ncp.mp.peer.authname);
+ 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] = 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] = subst(nargv[arg], "PROCESSID", pid);
+ nargv[arg] = subst(nargv[arg], "LABEL", bundle_GetLabel(bundle));
+ }
+ nargv[arg] = NULL;
+}
+
+static int
+ShellCommand(struct cmdargs const *arg, int bg)
+{
+ const char *shell;
+ pid_t shpid;
+
+#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;
+ }
+ }
+
+ 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);
+ }
+ for (i = 0; i < 3; i++)
+ dup2(fd, i);
+
+ fcntl(3, F_SETFD, 1); /* Set close-on-exec flag */
+
+ setuid(geteuid());
+ 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);
+ if (bg) {
+ pid_t p;
+
+ p = getpid();
+ if (daemon(1, 1) == -1) {
+ log_Printf(LogERROR, "%d: daemon: %s\n", (int)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, 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);
+}
+
+#ifndef NOALIAS
+static struct cmdtab const AliasCommands[] =
+{
+ {"addr", NULL, alias_RedirectAddr, LOCAL_AUTH,
+ "static address translation", "alias addr [addr_local addr_alias]"},
+ {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
+ "stop incoming connections", "alias deny_incoming [yes|no]",
+ (const void *) PKT_ALIAS_DENY_INCOMING},
+ {"enable", NULL, AliasEnable, LOCAL_AUTH,
+ "enable IP aliasing", "alias enable [yes|no]"},
+ {"log", NULL, AliasOption, LOCAL_AUTH,
+ "log aliasing link creation", "alias log [yes|no]",
+ (const void *) PKT_ALIAS_LOG},
+ {"port", NULL, alias_RedirectPort, LOCAL_AUTH, "port redirection",
+ "alias port proto localaddr:port[-port] aliasport[-aliasport]"},
+ {"pptp", NULL, alias_Pptp, LOCAL_AUTH,
+ "Set the PPTP address", "alias pptp IP"},
+ {"proxy", NULL, alias_ProxyRule, LOCAL_AUTH,
+ "proxy control", "alias proxy server host[:port] ..."},
+ {"same_ports", NULL, AliasOption, LOCAL_AUTH,
+ "try to leave port numbers unchanged", "alias same_ports [yes|no]",
+ (const void *) PKT_ALIAS_SAME_PORTS},
+ {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
+ "alias unregistered (private) IP address space only",
+ "alias unregistered_only [yes|no]",
+ (const void *) PKT_ALIAS_UNREGISTERED_ONLY},
+ {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
+ "allocate host sockets", "alias use_sockets [yes|no]",
+ (const void *) PKT_ALIAS_USE_SOCKETS},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "alias help|? [command]", AliasCommands},
+ {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"},
+ {"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", "alias 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},
+#ifndef NOALIAS
+ {"alias", NULL, RunListCommand, LOCAL_AUTH,
+ "alias control", "alias option [yes|no]", AliasCommands},
+#endif
+ {"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|modem [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"},
+ {"enable", NULL, NegotiateCommand, LOCAL_AUTH | LOCAL_CX_OPT,
+ "Enable option", "enable option .."},
+ {"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 ...]"},
+ {"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"},
+ {"save", NULL, SaveCommand, LOCAL_AUTH,
+ "Save settings", "save"},
+ {"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, VersionDate);
+ 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"},
+ {"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"},
+ {"modem", NULL, modem_ShowStatus, LOCAL_AUTH | LOCAL_CX,
+ "(low-level) link info", "show modem"},
+ {"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_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);
+ }
+ 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)) {
+ static char buf[LINE_LEN];
+ int f, n;
+
+ *buf = '\0';
+ if (label) {
+ strncpy(buf, label, sizeof buf - 3);
+ buf[sizeof buf - 3] = '\0';
+ strcat(buf, ": ");
+ }
+ n = strlen(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);
+ }
+}
+
+void
+command_Decode(struct bundle *bundle, char *buff, int nb, struct prompt *prompt,
+ const char *label)
+{
+ int argc;
+ char *argv[MAXARGS];
+
+ argc = command_Interpret(buff, nb, argv);
+ command_Run(bundle, argc, (char const *const *)argv, prompt, label, NULL);
+}
+
+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");
+ 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;
+}
+
+#define ismask(x) \
+ (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
+
+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;
+
+ /* 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];
+ if (!ismask(mask))
+ return -1;
+ } else if (strcasecmp(port, "none") == 0) {
+ if (server_Close(arg->bundle))
+ log_Printf(LogPHASE, "Disabled server port.\n");
+ return 0;
+ } else
+ return -1;
+
+ strncpy(server.passwd, passwd, sizeof server.passwd - 1);
+ server.passwd[sizeof server.passwd - 1] = '\0';
+
+ if (*port == '/') {
+ mode_t imask;
+ char *ptr, name[LINE_LEN + 12];
+
+ if (mask != NULL) {
+ unsigned m;
+
+ if (sscanf(mask, "%o", &m) == 1)
+ imask = m;
+ else
+ return -1;
+ } else
+ imask = (mode_t)-1;
+
+ 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
+SetModemParity(struct cmdargs const *arg)
+{
+ return arg->argc > arg->argn ? modem_SetParity(arg->cx->physical,
+ arg->argv[arg->argn]) : -1;
+}
+
+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 ipcp *ipcp = &arg->bundle->ncp.ipcp;
+ const char *hisaddr;
+
+ if (arg->argc > arg->argn + 4)
+ return -1;
+
+ hisaddr = NULL;
+ memset(&ipcp->cfg.my_range, '\0', sizeof ipcp->cfg.my_range);
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ ipcp->cfg.HaveTriggerAddress = 0;
+ ipcp->cfg.netmask.s_addr = INADDR_ANY;
+ iplist_reset(&ipcp->cfg.peer_list);
+
+ if (arg->argc > arg->argn) {
+ if (!ParseAddr(ipcp, arg->argv[arg->argn],
+ &ipcp->cfg.my_range.ipaddr, &ipcp->cfg.my_range.mask,
+ &ipcp->cfg.my_range.width))
+ return 1;
+ if (arg->argc > arg->argn+1) {
+ hisaddr = arg->argv[arg->argn+1];
+ if (arg->argc > arg->argn+2) {
+ ipcp->ifmask = ipcp->cfg.netmask = GetIpAddr(arg->argv[arg->argn+2]);
+ if (arg->argc > arg->argn+3) {
+ ipcp->cfg.TriggerAddress = GetIpAddr(arg->argv[arg->argn+3]);
+ ipcp->cfg.HaveTriggerAddress = 1;
+ }
+ }
+ }
+ }
+
+ /* 0.0.0.0 means any address (0 bits) */
+ if (ipcp->cfg.my_range.ipaddr.s_addr == INADDR_ANY) {
+ ipcp->cfg.my_range.mask.s_addr = INADDR_ANY;
+ ipcp->cfg.my_range.width = 0;
+ }
+ ipcp->my_ip.s_addr = ipcp->cfg.my_range.ipaddr.s_addr;
+
+ 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;
+ const char *argp;
+ struct datalink *cx = arg->cx; /* LOCAL_CX uses this */
+ const char *err = NULL;
+ struct link *l = command_ChooseLink(arg); /* LOCAL_CX_OPT uses this */
+ struct in_addr dummyaddr, *addr;
+
+ if (arg->argc > arg->argn)
+ argp = arg->argv[arg->argn];
+ else
+ argp = "";
+
+ 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:
+ switch (bundle_Phase(arg->bundle)) {
+ case PHASE_DEAD:
+ case PHASE_ESTABLISH:
+ 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;
+ default:
+ err = "set authkey: Only available at phase DEAD/ESTABLISH\n";
+ log_Printf(LogWARN, err);
+ break;
+ }
+ break;
+
+ case VAR_AUTHNAME:
+ switch (bundle_Phase(arg->bundle)) {
+ 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;
+ default:
+ err = "set authname: Only available at phase DEAD/ESTABLISH\n";
+ log_Printf(LogWARN, err);
+ break;
+ }
+ break;
+
+ case VAR_AUTOLOAD:
+ if (arg->argc == arg->argn + 2 || arg->argc == arg->argn + 4) {
+ arg->bundle->autoload.running = 1;
+ arg->bundle->cfg.autoload.max.timeout = atoi(arg->argv[arg->argn]);
+ arg->bundle->cfg.autoload.max.packets = atoi(arg->argv[arg->argn + 1]);
+ if (arg->argc == arg->argn + 4) {
+ arg->bundle->cfg.autoload.min.timeout = atoi(arg->argv[arg->argn + 2]);
+ arg->bundle->cfg.autoload.min.packets = atoi(arg->argv[arg->argn + 3]);
+ } else {
+ arg->bundle->cfg.autoload.min.timeout = 0;
+ arg->bundle->cfg.autoload.min.packets = 0;
+ }
+ } else {
+ err = "Set autoload requires two or four arguments\n";
+ log_Printf(LogWARN, err);
+ }
+ 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 {
+ err = "No window size specified\n";
+ log_Printf(LogWARN, err);
+ }
+ break;
+
+ 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 {
+ err = "No accmap specified\n";
+ log_Printf(LogWARN, err);
+ }
+ break;
+
+ case VAR_MODE:
+ mode = Nam2mode(argp);
+ if (mode == PHYS_NONE || mode == PHYS_ALL) {
+ log_Printf(LogWARN, "%s: Invalid mode\n", argp);
+ return -1;
+ }
+ 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");
+ return 1;
+ }
+ break;
+ default:
+ log_Printf(LogWARN, "mrru: Only changable at phase DEAD/ESTABLISH\n");
+ return 1;
+ }
+ 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);
+ return 1;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ return 1;
+ } else
+ arg->bundle->ncp.mp.cfg.mrru = long_val;
+ break;
+
+ case VAR_MRU:
+ long_val = atol(argp);
+ if (long_val == 0)
+ l->lcp.cfg.mru = DEF_MRU;
+ else if (long_val < MIN_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too small - min %d\n", long_val, MIN_MRU);
+ return 1;
+ } else if (long_val > MAX_MRU) {
+ log_Printf(LogWARN, "MRU %ld: too big - max %d\n", long_val, MAX_MRU);
+ return 1;
+ } else
+ l->lcp.cfg.mru = long_val;
+ break;
+
+ case VAR_MTU:
+ long_val = atol(argp);
+ if (long_val && long_val < MIN_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too small - min %d\n", long_val, MIN_MTU);
+ return 1;
+ } else if (long_val > MAX_MTU) {
+ log_Printf(LogWARN, "MTU %ld: too big - max %d\n", long_val, MAX_MTU);
+ return 1;
+ } else
+ arg->bundle->cfg.mtu = long_val;
+ 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 {
+ err = "%s: Invalid openmode\n";
+ log_Printf(LogWARN, err, argp);
+ }
+ 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_IDLETIMEOUT:
+ if (arg->argc > arg->argn+1)
+ err = "Too many idle timeout values\n";
+ else if (arg->argc == arg->argn+1)
+ bundle_SetIdleTimer(arg->bundle, atoi(argp));
+ if (err)
+ log_Printf(LogWARN, err);
+ 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);
+ return 1;
+ } else
+ l->lcp.cfg.lqrperiod = long_val;
+ break;
+
+ case VAR_LCPRETRY:
+ return 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:
+ return 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:
+ return 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:
+ return 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:
+ return 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;
+
+ case VAR_NBNS:
+ case VAR_DNS:
+ if (param == VAR_DNS)
+ addr = arg->bundle->ncp.ipcp.cfg.ns.dns;
+ else
+ addr = arg->bundle->ncp.ipcp.cfg.ns.nbns;
+
+ addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
+
+ if (arg->argc > arg->argn) {
+ ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn],
+ addr, &dummyaddr, &dummyint);
+ if (arg->argc > arg->argn+1)
+ ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn + 1],
+ addr + 1, &dummyaddr, &dummyint);
+
+ if (addr[1].s_addr == INADDR_ANY)
+ addr[1].s_addr = addr[0].s_addr;
+ if (addr[0].s_addr == INADDR_ANY)
+ addr[0].s_addr = addr[1].s_addr;
+ }
+ 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
+ return -1;
+ }
+ 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.ipcp.cfg.sendpipe = long_val;
+ break;
+
+ case VAR_RECVPIPE:
+ long_val = atol(argp);
+ arg->bundle->ncp.ipcp.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));
+ return 1;
+ } 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) {
+ long_val = atol(argp);
+ if (long_val < 0)
+ long_val = 0;
+ cx->physical->cfg.cd.delay = long_val;
+ cx->physical->cfg.cd.required = argp[strlen(argp)-1] == '!';
+ } else {
+ cx->physical->cfg.cd.delay = DEF_CDDELAY;
+ cx->physical->cfg.cd.required = 0;
+ }
+ break;
+ }
+
+ return err ? 1 : 0;
+}
+
+static int
+SetCtsRts(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+1) {
+ if (strcmp(arg->argv[arg->argn], "on") == 0)
+ physical_SetRtsCts(arg->cx->physical, 1);
+ else if (strcmp(arg->argv[arg->argn], "off") == 0)
+ physical_SetRtsCts(arg->cx->physical, 0);
+ else
+ return -1;
+ return 0;
+ }
+ return -1;
+}
+
+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},
+ {"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", SetCtsRts, LOCAL_AUTH | LOCAL_CX,
+ "Use hardware flow control", "set ctsrts [on|off]"},
+ {"deflate", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX_OPT,
+ "deflate window sizes", "set deflate out-winsize in-winsize",
+ (const void *) VAR_WINSIZE},
+ {"device", "line", SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "modem 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]] [tcp|udp|icmp [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]]]]"},
+ {"ipcpretry", "ipcpretries", SetVariable, LOCAL_AUTH, "IPCP retries",
+ "set ipcpretry value [attempts]", (const void *)VAR_IPCPRETRY},
+ {"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] [+|-]async|cbcp|ccp|chat|command|connect|debug|hdlc|id0|"
+ "ipcp|lcp|lqm|phase|tcp/ip|timer|tun..."},
+ {"login", NULL, SetVariable, LOCAL_AUTH | LOCAL_CX,
+ "login script", "set login chat-script", (const void *) VAR_LOGIN},
+ {"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_OPT,
+ "MRU value", "set mru value", (const void *)VAR_MRU},
+ {"mtu", NULL, SetVariable, LOCAL_AUTH,
+ "interface MTU value", "set mtu 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, SetModemParity, LOCAL_AUTH | LOCAL_CX,
+ "modem parity", "set parity [odd|even|none]"},
+ {"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,
+ "server port", "set server|socket TcpPort|LocalName|none password [mask]"},
+ {"speed", NULL, SetModemSpeed, LOCAL_AUTH | LOCAL_CX,
+ "modem speed", "set speed value"},
+ {"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},
+ {"vj", NULL, ipcp_vjset, LOCAL_AUTH,
+ "vj values", "set vj slots|slotcomp [value]"},
+ {"weight", NULL, mp_SetDatalinkWeight, LOCAL_AUTH | LOCAL_CX,
+ "datalink weighting", "set weight n"},
+ {"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 in_addr dest, gateway, netmask;
+ int gw, addrs;
+
+ if (arg->argc != arg->argn+3 && arg->argc != arg->argn+2)
+ return -1;
+
+ addrs = 0;
+ if (arg->argc == arg->argn+2) {
+ if (!strcasecmp(arg->argv[arg->argn], "default"))
+ dest.s_addr = netmask.s_addr = INADDR_ANY;
+ else {
+ int width;
+
+ if (!ParseAddr(&arg->bundle->ncp.ipcp, arg->argv[arg->argn],
+ &dest, &netmask, &width))
+ return -1;
+ if (!strncasecmp(arg->argv[arg->argn], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+ else if (!strncasecmp(arg->argv[arg->argn], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ }
+ gw = 1;
+ } else {
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ addrs = ROUTE_DSTMYADDR;
+ dest = arg->bundle->ncp.ipcp.my_ip;
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ addrs = ROUTE_DSTHISADDR;
+ dest = arg->bundle->ncp.ipcp.peer_ip;
+ } else
+ dest = GetIpAddr(arg->argv[arg->argn]);
+ netmask = GetIpAddr(arg->argv[arg->argn+1]);
+ gw = 2;
+ }
+
+ if (strcasecmp(arg->argv[arg->argn+gw], "HISADDR") == 0) {
+ gateway = arg->bundle->ncp.ipcp.peer_ip;
+ addrs |= ROUTE_GWHISADDR;
+ } else
+ gateway = GetIpAddr(arg->argv[arg->argn+gw]);
+
+ if (bundle_SetRoute(arg->bundle, RTM_ADD, dest, gateway, netmask,
+ arg->cmd->args ? 1 : 0, (addrs & ROUTE_GWHISADDR) ? 1 : 0)
+ && addrs != ROUTE_STATIC)
+ route_Add(&arg->bundle->ncp.ipcp.route, addrs, dest, netmask, gateway);
+
+ return 0;
+}
+
+static int
+DeleteCommand(struct cmdargs const *arg)
+{
+ struct in_addr dest, none;
+ 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.ipcp.route);
+ } else {
+ addrs = 0;
+ if (strcasecmp(arg->argv[arg->argn], "MYADDR") == 0) {
+ dest = arg->bundle->ncp.ipcp.my_ip;
+ addrs = ROUTE_DSTMYADDR;
+ } else if (strcasecmp(arg->argv[arg->argn], "HISADDR") == 0) {
+ dest = arg->bundle->ncp.ipcp.peer_ip;
+ addrs = ROUTE_DSTHISADDR;
+ } else {
+ dest = GetIpAddr(arg->argv[arg->argn]);
+ if (dest.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: Invalid IP address\n", arg->argv[arg->argn]);
+ return -1;
+ }
+ addrs = ROUTE_STATIC;
+ }
+ none.s_addr = INADDR_ANY;
+ bundle_SetRoute(arg->bundle, RTM_DELETE, dest, none, none,
+ arg->cmd->args ? 1 : 0, 0);
+ route_Delete(&arg->bundle->ncp.ipcp.route, addrs, dest);
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+#ifndef NOALIAS
+static int
+AliasEnable(struct cmdargs const *arg)
+{
+ if (arg->argc == arg->argn+1) {
+ if (strcasecmp(arg->argv[arg->argn], "yes") == 0) {
+ arg->bundle->AliasEnabled = 1;
+ return 0;
+ } else if (strcasecmp(arg->argv[arg->argn], "no") == 0) {
+ arg->bundle->AliasEnabled = 0;
+ arg->bundle->cfg.opt &= ~OPT_IFACEALIAS;
+ /* Don't iface_Clear() - there may be manually configured addresses */
+ return 0;
+ }
+ }
+
+ return -1;
+}
+
+
+static int
+AliasOption(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->AliasEnabled) {
+ PacketAliasSetMode(param, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "alias not enabled\n");
+ } else if (strcmp(arg->argv[arg->argn], "no") == 0) {
+ if (arg->bundle->AliasEnabled) {
+ PacketAliasSetMode(0, param);
+ return 0;
+ }
+ log_Printf(LogWARN, "alias not enabled\n");
+ }
+ }
+ return -1;
+}
+#endif /* #ifndef NOALIAS */
+
+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;
+ 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 (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->AliasEnabled) {
+ arg->bundle->cfg.opt = save;
+ log_Printf(LogWARN, "Cannot enable iface-alias without IP aliasing\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;
+#ifdef HAVE_DES
+ 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;
+#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_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[] = {
+ {"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},
+ {"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},
+ {"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},
+
+#define OPT_MAX 9 /* accept/deny allowed below and not above */
+
+ {"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},
+#ifdef HAVE_DES
+ {"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},
+#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},
+ {"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], "modem") == 0) {
+ cx = arg->cx;
+ if (!cx)
+ cx = bundle2datalink(arg->bundle, NULL);
+ if (!cx) {
+ log_Printf(LogWARN, "A link must be specified for ``clear modem''\n");
+ return 1;
+ }
+ t = &cx->physical->link.throughput;
+ } else if (strcasecmp(arg->argv[arg->argn], "ipcp") == 0)
+ t = &arg->bundle->ncp.ipcp.throughput;
+ 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] : "???";
+
+ 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)
+{
+ int bits, n, how;
+ struct in_addr ifa, mask, brd;
+
+ if (arg->argc == arg->argn + 1) {
+ if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
+ return -1;
+ mask.s_addr = brd.s_addr = INADDR_BROADCAST;
+ } else {
+ if (arg->argc == arg->argn + 2) {
+ if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, &mask, &bits))
+ return -1;
+ n = 1;
+ } else if (arg->argc == arg->argn + 3) {
+ if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
+ return -1;
+ if (!ParseAddr(NULL, arg->argv[arg->argn + 1], &mask, NULL, NULL))
+ return -1;
+ n = 2;
+ } else
+ return -1;
+
+ if (!ParseAddr(NULL, arg->argv[arg->argn + n], &brd, NULL, NULL))
+ return -1;
+ }
+
+ how = IFACE_ADD_LAST;
+ if (arg->cmd->args)
+ how |= IFACE_FORCE_ADD;
+
+ return !iface_inAdd(arg->bundle->iface, ifa, mask, brd, how);
+}
+
+static int
+IfaceDeleteCommand(struct cmdargs const *arg)
+{
+ struct in_addr ifa;
+ int ok;
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ if (!ParseAddr(NULL, arg->argv[arg->argn], &ifa, NULL, NULL))
+ return -1;
+
+ if (arg->bundle->ncp.ipcp.fsm.state == ST_OPENED &&
+ arg->bundle->ncp.ipcp.my_ip.s_addr == ifa.s_addr) {
+ log_Printf(LogWARN, "%s: Cannot remove active interface address\n",
+ inet_ntoa(ifa));
+ return 1;
+ }
+
+ ok = iface_inDelete(arg->bundle->iface, ifa);
+ if (!ok) {
+ if (arg->cmd->args)
+ ok = 1;
+ else if (arg->prompt)
+ prompt_Printf(arg->prompt, "%s: No such address\n", inet_ntoa(ifa));
+ else
+ log_Printf(LogWARN, "%s: No such address\n", inet_ntoa(ifa));
+ }
+
+ return !ok;
+}
+
+static int
+IfaceClearCommand(struct cmdargs const *arg)
+{
+ int how;
+
+ 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, how);
+
+ return 0;
+}
+
+static int
+SetProcTitle(struct cmdargs const *arg)
+{
+ static char title[LINE_LEN];
+ char *argv[MAXARGS], *ptr;
+ int len, remaining, f, argc = arg->argc - arg->argn;
+
+ if (arg->argc == arg->argn) {
+ arg->bundle->argv[0] = arg->bundle->argv0;
+ arg->bundle->argv[1] = arg->bundle->argv1;
+ 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);
+
+ ptr = title;
+ remaining = sizeof title - 1;
+ for (f = 0; f < argc && remaining; f++) {
+ if (f) {
+ *ptr++ = ' ';
+ remaining--;
+ }
+ len = strlen(argv[f]);
+ if (len > remaining)
+ len = remaining;
+ memcpy(ptr, argv[f], len);
+ remaining -= len;
+ ptr += len;
+ }
+ *ptr = '\0';
+
+ arg->bundle->argv[0] = title;
+ arg->bundle->argv[1] = NULL;
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/command.h b/usr.sbin/ppp/command.h
new file mode 100644
index 0000000..53934c7
--- /dev/null
+++ b/usr.sbin/ppp/command.h
@@ -0,0 +1,65 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: command.h,v 1.15 1998/06/15 19:06:06 brian Exp $
+ *
+ * TODO:
+ */
+
+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 const char VersionDate[];
+
+extern void command_Expand(char **, int, char const *const *, struct bundle *,
+ 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 void 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..ea1a3d8
--- /dev/null
+++ b/usr.sbin/ppp/datalink.c
@@ -0,0 +1,1368 @@
+/*-
+ * 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.
+ *
+ * $Id: datalink.c,v 1.36 1999/04/05 21:52:10 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/uio.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "async.h"
+#include "throughput.h"
+#include "ccp.h"
+#include "link.h"
+#include "physical.h"
+#include "iplist.h"
+#include "slcompress.h"
+#include "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "chat.h"
+#include "auth.h"
+#include "modem.h"
+#include "prompt.h"
+#include "lcpproto.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(LogPHASE, "%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) {
+ if (Timeout > 0)
+ dl->dial.timer.load = Timeout * SECTICKS;
+ else {
+ result = (random() % DIAL_TIMEOUT) + 1;
+ dl->dial.timer.load = result * SECTICKS;
+ }
+ 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, Timeout);
+ }
+ return result;
+}
+
+static void
+datalink_HangupDone(struct datalink *dl)
+{
+ if (dl->physical->type == PHYS_DEDICATED && !dl->bundle->CleaningUp &&
+ physical_GetFD(dl->physical) != -1) {
+ /* Don't close our modem if the link is dedicated */
+ datalink_LoginDone(dl);
+ return;
+ }
+
+ modem_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_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)
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ } else {
+ datalink_NewState(dl, DATALINK_OPENING);
+ if (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--;
+ } else {
+ if (dl->phone.next == NULL)
+ datalink_StartDialTimer(dl, datalink_GetDialTimeout(dl));
+ else
+ datalink_StartDialTimer(dl, dl->cfg.dial.next_timeout);
+ }
+ }
+}
+
+static 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';
+ 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(LogPHASE, "Phone: %s\n", phone);
+ return phone;
+}
+
+static void
+datalink_LoginDone(struct datalink *dl)
+{
+ if (!dl->script.packetmode) {
+ dl->dial.tries = -1;
+ dl->dial.incs = 0;
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (modem_Raw(dl->physical, dl->bundle) < 0) {
+ dl->dial.tries = 0;
+ log_Printf(LogWARN, "datalink_LoginDone: Not connected.\n");
+ if (dl->script.run) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ modem_Offline(dl->physical);
+ chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
+ } else {
+ timer_Stop(&dl->physical->Timer);
+ if (dl->physical->type == PHYS_DEDICATED)
+ /* force a redial timeout */
+ modem_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_Init(&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 descriptor *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_DDIAL)) &&
+ !bundle_IsDead(dl->bundle))
+ /*
+ * Our first time in - DEDICATED & DDIAL never come down, and
+ * DIRECT & 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;
+ /* fall through */
+
+ case DATALINK_OPENING:
+ if (dl->dial.timer.state != TIMER_RUNNING) {
+ if (--dl->dial.tries < 0)
+ dl->dial.tries = 0;
+ if (modem_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);
+ chat_Init(&dl->chat, dl->physical, dl->cfg.script.dial, 1,
+ datalink_ChoosePhoneNumber(dl));
+ 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_LoginDone(dl);
+ 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 modem (attempt %u of %d)\n",
+ dl->cfg.dial.max - dl->dial.tries, dl->cfg.dial.max);
+ else
+ log_Printf(LogCHAT, "Failed to open modem\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));
+ log_WritePrompts(dl, "Failed to open %s, pause %d seconds\n",
+ dl->physical->name.full, timeout);
+ }
+ }
+ }
+ break;
+
+ case DATALINK_HANGUP:
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ result = descriptor_UpdateSet(&dl->chat.desc, r, w, e, n);
+ switch (dl->chat.state) {
+ case CHAT_DONE:
+ /* script succeeded */
+ chat_Destroy(&dl->chat);
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ datalink_NewState(dl, DATALINK_LOGIN);
+ chat_Init(&dl->chat, dl->physical, dl->cfg.script.login, 0, NULL);
+ 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");
+ chat_Destroy(&dl->chat);
+ switch(dl->state) {
+ case DATALINK_HANGUP:
+ datalink_HangupDone(dl);
+ break;
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ datalink_NewState(dl, DATALINK_HANGUP);
+ modem_Offline(dl->physical); /* Is this required ? */
+ chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
+ 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 descriptor *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_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 descriptor *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_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 descriptor *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_LOGIN:
+ result = descriptor_Write(&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))
+ result += descriptor_Write(&dl->chap.desc, bundle, fdset);
+ if (descriptor_IsSet(&dl->physical->desc, fdset))
+ result += descriptor_Write(&dl->physical->desc, bundle, fdset);
+ break;
+ }
+
+ return result;
+}
+
+static void
+datalink_ComeDown(struct datalink *dl, int how)
+{
+ if (how != CLOSE_NORMAL) {
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ if (dl->state >= DATALINK_READY && how == CLOSE_LCP)
+ dl->stayonline = 1;
+ }
+
+ if (dl->state >= DATALINK_READY && dl->stayonline) {
+ dl->stayonline = 0;
+ timer_Stop(&dl->physical->Timer);
+ datalink_NewState(dl, DATALINK_READY);
+ } else if (dl->state != DATALINK_CLOSED && dl->state != DATALINK_HANGUP) {
+ modem_Offline(dl->physical);
+ chat_Destroy(&dl->chat);
+ if (dl->script.run && dl->state != DATALINK_OPENING) {
+ datalink_NewState(dl, DATALINK_HANGUP);
+ chat_Init(&dl->chat, dl->physical, dl->cfg.script.hangup, 1, NULL);
+ } 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);
+ }
+}
+
+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);
+ /* fall through */
+ case MP_ADDED:
+ /* We're in multilink mode ! */
+ dl->physical->link.ccp.fsm.open_mode = OPEN_PASSIVE; /* override */
+ 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);
+ (*dl->parent->LayerUp)(dl->parent->object, &dl->physical->link.lcp.fsm);
+ return;
+ } else {
+ dl->bundle->ncp.mp.peer = dl->peer;
+ ipcp_SetLink(&dl->bundle->ncp.ipcp, &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);
+ /* fall through (just in case) */
+
+ case DATALINK_CBCP:
+ if (!dl->cbcp.required)
+ cbcp_Down(&dl->cbcp);
+ /* fall through (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.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 = modem_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);
+ chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
+
+ 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 = modem_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);
+ chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
+
+ 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_Destroy(&dl->chat); /* Gotta blat the timers ! */
+ break;
+ }
+ }
+
+ timer_Stop(&dl->dial.timer);
+ result = dl->next;
+ modem_Destroy(dl->physical);
+ free(dl->name);
+ free(dl);
+
+ return result;
+}
+
+void
+datalink_Up(struct datalink *dl, int runscripts, int packetmode)
+{
+ if (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;
+ /* fall through */
+
+ case DATALINK_DIAL:
+ case DATALINK_LOGIN:
+ case DATALINK_READY:
+ if (!dl->script.packetmode && packetmode) {
+ dl->script.packetmode = 1;
+ if (dl->state == DATALINK_READY)
+ datalink_LoginDone(dl);
+ }
+ 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);
+ /* fall through */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ datalink_AuthReInit(dl);
+ fsm_Close(&dl->physical->link.lcp.fsm);
+ if (how != CLOSE_NORMAL) {
+ dl->dial.tries = -1;
+ dl->reconnect_tries = 0;
+ if (how == CLOSE_LCP)
+ dl->stayonline = 1;
+ }
+ 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);
+ /* fall through */
+
+ case DATALINK_CBCP:
+ case DATALINK_AUTH:
+ case DATALINK_LCP:
+ fsm2initial(&dl->physical->link.lcp.fsm);
+ /* fall through */
+
+ default:
+ datalink_ComeDown(dl, how);
+ }
+}
+
+void
+datalink_StayDown(struct datalink *dl)
+{
+ dl->reconnect_tries = 0;
+}
+
+void
+datalink_DontHangup(struct datalink *dl)
+{
+ if (dl->state >= DATALINK_LCP)
+ dl->stayonline = 1;
+}
+
+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, " 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 *states[] = {
+ "closed",
+ "opening",
+ "hangup",
+ "dial",
+ "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)
+{
+ 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 = iov2modem(dl, iov, niov, maxiov, fd);
+
+ 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);
+ chat_Init(&dl->chat, dl->physical, NULL, 1, NULL);
+
+ 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,
+ pid_t newpid)
+{
+ /* 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 = dl ? dl : malloc(sizeof *dl);
+ iov[(*niov)++].iov_len = sizeof *dl;
+ iov[*niov].iov_base =
+ dl ? realloc(dl->name, DATALINK_MAXNAME) : malloc(DATALINK_MAXNAME);
+ iov[(*niov)++].iov_len = DATALINK_MAXNAME;
+
+ link_fd = modem2iov(dl ? dl->physical : NULL, iov, niov, maxiov, newpid);
+
+ 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) && 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..edec3b4
--- /dev/null
+++ b/usr.sbin/ppp/datalink.h
@@ -0,0 +1,151 @@
+/*-
+ * 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.
+ *
+ * $Id: datalink.h,v 1.6 1999/02/06 02:54:45 brian Exp $
+ */
+
+#define DATALINK_CLOSED (0)
+#define DATALINK_OPENING (1)
+#define DATALINK_HANGUP (2)
+#define DATALINK_DIAL (3)
+#define DATALINK_LOGIN (4)
+#define DATALINK_READY (5)
+#define DATALINK_LCP (6)
+#define DATALINK_AUTH (7)
+#define DATALINK_CBCP (8)
+#define DATALINK_OPEN (9)
+
+#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 descriptor 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]; /* dial */
+ char login[SCRIPT_LEN]; /* login */
+ char hangup[SCRIPT_LEN]; /* hangup */
+ } script;
+ 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);
+extern int datalink2iov(struct datalink *, struct iovec *, int *, int, pid_t);
+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 *);
diff --git a/usr.sbin/ppp/deflate.c b/usr.sbin/ppp/deflate.c
new file mode 100644
index 0000000..d19d93c
--- /dev/null
+++ b/usr.sbin/ppp/deflate.c
@@ -0,0 +1,593 @@
+/*-
+ * 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.
+ *
+ * $Id: deflate.c,v 1.11 1998/08/07 18:42:48 brian Exp $
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "fsm.h"
+#include "lcp.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 1024 /* Allocate mbufs this size */
+
+static void
+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");
+}
+
+static int
+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 = mbuf_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 = mbuf_Alloc(2, MB_HDLCOUT);
+ mi->next = mp;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[0] = proto & 0377;
+ mi->cnt = 1;
+ } else { /* Don't compress the protocol */
+ rp[0] = proto >> 8;
+ rp[1] = proto & 0377;
+ mi->cnt = 2;
+ }
+
+ /* Allocate the initial output mbuf */
+ mo_head = mo = mbuf_Alloc(DEFLATE_CHUNK_LEN, MB_HDLCOUT);
+ mo->cnt = 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->cnt;
+ 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 : "");
+ mbuf_Free(mo_head);
+ mbuf_FreeSeg(mi_head);
+ state->seqno--;
+ return 1; /* packet dropped */
+ }
+
+ if (flush == Z_SYNC_FLUSH && state->cx.avail_out != 0)
+ break;
+
+ if (state->cx.avail_in == 0 && mi->next != NULL) {
+ mi = mi->next;
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->cnt;
+ if (mi->next == NULL)
+ flush = Z_SYNC_FLUSH;
+ }
+
+ if (state->cx.avail_out == 0) {
+ mo->next = mbuf_Alloc(DEFLATE_CHUNK_LEN, MB_HDLCOUT);
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN);
+ mo = mo->next;
+ mo->cnt = 0;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+
+ olen += (mo->cnt = 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) {
+ mbuf_Free(mo_head);
+ mbuf_FreeSeg(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 0;
+ }
+
+ mbuf_Free(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.
+ */
+ for (mo = mo_head, len = mo->cnt; len < olen; mo = mo->next, len += mo->cnt)
+ ;
+ mo->cnt -= len - olen;
+ if (mo->next != NULL) {
+ mbuf_Free(mo->next);
+ mo->next = NULL;
+ }
+
+ ccp->uncompout += ilen;
+ ccp->compout += olen;
+
+ log_Printf(LogDEBUG, "DeflateOutput: %d => %d bytes, proto 0x%04x\n",
+ ilen, olen, proto);
+
+ hdlc_Output(l, PRI_NORMAL, ccp_Proto(ccp), mo_head);
+ return 1;
+}
+
+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);
+ mbuf_Free(mi_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+ }
+ state->seqno++;
+ state->uncomp_rec = 0;
+
+ /* Allocate an output mbuf */
+ mo_head = mo = mbuf_Alloc(DEFLATE_CHUNK_LEN, MB_IPIN);
+
+ /* 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->cnt;
+ state->cx.next_out = wp + 1;
+ state->cx.avail_out = 1;
+ ilen += mi->cnt;
+
+ flush = mi->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 : "");
+ mbuf_Free(mo_head);
+ mbuf_Free(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 = mbuf_FreeSeg(mi)) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ ilen += (state->cx.avail_in = mi->cnt);
+ if (mi->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->cnt = DEFLATE_CHUNK_LEN);
+ mo->next = mbuf_Alloc(DEFLATE_CHUNK_LEN, MB_IPIN);
+ mo = mo->next;
+ state->cx.next_out = MBUF_CTOP(mo);
+ state->cx.avail_out = DEFLATE_CHUNK_LEN;
+ }
+ }
+ }
+
+ if (mi != NULL)
+ mbuf_Free(mi);
+
+ if (first) {
+ log_Printf(LogCCP, "DeflateInput: Length error\n");
+ mbuf_Free(mo_head);
+ ccp_SendResetReq(&ccp->fsm);
+ return NULL;
+ }
+
+ olen += (mo->cnt = DEFLATE_CHUNK_LEN - state->cx.avail_out);
+
+ *proto = ((u_short)wp[0] << 8) | wp[1];
+ mo_head->offset += 2;
+ mo_head->cnt -= 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 = mbuf_Alloc(7, MB_HDLCOUT);
+ mi_head->next = mi;
+ len = mbuf_Length(mi);
+ mi = mi_head;
+ rp = MBUF_CTOP(mi);
+ if (proto < 0x100) { /* Compress the protocol */
+ rp[5] = proto & 0377;
+ mi->cnt = 6;
+ len++;
+ } else { /* Don't compress the protocol */
+ rp[5] = proto >> 8;
+ rp[6] = proto & 0377;
+ mi->cnt = 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->cnt;
+ 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);
+ mbuf_FreeSeg(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->next) != NULL) {
+ /* underflow */
+ state->cx.next_in = MBUF_CTOP(mi);
+ state->cx.avail_in = mi->cnt;
+ if (mi->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++;
+ mbuf_FreeSeg(mi_head); /* lose our allocated ``head'' buf */
+}
+
+static const char *
+DeflateDispOpts(struct lcp_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 lcp_opt *o, const struct ccp_config *cfg)
+{
+ o->len = 4;
+ o->data[0] = ((cfg->deflate.out.winsize - 8) << 4) + 8;
+ o->data[1] = '\0';
+}
+
+static int
+DeflateSetOptsOutput(struct lcp_opt *o)
+{
+ if (o->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 lcp_opt *o, const struct ccp_config *cfg)
+{
+ int want;
+
+ if (o->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 lcp_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 lcp_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, /* pppd (wrongly) expects this ``type'' field */
+ CCP_NEG_DEFLATE24,
+ DeflateDispOpts,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ DeflateInitOptsOutput,
+ DeflateSetOptsOutput,
+ DeflateInitOutput,
+ DeflateTermOutput,
+ DeflateResetOutput,
+ DeflateOutput
+ },
+};
+
+const struct ccp_algorithm DeflateAlgorithm = {
+ TY_DEFLATE, /* rfc 1979 */
+ CCP_NEG_DEFLATE,
+ DeflateDispOpts,
+ {
+ DeflateSetOptsInput,
+ DeflateInitInput,
+ DeflateTermInput,
+ DeflateResetInput,
+ DeflateInput,
+ DeflateDictSetup
+ },
+ {
+ 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..ed36c87
--- /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.
+ *
+ * $Id$
+ */
+
+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..0e1ec62
--- /dev/null
+++ b/usr.sbin/ppp/defs.c
@@ -0,0 +1,153 @@
+/*-
+ * 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.
+ *
+ * $Id: defs.c,v 1.17 1998/06/27 14:18:05 brian Exp $
+ */
+
+
+#include <sys/types.h>
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#if !defined(__FreeBSD__) || __FreeBSD__ < 3
+#include <time.h>
+#endif
+#include <unistd.h>
+
+#include "defs.h"
+
+void
+randinit()
+{
+#if __FreeBSD__ >= 3
+ static int initdone; /* srandomdev() call is only required once */
+
+ if (!initdone) {
+ initdone = 1;
+ srandomdev();
+ }
+#else
+ srandom((time(NULL)^getpid())+random());
+#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_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;
+}
diff --git a/usr.sbin/ppp/defs.h b/usr.sbin/ppp/defs.h
new file mode 100644
index 0000000..48f0677
--- /dev/null
+++ b/usr.sbin/ppp/defs.h
@@ -0,0 +1,97 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: defs.h,v 1.40 1999/02/25 20:05:55 brian Exp $
+ *
+ * TODO:
+ */
+
+/* Check the following definitions for your machine environment */
+#ifdef __FreeBSD__
+# define MODEM_LIST "/dev/cuaa1, /dev/cuaa0" /* name of tty device */
+#else
+# ifdef __OpenBSD__
+# define MODEM_LIST "/dev/cua01, /dev/cua00" /* name of tty device */
+# else
+# define MODEM_LIST "/dev/tty01, /dev/tty00" /* name of tty device */
+# endif
+#endif
+
+#define _PATH_PPP "/etc/ppp"
+
+#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 scripts */
+#define LINE_LEN SCRIPT_LEN /* Size of login scripts */
+#define AUTHLEN 100 /* Size of authname/authkey */
+#define CHAPDIGESTLEN 100 /* Maximum chap digest */
+#define CHAPCHALLENGELEN 48 /* Maximum chap challenge */
+#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 LINK_MINWEIGHT 20
+#define MIN_LQRPERIOD 2 /* Minimum LQR frequency */
+#define DEF_LQRPERIOD 30 /* Default LQR frequency */
+#define MIN_FSMRETRY 3 /* 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_CDDELAY 1 /* Delay before checking for carrier */
+
+#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 10
+#define EX_TERM 11
+#define EX_NODIAL 12
+#define EX_NOLOGIN 13
+
+/* 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_ALL 63
+
+extern void randinit(void);
+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 *);
diff --git a/usr.sbin/ppp/descriptor.h b/usr.sbin/ppp/descriptor.h
new file mode 100644
index 0000000..70a16db
--- /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.
+ *
+ * $Id: descriptor.h,v 1.5 1999/02/06 02:54:45 brian Exp $
+ */
+
+#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 descriptor {
+ int type;
+
+ int (*UpdateSet)(struct descriptor *, fd_set *, fd_set *, fd_set *, int *);
+ int (*IsSet)(struct descriptor *, const fd_set *);
+ void (*Read)(struct descriptor *, struct bundle *, const fd_set *);
+ int (*Write)(struct descriptor *, 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/filter.c b/usr.sbin/ppp/filter.c
new file mode 100644
index 0000000..c9972fd
--- /dev/null
+++ b/usr.sbin/ppp/filter.c
@@ -0,0 +1,549 @@
+/*
+ * PPP Filter command Interface
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: filter.c,v 1.26 1998/10/22 02:32:48 brian Exp $
+ *
+ * TODO: Shoud send ICMP error message when we discard packets.
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <termios.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 "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+
+static int filter_Nam2Proto(int, char const *const *);
+static int filter_Nam2Op(const char *);
+
+static const u_int32_t netmasks[33] = {
+ 0x00000000,
+ 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000,
+ 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000,
+ 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000,
+ 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
+ 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
+ 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00,
+ 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0,
+ 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF,
+};
+
+struct in_addr
+bits2mask(int bits)
+{
+ struct in_addr result;
+
+ result.s_addr = htonl(netmasks[bits]);
+ return result;
+}
+
+int
+ParseAddr(struct ipcp *ipcp, const char *data,
+ struct in_addr *paddr, struct in_addr *pmask, int *pwidth)
+{
+ int bits, len;
+ char *wp;
+ const char *cp;
+
+ if (pmask)
+ pmask->s_addr = INADDR_BROADCAST; /* Assume 255.255.255.255 as default */
+
+ cp = pmask || pwidth ? strchr(data, '/') : NULL;
+ len = cp ? cp - data : strlen(data);
+
+ if (ipcp && strncasecmp(data, "HISADDR", len) == 0)
+ *paddr = ipcp->peer_ip;
+ else if (ipcp && strncasecmp(data, "MYADDR", len) == 0)
+ *paddr = ipcp->my_ip;
+ else if (len > 15)
+ log_Printf(LogWARN, "ParseAddr: %s: Bad address\n", data);
+ else {
+ char s[16];
+ strncpy(s, data, len);
+ s[len] = '\0';
+ if (inet_aton(s, paddr) == 0) {
+ log_Printf(LogWARN, "ParseAddr: %s: Bad address\n", s);
+ return (0);
+ }
+ }
+ if (cp && *++cp) {
+ bits = strtol(cp, &wp, 0);
+ if (cp == wp || bits < 0 || bits > 32) {
+ log_Printf(LogWARN, "ParseAddr: bad mask width.\n");
+ return (0);
+ }
+ } else if (paddr->s_addr == INADDR_ANY)
+ /* An IP of 0.0.0.0 without a width is anything */
+ bits = 0;
+ else
+ /* If a valid IP is given without a width, assume 32 bits */
+ bits = 32;
+
+ if (pwidth)
+ *pwidth = bits;
+
+ if (pmask) {
+ if (paddr->s_addr == INADDR_ANY)
+ pmask->s_addr = INADDR_ANY;
+ else
+ *pmask = bits2mask(bits);
+ }
+
+ return (1);
+}
+
+static int
+ParsePort(const char *service, int proto)
+{
+ const char *protocol_name;
+ char *cp;
+ struct servent *servent;
+ int port;
+
+ switch (proto) {
+ case P_UDP:
+ protocol_name = "udp";
+ break;
+ case P_TCP:
+ protocol_name = "tcp";
+ break;
+ default:
+ protocol_name = 0;
+ }
+
+ servent = getservbyname(service, protocol_name);
+ if (servent != 0)
+ return (ntohs(servent->s_port));
+
+ port = strtol(service, &cp, 0);
+ if (cp == service) {
+ log_Printf(LogWARN, "ParsePort: %s is not a port name or number.\n",
+ service);
+ return (0);
+ }
+ return (port);
+}
+
+/*
+ * ICMP Syntax: src eq icmp_message_type
+ */
+static int
+ParseIcmp(int argc, char const *const *argv, struct filterent *tgt)
+{
+ int type;
+ char *cp;
+
+ switch (argc) {
+ case 0:
+ /* permit/deny all ICMP types */
+ tgt->opt.srcop = 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->opt.srcop = OP_EQ;
+ tgt->opt.srcport = type;
+ }
+ 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, int proto,
+ struct filterent *tgt)
+{
+ tgt->opt.srcop = tgt->opt.dstop = OP_NONE;
+ tgt->opt.estab = tgt->opt.syn = tgt->opt.finrst = 0;
+
+ if (argc >= 3 && !strcmp(*argv, "src")) {
+ tgt->opt.srcop = filter_Nam2Op(argv[1]);
+ if (tgt->opt.srcop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operation\n");
+ return (0);
+ }
+ tgt->opt.srcport = ParsePort(argv[2], proto);
+ if (tgt->opt.srcport == 0)
+ return (0);
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (argc >= 3 && !strcmp(argv[0], "dst")) {
+ tgt->opt.dstop = filter_Nam2Op(argv[1]);
+ if (tgt->opt.dstop == OP_NONE) {
+ log_Printf(LogWARN, "ParseUdpOrTcp: bad operation\n");
+ return (0);
+ }
+ tgt->opt.dstport = ParsePort(argv[2], proto);
+ if (tgt->opt.dstport == 0)
+ return (0);
+ argc -= 3;
+ argv += 3;
+ }
+
+ if (proto == P_TCP) {
+ for (; argc > 0; argc--, argv++)
+ if (!strcmp(*argv, "estab"))
+ tgt->opt.estab = 1;
+ else if (!strcmp(*argv, "syn"))
+ tgt->opt.syn = 1;
+ else if (!strcmp(*argv, "finrst"))
+ tgt->opt.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
+Parse(struct ipcp *ipcp, int argc, char const *const *argv,
+ struct filterent *ofp)
+{
+ int action, proto;
+ int val;
+ char *wp;
+ struct filterent filterdata;
+
+ val = strtol(*argv, &wp, 0);
+ if (*argv == wp || val > MAXFILTERS) {
+ log_Printf(LogWARN, "Parse: invalid filter number.\n");
+ return (0);
+ }
+ if (val < 0) {
+ for (val = 0; val < MAXFILTERS; val++) {
+ ofp->action = A_NONE;
+ ofp++;
+ }
+ log_Printf(LogWARN, "Parse: filter cleared.\n");
+ return (1);
+ }
+ ofp += val;
+
+ if (--argc == 0) {
+ log_Printf(LogWARN, "Parse: missing action.\n");
+ return (0);
+ }
+ argv++;
+
+ proto = P_NONE;
+ memset(&filterdata, '\0', sizeof filterdata);
+
+ if (!strcmp(*argv, "permit")) {
+ action = A_PERMIT;
+ } else if (!strcmp(*argv, "deny")) {
+ action = A_DENY;
+ } else if (!strcmp(*argv, "clear")) {
+ ofp->action = A_NONE;
+ return (1);
+ } else {
+ log_Printf(LogWARN, "Parse: bad action: %s\n", *argv);
+ return (0);
+ }
+ filterdata.action = action;
+
+ argc--;
+ argv++;
+
+ if (argc && filterdata.action == A_DENY) {
+ if (!strcmp(*argv, "host")) {
+ filterdata.action |= A_UHOST;
+ argc--;
+ argv++;
+ } else if (!strcmp(*argv, "port")) {
+ filterdata.action |= A_UPORT;
+ argc--;
+ argv++;
+ }
+ }
+
+ proto = filter_Nam2Proto(argc, argv);
+ if (proto == P_NONE) {
+ if (!argc)
+ log_Printf(LogWARN, "Parse: address/mask is expected.\n");
+ else if (ParseAddr(ipcp, *argv, &filterdata.saddr, &filterdata.smask,
+ &filterdata.swidth)) {
+ argc--;
+ argv++;
+ proto = filter_Nam2Proto(argc, argv);
+ if (!argc)
+ log_Printf(LogWARN, "Parse: address/mask is expected.\n");
+ else if (proto == P_NONE) {
+ if (ParseAddr(ipcp, *argv, &filterdata.daddr, &filterdata.dmask,
+ &filterdata.dwidth)) {
+ argc--;
+ argv++;
+ }
+ proto = filter_Nam2Proto(argc, argv);
+ if (argc && proto != P_NONE) {
+ argc--;
+ argv++;
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+ } else {
+ log_Printf(LogWARN, "Parse: Address/protocol expected.\n");
+ return (0);
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+
+ val = 1;
+ filterdata.proto = proto;
+
+ switch (proto) {
+ case P_TCP:
+ val = ParseUdpOrTcp(argc, argv, P_TCP, &filterdata);
+ break;
+ case P_UDP:
+ val = ParseUdpOrTcp(argc, argv, P_UDP, &filterdata);
+ break;
+ case P_ICMP:
+ val = ParseIcmp(argc, argv, &filterdata);
+ break;
+ }
+
+ log_Printf(LogDEBUG, "Parse: Src: %s\n", inet_ntoa(filterdata.saddr));
+ log_Printf(LogDEBUG, "Parse: Src mask: %s\n", inet_ntoa(filterdata.smask));
+ log_Printf(LogDEBUG, "Parse: Dst: %s\n", inet_ntoa(filterdata.daddr));
+ log_Printf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(filterdata.dmask));
+ log_Printf(LogDEBUG, "Parse: Proto = %d\n", proto);
+
+ log_Printf(LogDEBUG, "Parse: src: %s (%d)\n",
+ filter_Op2Nam(filterdata.opt.srcop), filterdata.opt.srcport);
+ log_Printf(LogDEBUG, "Parse: dst: %s (%d)\n",
+ filter_Op2Nam(filterdata.opt.dstop), filterdata.opt.dstport);
+ log_Printf(LogDEBUG, "Parse: estab: %u\n", filterdata.opt.estab);
+ log_Printf(LogDEBUG, "Parse: syn: %u\n", filterdata.opt.syn);
+ log_Printf(LogDEBUG, "Parse: finrst: %u\n", filterdata.opt.finrst);
+
+ if (val)
+ *ofp = filterdata;
+ 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;
+ }
+
+ Parse(&arg->bundle->ncp.ipcp, arg->argc - arg->argn - 1,
+ arg->argv + arg->argn + 1, filter->rule);
+ return 0;
+}
+
+const char *
+filter_Action2Nam(int act)
+{
+ static const char *actname[] = { "none ", "permit ", "deny " };
+ return actname[act & (A_PERMIT|A_DENY)];
+}
+
+static void
+doShowFilter(struct filterent *fp, struct prompt *prompt)
+{
+ int n;
+
+ for (n = 0; n < MAXFILTERS; n++, fp++) {
+ if (fp->action != A_NONE) {
+ prompt_Printf(prompt, " %2d %s", n, filter_Action2Nam(fp->action));
+ if (fp->action & A_UHOST)
+ prompt_Printf(prompt, "host ");
+ else if (fp->action & A_UPORT)
+ prompt_Printf(prompt, "port ");
+ else
+ prompt_Printf(prompt, " ");
+ prompt_Printf(prompt, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
+ prompt_Printf(prompt, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
+ if (fp->proto) {
+ prompt_Printf(prompt, "%s", filter_Proto2Nam(fp->proto));
+
+ if (fp->opt.srcop)
+ prompt_Printf(prompt, " src %s %d", filter_Op2Nam(fp->opt.srcop),
+ fp->opt.srcport);
+ if (fp->opt.dstop)
+ prompt_Printf(prompt, " dst %s %d", filter_Op2Nam(fp->opt.dstop),
+ fp->opt.dstport);
+ if (fp->opt.estab)
+ prompt_Printf(prompt, " estab");
+ if (fp->opt.syn)
+ prompt_Printf(prompt, " syn");
+ if (fp->opt.finrst)
+ prompt_Printf(prompt, " finrst");
+ }
+ 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 *protoname[] = { "none", "tcp", "udp", "icmp" };
+
+const char *
+filter_Proto2Nam(int proto)
+{
+ if (proto >= sizeof protoname / sizeof protoname[0])
+ return "unknown";
+ return protoname[proto];
+}
+
+static int
+filter_Nam2Proto(int argc, char const *const *argv)
+{
+ int proto;
+
+ if (argc == 0)
+ proto = 0;
+ else
+ for (proto = sizeof protoname / sizeof protoname[0] - 1; proto; proto--)
+ if (!strcasecmp(*argv, protoname[proto]))
+ break;
+
+ return proto;
+}
+
+static const char *opname[] = {"none", "eq", "gt", "unknown", "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;
+}
diff --git a/usr.sbin/ppp/filter.h b/usr.sbin/ppp/filter.h
new file mode 100644
index 0000000..d0c5d2c
--- /dev/null
+++ b/usr.sbin/ppp/filter.h
@@ -0,0 +1,93 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: filter.h,v 1.13 1999/01/19 22:16:01 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Actions
+ */
+#define A_NONE 0
+#define A_PERMIT 1
+#define A_DENY 2
+#define A_MASK 3
+#define A_UHOST 4
+#define A_UPORT 8
+
+/*
+ * Known protocols
+ */
+#define P_NONE 0
+#define P_TCP 1
+#define P_UDP 2
+#define P_ICMP 3
+
+/*
+ * Operations
+ */
+#define OP_NONE 0
+#define OP_EQ 1
+#define OP_GT 2
+#define OP_LT 4
+
+struct filterent {
+ int action; /* Filtering action */
+ int swidth; /* Effective source address width */
+ struct in_addr saddr; /* Source address */
+ struct in_addr smask; /* Source address mask */
+ int dwidth; /* Effective destination address width */
+ struct in_addr daddr; /* Destination address */
+ struct in_addr dmask; /* Destination address mask */
+ int proto; /* Protocol */
+ struct {
+ short srcop;
+ u_short srcport;
+ short dstop;
+ u_short dstport;
+ unsigned estab : 1;
+ unsigned syn : 1;
+ unsigned finrst : 1;
+ } opt;
+};
+
+#define MAXFILTERS 40 /* in each filter set */
+
+struct filter {
+ struct filterent rule[MAXFILTERS]; /* incoming packet filter */
+ const char *name;
+ unsigned fragok : 1;
+ unsigned logok : 1;
+};
+
+#define FL_IN 0
+#define FL_OUT 1
+#define FL_DIAL 2
+#define FL_KEEP 3
+
+struct ipcp;
+struct cmdargs;
+
+extern int ParseAddr(struct ipcp *, const char *, struct in_addr *,
+ struct in_addr *, int *);
+extern int filter_Show(struct cmdargs const *);
+extern int filter_Set(struct cmdargs const *);
+extern const char * filter_Action2Nam(int);
+extern const char *filter_Proto2Nam(int);
+extern const char *filter_Op2Nam(int);
+extern struct in_addr bits2mask(int);
diff --git a/usr.sbin/ppp/fsm.c b/usr.sbin/ppp/fsm.c
new file mode 100644
index 0000000..db4be6d
--- /dev/null
+++ b/usr.sbin/ppp/fsm.c
@@ -0,0 +1,1050 @@
+/*
+ * PPP Finite State Machine for LCP/IPCP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.c,v 1.40 1999/03/01 02:52:39 brian Exp $
+ *
+ * TODO:
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.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 "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 "bundle.h"
+#include "async.h"
+#include "physical.h"
+#include "lcpproto.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, 0, "Ident" },
+ { FsmRecvTimeRemain,0, 0, "TimeRemain" },
+ { FsmRecvResetReq, 0, 0, "ResetReqt" },
+ { 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 *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 *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 = 1;
+ 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 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, count, MODE_NOP, NULL);
+ if (count < sizeof(struct fsmconfig))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+ break;
+ }
+ }
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = mbuf_Alloc(plen, MB_FSM);
+ 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);
+ hdlc_Output(fp->link, PRI_LINK, fp->proto, bp);
+}
+
+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);
+ 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);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmSendTerminateReq(struct fsm *fp)
+{
+ fsm_Output(fp, CODE_TERMREQ, fp->reqid, NULL, 0);
+ (*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;
+
+ plen = mbuf_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);
+ mbuf_Free(bp);
+ return;
+ }
+
+ /*
+ * 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. This is a bit smelly, we know that
+ * ``bp'' really has ``fsmheader'' in front of it, and CCP_PROTO
+ * in front of that. CCP_PROTO isn't compressed either 'cos it
+ * doesn't begin with 0x00....
+ */
+ bp->offset -= sizeof(struct fsmheader) + 2;
+ bp->cnt += sizeof(struct fsmheader) + 2;
+ lcp_SendProtoRej(&fp->link->lcp, MBUF_CTOP(bp), bp->cnt);
+ mbuf_Free(bp);
+ return;
+ }
+ /* Drop through */
+ case ST_STARTING:
+ log_Printf(fp->LogLevel, "%s: Oops, RCR in %s.\n",
+ fp->link->name, State2Nam(fp->state));
+ mbuf_Free(bp);
+ return;
+ case ST_CLOSED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ mbuf_Free(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:
+ mbuf_Free(bp);
+ return;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ (*fp->fn->DecodeConfig)(fp, MBUF_CTOP(bp), flen, MODE_REQ, &dec);
+ if (flen < sizeof(struct fsmconfig))
+ log_Printf(fp->LogLevel, " [EMPTY]\n");
+
+ if (dec.nakend == dec.nak && dec.rejend == dec.rej)
+ ackaction = 1;
+
+ switch (fp->state) {
+ case ST_STOPPED:
+ FsmInitRestartCounter(fp, FSM_REQ_TIMER);
+ /* Fall 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);
+ if (dec.nakend != dec.nak)
+ fsm_Output(fp, CODE_CONFIGNAK, lhp->id, dec.nak, dec.nakend - dec.nak);
+ if (ackaction)
+ fsm_Output(fp, CODE_CONFIGACK, lhp->id, dec.ack, dec.ackend - dec.ack);
+
+ 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);
+ /* Fall through */
+
+ case ST_OPENED:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ else
+ NewState(fp, ST_REQSENT);
+ 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);
+ }
+ }
+ break;
+ case ST_ACKSENT:
+ if (!ackaction)
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ mbuf_Free(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);
+ fsm_Close(fp);
+ }
+
+ if (dec.nakend != dec.nak && --fp->more.naks <= 0) {
+ fsm_Close(fp);
+ log_Printf(LogPHASE, "%s: Too many %s NAKs sent - abandoning negotiation\n",
+ fp->link->name, fp->name);
+ fsm_Close(fp);
+ }
+}
+
+static void
+FsmRecvConfigAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCA */
+{
+ 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);
+ }
+ break;
+ case ST_OPENED:
+ (*fp->fn->LayerDown)(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ (*fp->parent->LayerDown)(fp->parent->object, fp);
+ break;
+ }
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvConfigNak(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCN */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+
+ plen = mbuf_Length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ mbuf_Free(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));
+ mbuf_Free(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ mbuf_Free(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ mbuf_Free(bp);
+ return;
+ }
+
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ (*fp->fn->DecodeConfig)(fp, MBUF_CTOP(bp), flen, MODE_NAK, &dec);
+ if (flen < sizeof(struct fsmconfig))
+ 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;
+ }
+
+ mbuf_Free(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;
+ }
+ mbuf_Free(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;
+ }
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvConfigRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+/* RCJ */
+{
+ struct fsm_decode dec;
+ int plen, flen;
+
+ plen = mbuf_Length(bp);
+ flen = ntohs(lhp->length) - sizeof *lhp;
+ if (plen < flen) {
+ mbuf_Free(bp);
+ return;
+ }
+
+ /*
+ * 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));
+ mbuf_Free(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (*fp->fn->SendTerminateAck)(fp, lhp->id);
+ mbuf_Free(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ mbuf_Free(bp);
+ return;
+ }
+
+ dec.ackend = dec.ack;
+ dec.nakend = dec.nak;
+ dec.rejend = dec.rej;
+ (*fp->fn->DecodeConfig)(fp, MBUF_CTOP(bp), flen, MODE_REJ, &dec);
+ if (flen < sizeof(struct fsmconfig))
+ 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;
+ }
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvCodeRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvProtoRej(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ struct physical *p = link2physical(fp->link);
+ u_short *sp, proto;
+
+ bp = mbuf_Contiguous(bp);
+ sp = (u_short *)MBUF_CTOP(bp);
+ proto = ntohs(*sp);
+ 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);
+ default:
+ NewState(fp, ST_STOPPED);
+ break;
+ }
+ /* See above */
+ /* (*fp->parent->LayerFinish)(fp->parent->object, fp); */
+ }
+ break;
+ 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;
+ }
+ mbuf_Free(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;
+
+ if (lcp) {
+ cp = MBUF_CTOP(bp);
+ ua_ntohl(cp, &magic);
+ if (magic != lcp->his_magic) {
+ log_Printf(fp->LogLevel, "%s: RecvEchoReq: Error: His magic is bad!!\n",
+ fp->link->name);
+ /* 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, mbuf_Length(bp));
+ }
+ }
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvEchoRep(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ struct lcp *lcp = fsm2lcp(fp);
+ u_int32_t magic;
+
+ if (lcp) {
+ ua_ntohl(MBUF_CTOP(bp), &magic);
+ /* Tolerate echo replies with either magic number */
+ if (magic != 0 && magic != lcp->his_magic && magic != lcp->want_magic) {
+ log_Printf(LogWARN,
+ "%s: RecvEchoRep: Bad magic: expected 0x%08x, got: 0x%08x\n",
+ fp->link->name, lcp->his_magic, magic);
+ /*
+ * XXX: We should send terminate request. But poor implementations may
+ * die as a result.
+ */
+ }
+ lqr_RecvEcho(fp, bp);
+ }
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvDiscReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvIdent(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvTimeRemain(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvResetReq(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ (*fp->fn->RecvResetReq)(fp);
+ /*
+ * All sendable compressed packets are queued in the PRI_NORMAL 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);
+ mbuf_Free(bp);
+}
+
+static void
+FsmRecvResetAck(struct fsm *fp, struct fsmheader *lhp, struct mbuf *bp)
+{
+ (*fp->fn->RecvResetAck)(fp, lhp->id);
+ mbuf_Free(bp);
+}
+
+void
+fsm_Input(struct fsm *fp, struct mbuf *bp)
+{
+ int len;
+ struct fsmheader *lhp;
+ const struct fsmcodedesc *codep;
+
+ len = mbuf_Length(bp);
+ if (len < sizeof(struct fsmheader)) {
+ mbuf_Free(bp);
+ return;
+ }
+ lhp = (struct fsmheader *) MBUF_CTOP(bp);
+ if (lhp->code < fp->min_code || lhp->code > fp->max_code ||
+ lhp->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;
+
+ fsm_Output(fp, CODE_CODEREJ, id++, MBUF_CTOP(bp), bp->cnt);
+ mbuf_Free(bp);
+ return;
+ }
+ bp->offset += sizeof(struct fsmheader);
+ bp->cnt -= sizeof(struct fsmheader);
+
+ codep = FsmCodes + lhp->code - 1;
+ if (lhp->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, lhp->id, fp->reqid);
+ return;
+ }
+
+ log_Printf(fp->LogLevel, "%s: Recv%s(%d) state = %s\n",
+ fp->link->name, codep->name, lhp->id, State2Nam(fp->state));
+
+ if (log_IsKept(LogDEBUG))
+ mbuf_Log();
+
+ if (codep->inc_reqid && (lhp->id == fp->reqid ||
+ (!Enabled(fp->bundle, OPT_IDCHECK) && codep->check_reqid)))
+ fp->reqid++; /* That's the end of that ``exchange''.... */
+
+ (*codep->recv)(fp, lhp, bp);
+
+ if (log_IsKept(LogDEBUG))
+ mbuf_Log();
+}
+
+void
+fsm_NullRecvResetReq(struct fsm *fp)
+{
+ log_Printf(fp->LogLevel, "%s: Oops - received unexpected reset req\n",
+ fp->link->name);
+}
+
+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);
+}
diff --git a/usr.sbin/ppp/fsm.h b/usr.sbin/ppp/fsm.h
new file mode 100644
index 0000000..c25a6ba
--- /dev/null
+++ b/usr.sbin/ppp/fsm.h
@@ -0,0 +1,173 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h,v 1.19 1998/06/25 22:33:24 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * 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
+
+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[100], *ackend;
+ u_char nak[100], *nakend;
+ u_char rej[100], *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 up (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 *, int, int, struct fsm_decode *);
+ /* Deal with incoming data */
+ void (*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 */
+
+/* Minimum config req size. This struct is *only* used for it's size */
+struct fsmconfig {
+ u_char type;
+ u_char length;
+};
+
+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 *[3]);
+extern void fsm_Output(struct fsm *, u_int, u_int, u_char *, 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 void 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);
diff --git a/usr.sbin/ppp/hdlc.c b/usr.sbin/ppp/hdlc.c
new file mode 100644
index 0000000..2176b5f
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.c
@@ -0,0 +1,652 @@
+/*
+ * PPP High Level Link Control (HDLC) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: hdlc.c,v 1.40 1999/03/29 08:21:26 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcpproto.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "ipcp.h"
+#include "ip.h"
+#include "vjcomp.h"
+#include "auth.h"
+#include "pap.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"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.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.
+ */
+inline u_short
+hdlc_Fcs(u_short fcs, u_char * cp, int len)
+{
+ 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 = mbuf_Length(m);
+ pos = MBUF_CTOP(m);
+ end = pos + m->cnt;
+ while (len--) {
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *pos++) & 0xff];
+ if (pos == end && len) {
+ m = m->next;
+ pos = MBUF_CTOP(m);
+ end = pos + m->cnt;
+ }
+ }
+ return (fcs);
+}
+
+void
+hdlc_Output(struct link *l, int pri, u_short proto, struct mbuf *bp)
+{
+ struct physical *p = link2physical(l);
+ struct mbuf *mhp, *mfcs;
+ u_char *cp;
+ u_short fcs;
+
+ if (!p || physical_IsSync(p))
+ mfcs = NULL;
+ else
+ mfcs = mbuf_Alloc(2, MB_HDLCOUT);
+
+ mhp = mbuf_Alloc(4, MB_HDLCOUT);
+ mhp->cnt = 0;
+ cp = MBUF_CTOP(mhp);
+ if (p && (proto == PROTO_LCP || l->lcp.his_acfcomp == 0)) {
+ *cp++ = HDLC_ADDR;
+ *cp++ = HDLC_UI;
+ mhp->cnt += 2;
+ }
+
+ /*
+ * If possible, compress protocol field.
+ */
+ if (l->lcp.his_protocomp && (proto & 0xff00) == 0) {
+ *cp++ = proto;
+ mhp->cnt++;
+ } else {
+ *cp++ = proto >> 8;
+ *cp = proto & 0377;
+ mhp->cnt += 2;
+ }
+
+ mhp->next = bp = mbuf_Contiguous(bp);
+
+ if (!p) {
+ /*
+ * This is where we multiplex the data over our available physical
+ * links. We don't frame our logical link data. Instead we wait
+ * for the logical link implementation to chop our data up and pile
+ * it into the physical links by re-calling this function with the
+ * encapsulated fragments.
+ */
+ link_Output(l, pri, mhp);
+ return;
+ }
+
+ bp->next = mfcs; /* Tack mfcs onto the end */
+
+ p->hdlc.lqm.OutOctets += mbuf_Length(mhp) + 1;
+ p->hdlc.lqm.OutPackets++;
+
+ if (proto == PROTO_LQR) {
+ /* Overwrite the entire packet */
+ 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));
+ }
+
+ if (mfcs) {
+ mfcs->cnt = 0;
+ fcs = HdlcFcsBuf(INITFCS, mhp);
+ fcs = ~fcs;
+ cp = MBUF_CTOP(mfcs);
+ *cp++ = fcs & 0377; /* Low byte first!! */
+ *cp++ = fcs >> 8;
+ mfcs->cnt = 2;
+ }
+
+ log_DumpBp(LogHDLC, "hdlc_Output", mhp);
+
+ link_ProtocolRecord(l, proto, PROTO_OUT);
+ log_Printf(LogDEBUG, "hdlc_Output: proto = 0x%04x\n", proto);
+
+ if (physical_IsSync(p))
+ link_Output(l, pri, mhp); /* Send it raw */
+ else
+ async_Output(pri, mhp, proto, p);
+}
+
+/* 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" },
+ { 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" },
+ { 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";
+}
+
+void
+hdlc_DecodePacket(struct bundle *bundle, u_short proto, struct mbuf * bp,
+ struct link *l)
+{
+ struct physical *p = link2physical(l);
+ u_char *cp;
+ const char *type;
+
+ log_Printf(LogDEBUG, "DecodePacket: proto = 0x%04x\n", proto);
+
+ /* decompress everything. CCP needs uncompressed data too */
+ if ((bp = ccp_Decompress(&l->ccp, &proto, bp)) == NULL)
+ return;
+
+ switch (proto) {
+ case PROTO_LCP:
+ lcp_Input(&l->lcp, bp);
+ break;
+ case PROTO_PAP:
+ if (p)
+ pap_Input(p, bp);
+ else {
+ log_Printf(LogERROR, "DecodePacket: PAP: Not a physical link !\n");
+ mbuf_Free(bp);
+ }
+ break;
+ case PROTO_CBCP:
+ if (p)
+ cbcp_Input(p, bp);
+ else {
+ log_Printf(LogERROR, "DecodePacket: CBCP: Not a physical link !\n");
+ mbuf_Free(bp);
+ }
+ break;
+ case PROTO_LQR:
+ if (p) {
+ p->hdlc.lqm.lqr.SaveInLQRs++;
+ lqr_Input(p, bp);
+ } else {
+ log_Printf(LogERROR, "DecodePacket: LQR: Not a physical link !\n");
+ mbuf_Free(bp);
+ }
+ break;
+ case PROTO_CHAP:
+ if (p)
+ chap_Input(p, bp);
+ else {
+ log_Printf(LogERROR, "DecodePacket: CHAP: Not a physical link !\n");
+ mbuf_Free(bp);
+ }
+ break;
+ case PROTO_VJUNCOMP:
+ case PROTO_VJCOMP:
+ bp = vj_Input(&bundle->ncp.ipcp, bp, proto);
+ if (bp == NULL)
+ break;
+ /* fall down */
+ case PROTO_IP:
+ ip_Input(bundle, bp);
+ break;
+ case PROTO_IPCP:
+ ipcp_Input(&bundle->ncp.ipcp, bundle, bp);
+ break;
+ case PROTO_CCP:
+ ccp_Input(&l->ccp, bundle, bp);
+ break;
+ case PROTO_MP:
+ if (bundle->ncp.mp.active) {
+ if (p)
+ mp_Input(&bundle->ncp.mp, bp, p);
+ else {
+ log_Printf(LogWARN, "DecodePacket: Can't do MP inside MP !\n");
+ mbuf_Free(bp);
+ }
+ break;
+ }
+ /* Fall through */
+ default:
+ switch (proto) {
+ case PROTO_MP:
+ case PROTO_COMPD:
+ case PROTO_ICOMPD:
+ type = "Unexpected";
+ break;
+ default:
+ type = "Unknown";
+ break;
+ }
+ log_Printf(LogPHASE, "%s protocol 0x%04x (%s)\n", type, proto,
+ hdlc_Protocol2Nam(proto));
+ bp->offset -= 2;
+ bp->cnt += 2;
+ cp = MBUF_CTOP(bp);
+ lcp_SendProtoRej(&l->lcp, cp, bp->cnt);
+ if (p) {
+ p->hdlc.lqm.SaveInDiscards++;
+ p->hdlc.stats.unknownproto++;
+ }
+ mbuf_Free(bp);
+ break;
+ }
+}
+
+static int
+hdlc_GetProto(const u_char *cp, u_short *proto)
+{
+ *proto = *cp;
+ if (!(*proto & 1)) {
+ *proto = (*proto << 8) | cp[1];
+ return 2;
+ }
+ return 1;
+}
+
+void
+hdlc_Input(struct bundle *bundle, struct mbuf * bp, struct physical *physical)
+{
+ u_short fcs, proto;
+ u_char *cp, addr, ctrl;
+ int n;
+
+ log_DumpBp(LogHDLC, "hdlc_Input:", bp);
+ if (physical_IsSync(physical))
+ fcs = GOODFCS;
+ else
+ fcs = hdlc_Fcs(INITFCS, MBUF_CTOP(bp), bp->cnt);
+ physical->hdlc.lqm.SaveInOctets += bp->cnt + 1;
+
+ log_Printf(LogDEBUG, "%s: hdlc_Input: fcs = %04x (%s)\n",
+ physical->link.name, fcs, (fcs == GOODFCS) ? "good" : "BAD!");
+ if (fcs != GOODFCS) {
+ physical->hdlc.lqm.SaveInErrors++;
+ physical->hdlc.stats.badfcs++;
+ mbuf_Free(bp);
+ return;
+ }
+ if (!physical_IsSync(physical))
+ bp->cnt -= 2; /* discard FCS part */
+
+ if (bp->cnt < 2) { /* XXX: raise this bar ? */
+ mbuf_Free(bp);
+ return;
+ }
+ cp = MBUF_CTOP(bp);
+
+ if (!physical->link.lcp.want_acfcomp) {
+ /* We expect the packet not to be compressed */
+ addr = *cp++;
+ if (addr != HDLC_ADDR) {
+ physical->hdlc.lqm.SaveInErrors++;
+ physical->hdlc.stats.badaddr++;
+ log_Printf(LogDEBUG, "hdlc_Input: addr %02x\n", *cp);
+ mbuf_Free(bp);
+ return;
+ }
+ ctrl = *cp++;
+ if (ctrl != HDLC_UI) {
+ physical->hdlc.lqm.SaveInErrors++;
+ physical->hdlc.stats.badcommand++;
+ log_Printf(LogDEBUG, "hdlc_Input: %02x\n", *cp);
+ mbuf_Free(bp);
+ return;
+ }
+ bp->offset += 2;
+ bp->cnt -= 2;
+ } else if (cp[0] == HDLC_ADDR && cp[1] == HDLC_UI) {
+ /*
+ * We can receive compressed packets, but the peer still sends
+ * uncompressed packets !
+ */
+ cp += 2;
+ bp->offset += 2;
+ bp->cnt -= 2;
+ }
+
+ n = hdlc_GetProto(cp, &proto);
+ bp->offset += n;
+ bp->cnt -= n;
+ if (!physical->link.lcp.want_protocomp && n == 1)
+ log_Printf(LogHDLC, "%s: Warning: received a proto-compressed packet !\n",
+ physical->link.name);
+
+ link_ProtocolRecord(&physical->link, proto, PROTO_IN);
+ physical->hdlc.lqm.SaveInPackets++;
+
+ hdlc_DecodePacket(bundle, proto, bp, &physical->link);
+}
+
+/*
+ * 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);
+}
diff --git a/usr.sbin/ppp/hdlc.h b/usr.sbin/ppp/hdlc.h
new file mode 100644
index 0000000..39d4388
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.h
@@ -0,0 +1,115 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: hdlc.h,v 1.15 1998/05/21 21:45:30 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * 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
+
+/*
+ * Output priority
+ */
+/* PRI_NORMAL and PRI_FAST have meaning only on the IP queue.
+ * All IP frames have the same priority once they are compressed.
+ * IP frames stay on the IP queue till they can be sent on the
+ * link. They are compressed at that time.
+*/
+#define PRI_NORMAL 0 /* Normal priority */
+#define PRI_FAST 1 /* Fast (interractive) */
+#define PRI_LINK 1 /* Urgent (LQR packets) */
+#define PRI_MAX 1
+
+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 void hdlc_Input(struct bundle *, struct mbuf *, struct physical *);
+extern void hdlc_Output(struct link *, int, u_short, struct mbuf *bp);
+extern u_short hdlc_Fcs(u_short, u_char *, int);
+extern int hdlc_Detect(u_char const **, int, int);
diff --git a/usr.sbin/ppp/id.c b/usr.sbin/ppp/id.c
new file mode 100644
index 0000000..a602ef6
--- /dev/null
+++ b/usr.sbin/ppp/id.c
@@ -0,0 +1,267 @@
+/*-
+ * 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.
+ *
+ * $Id: id.c,v 1.10 1998/05/29 18:33:08 brian Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <sysexits.h>
+#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\", %d)\n", ret, basettyname,
+ (int)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)
+{
+ struct utmp ut;
+
+ strncpy(ut.ut_line, device, sizeof ut.ut_line - 1);
+ ut.ut_line[sizeof ut.ut_line - 1] = '\0';
+
+ 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);
+ } else
+ log_Printf(LogERROR, "ID0logout: No longer logged in on %s\n", ut.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(%d, %d)\n", result, (int)pid, sig);
+ ID0setuser();
+ return result;
+}
diff --git a/usr.sbin/ppp/id.h b/usr.sbin/ppp/id.h
new file mode 100644
index 0000000..2fcf3c9
--- /dev/null
+++ b/usr.sbin/ppp/id.h
@@ -0,0 +1,47 @@
+/*-
+ * 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.
+ *
+ * $Id: id.h,v 1.6 1998/05/28 23:15:37 brian Exp $
+ */
+
+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 *);
+extern int ID0bind_un(int, const struct sockaddr_un *);
+extern int ID0connect_un(int, const struct sockaddr_un *);
+extern int ID0kill(pid_t, int);
diff --git a/usr.sbin/ppp/iface.c b/usr.sbin/ppp/iface.c
new file mode 100644
index 0000000..d3dfc89
--- /dev/null
+++ b/usr.sbin/ppp/iface.c
@@ -0,0 +1,484 @@
+/*-
+ * 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.
+ *
+ * $Id: iface.c,v 1.2 1998/10/26 19:07:36 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <unistd.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 "filter.h"
+#include "descriptor.h"
+#include "ipcp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "prompt.h"
+#include "iface.h"
+
+
+static int
+bitsinmask(struct in_addr mask)
+{
+ u_int32_t bitmask, maskaddr;
+ int bits;
+
+ bitmask = 0xffffffff;
+ maskaddr = ntohl(mask.s_addr);
+ for (bits = 32; bits >= 0; bits--) {
+ if (maskaddr == bitmask)
+ break;
+ bitmask &= ~(1 << (32 - bits));
+ }
+
+ return bits;
+}
+
+struct iface *
+iface_Create(const char *name)
+{
+ int mib[6], i, s;
+ size_t needed;
+ char *buf, *ptr, *end, *cp, *lim;
+ struct if_msghdr *ifm;
+ struct ifa_msghdr *ifam;
+ struct sockaddr_dl *dl;
+ struct rt_addrinfo rti;
+ struct iface *iface;
+ struct iface_addr *addr;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ fprintf(stderr, "iface_Create: socket(): %s\n", strerror(errno));
+ return NULL;
+ }
+
+ 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) {
+ fprintf(stderr, "clean: sysctl: estimate: %s\n",
+ strerror(errno));
+ close(s);
+ return NULL;
+ }
+
+ if ((buf = (char *)malloc(needed)) == NULL) {
+ close(s);
+ return NULL;
+ }
+
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ close(s);
+ return NULL;
+ }
+
+ ptr = buf;
+ end = buf + needed;
+ iface = NULL;
+
+ 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 (!strncmp(name, dl->sdl_data, dl->sdl_nlen)) {
+ 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->flags = ifm->ifm_flags;
+ iface->index = ifm->ifm_index;
+ iface->in_addrs = 0;
+ iface->in_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) /* Keep wading */
+ continue;
+
+ /* Found an address ! */
+
+ if (ifam->ifam_addrs & (1 << RTAX_IFA)) {
+ /* *And* it's configured ! */
+ rti.rti_addrs = ifam->ifam_addrs;
+ lim = (char *)ifam + ifam->ifam_msglen;
+ cp = (char *)(ifam + 1);
+ memset(rti.rti_info, '\0', sizeof(rti.rti_info));
+ for (i = 0; i < RTAX_MAX && cp < lim; i++) {
+ if ((rti.rti_addrs & (1 << i)) == 0)
+ continue;
+ rti.rti_info[i] = (struct sockaddr *)cp;
+#define ROUNDUP(x) \
+ ((x) > 0 ? (1 + (((x) - 1) | (sizeof(long) - 1))) : sizeof(long))
+ cp += ROUNDUP(rti.rti_info[i]->sa_len);
+ }
+
+ if (rti.rti_info[RTAX_IFA] &&
+ rti.rti_info[RTAX_IFA]->sa_family == AF_INET) {
+ /* Record the iface address rti */
+
+ addr = (struct iface_addr *)realloc
+ (iface->in_addr, (iface->in_addrs + 1) * sizeof iface->in_addr[0]);
+ if (addr == NULL)
+ break;
+ iface->in_addr = addr;
+
+ addr += iface->in_addrs;
+ iface->in_addrs++;
+
+ addr->ifa.s_addr = ((struct sockaddr_in *)rti.rti_info[RTAX_IFA])->
+ sin_addr.s_addr;
+ addr->brd.s_addr = rti.rti_info[RTAX_BRD] ?
+ ((struct sockaddr_in *)rti.rti_info[RTAX_BRD])->sin_addr.s_addr :
+ INADDR_ANY;
+ addr->mask.s_addr = rti.rti_info[RTAX_NETMASK] ?
+ ((struct sockaddr_in *)rti.rti_info[RTAX_NETMASK])->sin_addr.s_addr:
+ INADDR_ANY;
+
+ addr->bits = bitsinmask(addr->mask);
+ }
+ }
+ }
+ }
+
+ free(buf);
+ close(s);
+
+ return iface;
+}
+
+static void
+iface_addr_Zap(const char *name, struct iface_addr *addr)
+{
+ struct ifaliasreq ifra;
+ struct sockaddr_in *me, *peer;
+ int s;
+
+ s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ log_Printf(LogERROR, "iface_addr_Zap: socket(): %s\n", strerror(errno));
+ else {
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, name, sizeof ifra.ifra_name - 1);
+ me = (struct sockaddr_in *)&ifra.ifra_addr;
+ peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ me->sin_family = peer->sin_family = AF_INET;
+ me->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
+ me->sin_addr = addr->ifa;
+ peer->sin_addr = addr->brd;
+ log_Printf(LogDEBUG, "Delete %s\n", inet_ntoa(addr->ifa));
+ if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0)
+ log_Printf(LogWARN, "iface_addr_Zap: ioctl(SIOCDIFADDR, %s): %s\n",
+ inet_ntoa(addr->ifa), strerror(errno));
+ close(s);
+ }
+}
+
+void
+iface_inClear(struct iface *iface, int how)
+{
+ int n, addrs;
+
+ addrs = n = how == IFACE_CLEAR_ALL ? 0 : 1;
+ for (; n < iface->in_addrs; n++)
+ iface_addr_Zap(iface->name, iface->in_addr + n);
+
+ iface->in_addrs = addrs;
+ /* Don't bother realloc()ing - we have little to gain */
+}
+
+int
+iface_inAdd(struct iface *iface, struct in_addr ifa, struct in_addr mask,
+ struct in_addr brd, int how)
+{
+ int slot, s, chg;
+ struct ifaliasreq ifra;
+ struct sockaddr_in *me, *peer, *msk;
+ struct iface_addr *addr;
+
+ for (slot = 0; slot < iface->in_addrs; slot++)
+ if (iface->in_addr[slot].ifa.s_addr == ifa.s_addr) {
+ if (how & IFACE_FORCE_ADD)
+ break;
+ else
+ /* errno = EEXIST; */
+ return 0;
+ }
+
+ addr = (struct iface_addr *)realloc
+ (iface->in_addr, (iface->in_addrs + 1) * sizeof iface->in_addr[0]);
+ if (addr == NULL) {
+ log_Printf(LogERROR, "iface_inAdd: realloc: %s\n", strerror(errno));
+ return 0;
+ }
+ iface->in_addr = addr;
+
+ s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "iface_inAdd: socket(): %s\n", strerror(errno));
+ return 0;
+ }
+
+ /*
+ * We've gotta be careful here. If we try to add an address with the
+ * same destination as an existing interface, nothing will work.
+ * Instead, we tweak all previous address entries that match the
+ * to-be-added destination to 255.255.255.255 (w/ a similar netmask).
+ * There *may* be more than one - if the user has ``iface add''ed
+ * stuff previously.
+ */
+ for (chg = 0; chg < iface->in_addrs; chg++) {
+ if ((iface->in_addr[chg].brd.s_addr == brd.s_addr &&
+ brd.s_addr != INADDR_BROADCAST) || chg == slot) {
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, iface->name, sizeof ifra.ifra_name - 1);
+ me = (struct sockaddr_in *)&ifra.ifra_addr;
+ msk = (struct sockaddr_in *)&ifra.ifra_mask;
+ peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ me->sin_family = msk->sin_family = peer->sin_family = AF_INET;
+ me->sin_len = msk->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
+ me->sin_addr = iface->in_addr[chg].ifa;
+ msk->sin_addr = iface->in_addr[chg].mask;
+ peer->sin_addr = iface->in_addr[chg].brd;
+ log_Printf(LogDEBUG, "Delete %s\n", inet_ntoa(me->sin_addr));
+ ID0ioctl(s, SIOCDIFADDR, &ifra); /* Don't care if it fails... */
+ if (chg != slot) {
+ peer->sin_addr.s_addr = iface->in_addr[chg].brd.s_addr =
+ msk->sin_addr.s_addr = iface->in_addr[chg].mask.s_addr =
+ INADDR_BROADCAST;
+ iface->in_addr[chg].bits = 32;
+ log_Printf(LogDEBUG, "Add %s -> 255.255.255.255\n",
+ inet_ntoa(me->sin_addr));
+ if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0 && errno != EEXIST) {
+ /* Oops - that's bad(ish) news ! We've lost an alias ! */
+ log_Printf(LogERROR, "iface_inAdd: ioctl(SIOCAIFADDR): %s: %s\n",
+ inet_ntoa(me->sin_addr), strerror(errno));
+ iface->in_addrs--;
+ bcopy(iface->in_addr + chg + 1, iface->in_addr + chg,
+ (iface->in_addrs - chg) * sizeof iface->in_addr[0]);
+ if (slot > chg)
+ slot--;
+ chg--;
+ }
+ }
+ }
+ }
+
+ memset(&ifra, '\0', sizeof ifra);
+ strncpy(ifra.ifra_name, iface->name, sizeof ifra.ifra_name - 1);
+ me = (struct sockaddr_in *)&ifra.ifra_addr;
+ msk = (struct sockaddr_in *)&ifra.ifra_mask;
+ peer = (struct sockaddr_in *)&ifra.ifra_broadaddr;
+ me->sin_family = msk->sin_family = peer->sin_family = AF_INET;
+ me->sin_len = msk->sin_len = peer->sin_len = sizeof(struct sockaddr_in);
+ me->sin_addr = ifa;
+ msk->sin_addr = mask;
+ peer->sin_addr = brd;
+
+ if (log_IsKept(LogDEBUG)) {
+ char buf[16];
+
+ strncpy(buf, inet_ntoa(brd), sizeof buf-1);
+ buf[sizeof buf - 1] = '\0';
+ log_Printf(LogDEBUG, "Add %s -> %s\n", inet_ntoa(ifa), buf);
+ }
+
+ /* An EEXIST failure w/ brd == INADDR_BROADCAST is ok (and works!) */
+ if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0 &&
+ (brd.s_addr != INADDR_BROADCAST || errno != EEXIST)) {
+ log_Printf(LogERROR, "iface_inAdd: ioctl(SIOCAIFADDR): %s: %s\n",
+ inet_ntoa(ifa), strerror(errno));
+ ID0ioctl(s, SIOCDIFADDR, &ifra); /* EEXIST ? */
+ close(s);
+ return 0;
+ }
+ close(s);
+
+ if (slot == iface->in_addrs) {
+ /* We're adding a new interface address */
+
+ if (how & IFACE_ADD_FIRST) {
+ /* Stuff it at the start of our list */
+ slot = 0;
+ bcopy(iface->in_addr, iface->in_addr + 1,
+ iface->in_addrs * sizeof iface->in_addr[0]);
+ }
+
+ iface->in_addrs++;
+ } else if (how & IFACE_ADD_FIRST) {
+ /* Shift it up to the first slot */
+ bcopy(iface->in_addr, iface->in_addr + 1, slot * sizeof iface->in_addr[0]);
+ slot = 0;
+ }
+
+ iface->in_addr[slot].ifa = ifa;
+ iface->in_addr[slot].mask = mask;
+ iface->in_addr[slot].brd = brd;
+ iface->in_addr[slot].bits = bitsinmask(iface->in_addr[slot].mask);
+
+ return 1;
+}
+
+int
+iface_inDelete(struct iface *iface, struct in_addr ip)
+{
+ int n;
+
+ for (n = 0; n < iface->in_addrs; n++)
+ if (iface->in_addr[n].ifa.s_addr == ip.s_addr) {
+ iface_addr_Zap(iface->name, iface->in_addr + n);
+ bcopy(iface->in_addr + n + 1, iface->in_addr + n,
+ (iface->in_addrs - n - 1) * sizeof iface->in_addr[0]);
+ iface->in_addrs--;
+ return 1;
+ }
+
+ return 0;
+}
+
+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->in_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 iface *iface = arg->bundle->iface, *current;
+ int f, flags;
+
+ 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) || (!if_flags[f].flag && flags)) {
+ prompt_Printf(arg->prompt, "%s%s", flags == iface->flags ? "" : ",",
+ if_flags[f].value);
+ flags &= ~if_flags[f].flag;
+ }
+ prompt_Printf(arg->prompt, "> has %d address%s:\n", iface->in_addrs,
+ iface->in_addrs == 1 ? "" : "es");
+
+ for (f = 0; f < iface->in_addrs; f++) {
+ prompt_Printf(arg->prompt, " %s", inet_ntoa(iface->in_addr[f].ifa));
+ if (iface->in_addr[f].bits >= 0)
+ prompt_Printf(arg->prompt, "/%d", iface->in_addr[f].bits);
+ if (iface->flags & IFF_POINTOPOINT)
+ prompt_Printf(arg->prompt, " -> %s", inet_ntoa(iface->in_addr[f].brd));
+ else if (iface->flags & IFF_BROADCAST)
+ prompt_Printf(arg->prompt, " broadcast %s",
+ inet_ntoa(iface->in_addr[f].brd));
+ if (iface->in_addr[f].bits < 0)
+ prompt_Printf(arg->prompt, " (mask %s)",
+ inet_ntoa(iface->in_addr[f].mask));
+ prompt_Printf(arg->prompt, "\n");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/iface.h b/usr.sbin/ppp/iface.h
new file mode 100644
index 0000000..f3b1dad
--- /dev/null
+++ b/usr.sbin/ppp/iface.h
@@ -0,0 +1,60 @@
+/*-
+ * 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.
+ *
+ * $Id:$
+ */
+
+struct iface_addr {
+ struct in_addr ifa; /* local address */
+ struct in_addr mask; /* netmask */
+ int bits; /* netmask bits - -1 if not contiguous */
+ struct in_addr brd; /* peer address */
+};
+
+struct iface {
+ char *name; /* Interface name (malloc'd) */
+ int index; /* Interface index */
+ int flags; /* Interface flags (IFF_*) */
+
+ int in_addrs; /* How many in_addr's */
+ struct iface_addr *in_addr; /* Array of addresses (malloc'd) */
+};
+
+#define IFACE_CLEAR_ALL 0 /* Nuke 'em all */
+#define IFACE_CLEAR_ALIASES 1 /* Leave the IPCP 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_Clear iface_inClear /* Same for now */
+
+extern struct iface *iface_Create(const char *name);
+extern void iface_inClear(struct iface *, int);
+extern int iface_inAdd(struct iface *, struct in_addr, struct in_addr,
+ struct in_addr, int);
+extern int iface_inDelete(struct iface *, struct in_addr);
+extern int iface_Show(struct cmdargs const *);
+extern void iface_Destroy(struct iface *);
diff --git a/usr.sbin/ppp/ip.c b/usr.sbin/ppp/ip.c
new file mode 100644
index 0000000..79ab1ca
--- /dev/null
+++ b/usr.sbin/ppp/ip.c
@@ -0,0 +1,570 @@
+/*
+ * PPP IP Protocol Interface
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ip.c,v 1.55 1999/01/28 01:56:32 brian Exp $
+ *
+ * TODO:
+ * o Return ICMP message for filterd packet
+ * and optionaly record it into log.
+ */
+#include <sys/param.h>
+#ifdef __OpenBSD__
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifndef NOALIAS
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+#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 "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 "bundle.h"
+#include "vjcomp.h"
+#include "tun.h"
+#include "ip.h"
+
+static const u_short interactive_ports[32] = {
+ 544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
+};
+
+#define INTERACTIVE(p) (interactive_ports[(p) & 0x1F] == (p))
+
+static const char *TcpFlags[] = { "FIN", "SYN", "RST", "PSH", "ACK", "URG" };
+
+static 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);
+ }
+}
+
+/*
+ * Check a packet against with defined filters
+ */
+static int
+FilterCheck(struct ip *pip, struct filter *filter)
+{
+ int gotinfo, cproto, estab, syn, finrst, n, len, didname;
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmp *ih;
+ char *ptop;
+ u_short sport, dport;
+ struct filterent *fp = filter->rule;
+ char dbuff[100];
+
+ if (fp->action) {
+ cproto = gotinfo = estab = syn = finrst = didname = 0;
+ sport = dport = 0;
+ for (n = 0; n < MAXFILTERS; n++) {
+ if (fp->action) {
+ /* permit fragments on in and out filter */
+ if (filter->fragok && (ntohs(pip->ip_off) & IP_OFFMASK) != 0)
+ return (A_PERMIT);
+
+ if (!didname)
+ log_Printf(LogDEBUG, "%s filter:\n", filter->name);
+ didname = 1;
+
+ if ((pip->ip_src.s_addr & fp->smask.s_addr) ==
+ (fp->saddr.s_addr & fp->smask.s_addr) &&
+ (pip->ip_dst.s_addr & fp->dmask.s_addr) ==
+ (fp->daddr.s_addr & fp->dmask.s_addr)) {
+ if (fp->proto) {
+ if (!gotinfo) {
+ ptop = (char *) pip + (pip->ip_hl << 2);
+
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ cproto = P_ICMP;
+ ih = (struct icmp *) ptop;
+ sport = ih->icmp_type;
+ estab = syn = finrst = -1;
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d", sport);
+ break;
+ case IPPROTO_UDP:
+ case IPPROTO_IGMP:
+ case IPPROTO_IPIP:
+ cproto = P_UDP;
+ uh = (struct udphdr *) ptop;
+ sport = ntohs(uh->uh_sport);
+ dport = ntohs(uh->uh_dport);
+ estab = syn = finrst = -1;
+ if (log_IsKept(LogDEBUG))
+ snprintf(dbuff, sizeof dbuff, "sport = %d, dport = %d",
+ sport, dport);
+ break;
+ case IPPROTO_TCP:
+ cproto = P_TCP;
+ th = (struct tcphdr *) ptop;
+ 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:
+ return (A_DENY); /* We'll block unknown type of packet */
+ }
+ 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",
+ filter_Proto2Nam(cproto), dbuff);
+ }
+ gotinfo = 1;
+ }
+ if (log_IsKept(LogDEBUG)) {
+ if (fp->opt.srcop != OP_NONE) {
+ snprintf(dbuff, sizeof dbuff, ", src %s %d",
+ filter_Op2Nam(fp->opt.srcop), fp->opt.srcport);
+ len = strlen(dbuff);
+ } else
+ len = 0;
+ if (fp->opt.dstop != OP_NONE) {
+ snprintf(dbuff + len, sizeof dbuff - len,
+ ", dst %s %d", filter_Op2Nam(fp->opt.dstop),
+ fp->opt.dstport);
+ } else if (!len)
+ *dbuff = '\0';
+
+ log_Printf(LogDEBUG, " rule = %d: Address match, "
+ "check against proto %s%s, action = %s\n",
+ n, filter_Proto2Nam(fp->proto),
+ dbuff, filter_Action2Nam(fp->action));
+ }
+
+ if (cproto == fp->proto) {
+ if ((fp->opt.srcop == OP_NONE ||
+ PortMatch(fp->opt.srcop, sport, fp->opt.srcport)) &&
+ (fp->opt.dstop == OP_NONE ||
+ PortMatch(fp->opt.dstop, dport, fp->opt.dstport)) &&
+ (fp->opt.estab == 0 || estab) &&
+ (fp->opt.syn == 0 || syn) &&
+ (fp->opt.finrst == 0 || finrst)) {
+ return (fp->action);
+ }
+ }
+ } else {
+ /* Address is mached. Make a decision. */
+ log_Printf(LogDEBUG, " rule = %d: Address match, action = %s\n", n,
+ filter_Action2Nam(fp->action));
+ return (fp->action);
+ }
+ } else
+ log_Printf(LogDEBUG, " rule = %d: Address mismatch\n", n);
+ }
+ fp++;
+ }
+ return (A_DENY); /* No rule is mached. Deny this packet */
+ }
+ return (A_PERMIT); /* No rule is given. Permit this packet */
+}
+
+#ifdef notdef
+static void
+IcmpError(struct ip * pip, int code)
+{
+ struct mbuf *bp;
+
+ if (pip->ip_p != IPPROTO_ICMP) {
+ bp = mbuf_Alloc(cnt, MB_IPIN);
+ memcpy(MBUF_CTOP(bp), ptr, cnt);
+ vj_SendFrame(bp);
+ ipcp_AddOutOctets(cnt);
+ }
+}
+#endif
+
+/*
+ * For debugging aid.
+ */
+int
+PacketCheck(struct bundle *bundle, char *cp, int nb, struct filter *filter)
+{
+ struct ip *pip;
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmp *icmph;
+ char *ptop;
+ int mask, len, n;
+ int pri = PRI_NORMAL;
+ int logit, loglen;
+ char logbuf[200];
+
+ logit = log_IsKept(LogTCPIP) && filter->logok;
+ loglen = 0;
+
+ pip = (struct ip *) cp;
+
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ", filter->name);
+ loglen += strlen(logbuf + loglen);
+ }
+ ptop = (cp + (pip->ip_hl << 2));
+
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ if (logit && loglen < sizeof logbuf) {
+ icmph = (struct icmp *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_UDP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (struct udphdr *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_IPIP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (struct udphdr *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IPIP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_IGMP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (struct udphdr *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "IGMP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_TCP:
+ th = (struct tcphdr *) ptop;
+ if (pip->ip_tos == IPTOS_LOWDELAY)
+ pri = PRI_FAST;
+ else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
+ if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
+ pri = PRI_FAST;
+ }
+ if (logit && loglen < sizeof logbuf) {
+ len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), 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) {
+ u_short *sp;
+
+ ptop += 20;
+ sp = (u_short *) ptop;
+ if (ntohs(sp[0]) == 0x0204) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " MSS = %d", ntohs(sp[1]));
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+ }
+ break;
+ }
+
+ if ((FilterCheck(pip, filter) & A_DENY)) {
+ if (logit)
+ log_Printf(LogTCPIP, "%s - BLOCKED\n", logbuf);
+#ifdef notdef
+ if (direction == 0)
+ IcmpError(pip, pri);
+#endif
+ return (-1);
+ } else {
+ /* Check Keep Alive filter */
+ if (logit) {
+ if (FilterCheck(pip, &bundle->filter.alive) & A_DENY)
+ log_Printf(LogTCPIP, "%s - NO KEEPALIVE\n", logbuf);
+ else
+ log_Printf(LogTCPIP, "%s\n", logbuf);
+ }
+ return (pri);
+ }
+}
+
+void
+ip_Input(struct bundle *bundle, struct mbuf * bp)
+{
+ u_char *cp;
+ struct mbuf *wp;
+ int nb, nw;
+ struct tun_data tun;
+ struct ip *pip = (struct ip *)tun.data;
+#ifndef NOALIAS
+ struct ip *piip = (struct ip *)((char *)pip + (pip->ip_hl << 2));
+#endif
+
+ tun_fill_header(tun, AF_INET);
+ cp = tun.data;
+ nb = 0;
+ for (wp = bp; wp; wp = wp->next) { /* Copy to contiguous region */
+ if (sizeof tun.data - (cp - tun.data) < wp->cnt) {
+ log_Printf(LogWARN, "ip_Input: Packet too large (%d) - dropped\n",
+ mbuf_Length(bp));
+ mbuf_Free(bp);
+ return;
+ }
+ memcpy(cp, MBUF_CTOP(wp), wp->cnt);
+ cp += wp->cnt;
+ nb += wp->cnt;
+ }
+
+#ifndef NOALIAS
+ if (bundle->AliasEnabled && pip->ip_p != IPPROTO_IGMP &&
+ (pip->ip_p != IPPROTO_IPIP || !IN_CLASSD(ntohl(piip->ip_dst.s_addr)))) {
+ struct tun_data *frag;
+ int iresult;
+ char *fptr;
+
+ iresult = PacketAliasIn(tun.data, sizeof tun.data);
+ nb = ntohs(((struct ip *) tun.data)->ip_len);
+
+ if (nb > MAX_MRU) {
+ log_Printf(LogWARN, "ip_Input: Problem with IP header length\n");
+ mbuf_Free(bp);
+ return;
+ }
+ if (iresult == PKT_ALIAS_OK
+ || iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
+ if (PacketCheck(bundle, tun.data, nb, &bundle->filter.in) < 0) {
+ mbuf_Free(bp);
+ return;
+ }
+
+ if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
+ bundle_StartIdleTimer(bundle);
+
+ ipcp_AddInOctets(&bundle->ncp.ipcp, nb);
+
+ nb = ntohs(((struct ip *) tun.data)->ip_len);
+ nb += sizeof tun - sizeof tun.data;
+ nw = write(bundle->dev.fd, &tun, nb);
+ if (nw != nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %s\n", nb,
+ strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %d\n", nb, nw);
+ }
+
+ if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
+ while ((fptr = PacketAliasGetFragment(tun.data)) != NULL) {
+ PacketAliasFragmentIn(tun.data, fptr);
+ nb = ntohs(((struct ip *) fptr)->ip_len);
+ frag = (struct tun_data *)
+ ((char *)fptr - sizeof tun + sizeof tun.data);
+ nb += sizeof tun - sizeof tun.data;
+ nw = write(bundle->dev.fd, frag, nb);
+ if (nw != nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %s\n", nb,
+ strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %d\n", nb, nw);
+ }
+ free(frag);
+ }
+ }
+ } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
+ nb = ntohs(((struct ip *) tun.data)->ip_len);
+ nb += sizeof tun - sizeof tun.data;
+ frag = (struct tun_data *)malloc(nb);
+ if (frag == NULL)
+ log_Printf(LogALERT, "ip_Input: Cannot allocate memory for fragment\n");
+ else {
+ tun_fill_header(*frag, AF_INET);
+ memcpy(frag->data, tun.data, nb - sizeof tun + sizeof tun.data);
+ PacketAliasSaveFragment(frag->data);
+ }
+ }
+ } else
+#endif /* #ifndef NOALIAS */
+ { /* no aliasing */
+ if (PacketCheck(bundle, tun.data, nb, &bundle->filter.in) < 0) {
+ mbuf_Free(bp);
+ return;
+ }
+
+ if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
+ bundle_StartIdleTimer(bundle);
+
+ ipcp_AddInOctets(&bundle->ncp.ipcp, nb);
+
+ nb += sizeof tun - sizeof tun.data;
+ nw = write(bundle->dev.fd, &tun, nb);
+ if (nw != nb) {
+ if (nw == -1)
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %s\n", nb, strerror(errno));
+ else
+ log_Printf(LogERROR, "ip_Input: wrote %d, got %d\n", nb, nw);
+ }
+ }
+ mbuf_Free(bp);
+}
+
+void
+ip_Enqueue(struct ipcp *ipcp, int pri, char *ptr, int count)
+{
+ struct mbuf *bp;
+
+ if (pri < 0 || pri > sizeof ipcp->Queue / sizeof ipcp->Queue[0])
+ log_Printf(LogERROR, "Can't store in ip queue %d\n", pri);
+ else {
+ bp = mbuf_Alloc(count, MB_IPQ);
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ mbuf_Enqueue(&ipcp->Queue[pri], bp);
+ }
+}
+
+void
+ip_DeleteQueue(struct ipcp *ipcp)
+{
+ struct mqueue *queue;
+
+ for (queue = ipcp->Queue; queue < ipcp->Queue + PRI_MAX; queue++)
+ while (queue->top)
+ mbuf_Free(mbuf_Dequeue(queue));
+}
+
+int
+ip_QueueLen(struct ipcp *ipcp)
+{
+ struct mqueue *queue;
+ int result = 0;
+
+ for (queue = ipcp->Queue; queue < ipcp->Queue + PRI_MAX; queue++)
+ result += queue->qlen;
+
+ return result;
+}
+
+int
+ip_FlushPacket(struct link *l, struct bundle *bundle)
+{
+ struct ipcp *ipcp = &bundle->ncp.ipcp;
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int cnt;
+
+ if (ipcp->fsm.state != ST_OPENED)
+ return 0;
+
+ for (queue = &ipcp->Queue[PRI_FAST]; queue >= ipcp->Queue; queue--)
+ if (queue->top) {
+ bp = mbuf_Contiguous(mbuf_Dequeue(queue));
+ if (bp) {
+ struct ip *pip = (struct ip *)MBUF_CTOP(bp);
+
+ cnt = mbuf_Length(bp);
+ if (!(FilterCheck(pip, &bundle->filter.alive) & A_DENY))
+ bundle_StartIdleTimer(bundle);
+ vj_SendFrame(l, bp, bundle);
+ ipcp_AddOutOctets(ipcp, cnt);
+ return 1;
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/ip.h b/usr.sbin/ppp/ip.h
new file mode 100644
index 0000000..20ce67d
--- /dev/null
+++ b/usr.sbin/ppp/ip.h
@@ -0,0 +1,34 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ip.h,v 1.10 1998/08/25 17:48:42 brian Exp $
+ *
+ */
+
+struct mbuf;
+struct filter;
+struct link;
+struct bundle;
+
+extern int ip_FlushPacket(struct link *, struct bundle *);
+extern int PacketCheck(struct bundle *, char *, int, struct filter *);
+extern void ip_Enqueue(struct ipcp *, int, char *, int);
+extern void ip_Input(struct bundle *, struct mbuf *);
+extern void ip_DeleteQueue(struct ipcp *);
+extern int ip_QueueLen(struct ipcp *);
diff --git a/usr.sbin/ppp/ipcp.c b/usr.sbin/ppp/ipcp.c
new file mode 100644
index 0000000..c309449
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.c
@@ -0,0 +1,1230 @@
+/*
+ * PPP IP Control Protocol (IPCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.c,v 1.72 1999/02/26 21:28:11 brian Exp $
+ *
+ * TODO:
+ * o More RFC1772 backward compatibility
+ */
+#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 <fcntl.h>
+#include <resolv.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NOALIAS
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+#include "ua.h"
+#include "defs.h"
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.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 "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 *, int, 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 *cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+ "???",
+ "IPADDRS", /* 1: IP-Addresses */ /* deprecated */
+ "COMPPROTO", /* 2: IP-Compression-Protocol */
+ "IPADDR", /* 3: IP-Address */
+};
+
+#define NCFTYPES (sizeof cftypes/sizeof cftypes[0])
+
+static const char *cftypes128[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+ "???",
+ "PRIDNS", /* 129: Primary DNS Server Address */
+ "PRINBNS", /* 130: Primary NBNS Server Address */
+ "SECDNS", /* 131: Secondary DNS Server Address */
+ "SECNBNS", /* 132: Secondary NBNS Server Address */
+};
+
+#define NCFTYPES128 (sizeof cftypes128/sizeof cftypes128[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);
+}
+
+static void
+getdns(struct ipcp *ipcp, struct in_addr addr[2])
+{
+ FILE *fp;
+
+ addr[0].s_addr = addr[1].s_addr = INADDR_ANY;
+ if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) {
+ char buf[LINE_LEN], *cp, *end;
+ int n;
+
+ n = 0;
+ buf[sizeof buf - 1] = '\0';
+ while (fgets(buf, sizeof buf - 1, fp)) {
+ if (!strncmp(buf, "nameserver", 10) && issep(buf[10])) {
+ for (cp = buf + 11; issep(*cp); cp++)
+ ;
+ for (end = cp; isip(*end); end++)
+ ;
+ *end = '\0';
+ if (inet_aton(cp, addr+n) && ++n == 2)
+ break;
+ }
+ }
+ if (n == 1)
+ addr[1] = addr[0];
+ fclose(fp);
+ }
+}
+
+static int
+setdns(struct ipcp *ipcp, struct in_addr addr[2])
+{
+ FILE *fp;
+ char wbuf[LINE_LEN + 54];
+ int wlen;
+
+ if (addr[0].s_addr == INADDR_ANY || addr[1].s_addr == INADDR_ANY) {
+ struct in_addr old[2];
+
+ getdns(ipcp, old);
+ if (addr[0].s_addr == INADDR_ANY)
+ addr[0] = old[0];
+ if (addr[1].s_addr == INADDR_ANY)
+ addr[1] = old[1];
+ }
+
+ if (addr[0].s_addr == INADDR_ANY && addr[1].s_addr == INADDR_ANY) {
+ log_Printf(LogWARN, "%s not modified: All nameservers NAKd\n",
+ _PATH_RESCONF);
+ return 0;
+ }
+
+ wlen = 0;
+ if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) {
+ char buf[LINE_LEN];
+ int len;
+
+ buf[sizeof buf - 1] = '\0';
+ while (fgets(buf, sizeof buf - 1, fp)) {
+ if (strncmp(buf, "nameserver", 10) || !issep(buf[10])) {
+ len = strlen(buf);
+ if (len > sizeof wbuf - wlen) {
+ log_Printf(LogWARN, "%s: Can only cope with max file size %d\n",
+ _PATH_RESCONF, LINE_LEN);
+ fclose(fp);
+ return 0;
+ }
+ memcpy(wbuf + wlen, buf, len);
+ wlen += len;
+ }
+ }
+ fclose(fp);
+ }
+
+ if (addr[0].s_addr != INADDR_ANY) {
+ snprintf(wbuf + wlen, sizeof wbuf - wlen, "nameserver %s\n",
+ inet_ntoa(addr[0]));
+ log_Printf(LogIPCP, "Primary nameserver set to %s", wbuf + wlen + 11);
+ wlen += strlen(wbuf + wlen);
+ }
+
+ if (addr[1].s_addr != INADDR_ANY && addr[1].s_addr != addr[0].s_addr) {
+ snprintf(wbuf + wlen, sizeof wbuf - wlen, "nameserver %s\n",
+ inet_ntoa(addr[1]));
+ log_Printf(LogIPCP, "Secondary nameserver set to %s", wbuf + wlen + 11);
+ wlen += strlen(wbuf + wlen);
+ }
+
+ if (wlen) {
+ int fd;
+
+ if ((fd = ID0open(_PATH_RESCONF, O_WRONLY|O_CREAT, 0644)) != -1) {
+ if (write(fd, wbuf, wlen) != wlen) {
+ log_Printf(LogERROR, "setdns: write(): %s\n", strerror(errno));
+ close(fd);
+ return 0;
+ }
+ if (ftruncate(fd, wlen) == -1) {
+ log_Printf(LogERROR, "setdns: truncate(): %s\n", strerror(errno));
+ close(fd);
+ return 0;
+ }
+ close(fd);
+ } else {
+ log_Printf(LogERROR, "setdns: open(): %s\n", strerror(errno));
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+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));
+ }
+
+ if (ipcp->route) {
+ prompt_Printf(arg->prompt, "\n");
+ route_ShowSticky(arg->prompt, ipcp->route, "Sticky routes", 1);
+ }
+
+ 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/%d",
+ inet_ntoa(ipcp->cfg.my_range.ipaddr), ipcp->cfg.my_range.width);
+ prompt_Printf(arg->prompt, ", netmask %s\n", inet_ntoa(ipcp->cfg.netmask));
+ 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/%d\n",
+ inet_ntoa(ipcp->cfg.peer_range.ipaddr),
+ ipcp->cfg.peer_range.width);
+
+ prompt_Printf(arg->prompt, " DNS: %s, ",
+ inet_ntoa(ipcp->cfg.ns.dns[0]));
+ prompt_Printf(arg->prompt, "%s, %s\n", inet_ntoa(ipcp->cfg.ns.dns[1]),
+ command_ShowNegval(ipcp->cfg.ns.dns_neg));
+ prompt_Printf(arg->prompt, " NetBIOS NS: %s, ",
+ inet_ntoa(ipcp->cfg.ns.nbns[0]));
+ prompt_Printf(arg->prompt, "%s\n", inet_ntoa(ipcp->cfg.ns.nbns[1]));
+
+ prompt_Printf(arg->prompt, "\n");
+ 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;
+ char name[MAXHOSTNAMELEN];
+ static const char *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->route = NULL;
+ ipcp->cfg.vj.slots = DEF_VJ_STATES;
+ ipcp->cfg.vj.slotcomp = 1;
+ memset(&ipcp->cfg.my_range, '\0', sizeof ipcp->cfg.my_range);
+ if (gethostname(name, sizeof name) == 0) {
+ hp = gethostbyname(name);
+ if (hp && hp->h_addrtype == AF_INET)
+ memcpy(&ipcp->cfg.my_range.ipaddr.s_addr, hp->h_addr, hp->h_length);
+ }
+ ipcp->cfg.netmask.s_addr = INADDR_ANY;
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_setsrc(&ipcp->cfg.peer_list, "");
+ ipcp->cfg.HaveTriggerAddress = 0;
+
+ ipcp->cfg.ns.dns[0].s_addr = INADDR_ANY;
+ ipcp->cfg.ns.dns[1].s_addr = INADDR_ANY;
+ 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);
+
+ throughput_init(&ipcp->throughput);
+ memset(ipcp->Queue, '\0', sizeof ipcp->Queue);
+ ipcp_Setup(ipcp, INADDR_NONE);
+}
+
+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;
+ 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->in_addrs; n++) {
+ pos = iplist_ip2pos(&ipcp->cfg.peer_list, iface->in_addr[n].brd);
+ if (pos != -1) {
+ ipcp->cfg.peer_range.ipaddr =
+ iplist_setcurpos(&ipcp->cfg.peer_list, pos);
+ break;
+ }
+ }
+ if (n == iface->in_addrs)
+ /* Ok, so none of 'em fit.... pick a random one */
+ ipcp->cfg.peer_range.ipaddr = iplist_setrandpos(&ipcp->cfg.peer_list);
+
+ ipcp->cfg.peer_range.mask.s_addr = INADDR_BROADCAST;
+ ipcp->cfg.peer_range.width = 32;
+ }
+
+ ipcp->heis1172 = 0;
+
+ ipcp->peer_ip = ipcp->cfg.peer_range.ipaddr;
+ 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 (assuming we're not
+ * ``iface-alias''ing).
+ */
+ for (n = 0; n < iface->in_addrs; n++)
+ if ((iface->in_addr[n].ifa.s_addr & ipcp->cfg.my_range.mask.s_addr) ==
+ (ipcp->cfg.my_range.ipaddr.s_addr & ipcp->cfg.my_range.mask.s_addr)) {
+ ipcp->my_ip = iface->in_addr[n].ifa;
+ break;
+ }
+ if (n == iface->in_addrs)
+ ipcp->my_ip = ipcp->cfg.my_range.ipaddr;
+ }
+
+ 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;
+}
+
+static int
+ipcp_doproxyall(struct bundle *bundle,
+ int (*proxyfun)(struct bundle *, struct in_addr, int), int s)
+{
+ int n, ret;
+ struct sticky_route *rp;
+ struct in_addr addr;
+ struct ipcp *ipcp;
+
+ ipcp = &bundle->ncp.ipcp;
+ for (rp = ipcp->route; rp != NULL; rp = rp->next) {
+ if (rp->mask.s_addr == INADDR_BROADCAST)
+ continue;
+ n = ntohl(INADDR_BROADCAST) - ntohl(rp->mask.s_addr) - 1;
+ if (n > 0 && n <= 254 && rp->dst.s_addr != INADDR_ANY) {
+ addr = rp->dst;
+ while (n--) {
+ addr.s_addr = htonl(ntohl(addr.s_addr) + 1);
+ log_Printf(LogDEBUG, "ipcp_doproxyall: %s\n", inet_ntoa(addr));
+ ret = (*proxyfun)(bundle, addr, s);
+ if (!ret)
+ return ret;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ipcp_SetIPaddress(struct bundle *bundle, struct in_addr myaddr,
+ struct in_addr hisaddr, int silent)
+{
+ static struct in_addr none = { INADDR_ANY };
+ struct in_addr mask, oaddr;
+
+ mask = addr2mask(myaddr);
+
+ if (bundle->ncp.ipcp.ifmask.s_addr != INADDR_ANY &&
+ (bundle->ncp.ipcp.ifmask.s_addr & mask.s_addr) == mask.s_addr)
+ mask.s_addr = bundle->ncp.ipcp.ifmask.s_addr;
+
+ oaddr.s_addr = bundle->iface->in_addrs ?
+ bundle->iface->in_addr[0].ifa.s_addr : INADDR_ANY;
+ if (!iface_inAdd(bundle->iface, myaddr, mask, hisaddr,
+ IFACE_ADD_FIRST|IFACE_FORCE_ADD))
+ return -1;
+
+ if (!Enabled(bundle, OPT_IFACEALIAS) && bundle->iface->in_addrs > 1
+ && myaddr.s_addr != oaddr.s_addr)
+ /* Nuke the old one */
+ iface_inDelete(bundle->iface, oaddr);
+
+ if (bundle->ncp.ipcp.cfg.sendpipe > 0 || bundle->ncp.ipcp.cfg.recvpipe > 0)
+ bundle_SetRoute(bundle, RTM_CHANGE, hisaddr, myaddr, none, 0, 0);
+
+ if (Enabled(bundle, OPT_SROUTES))
+ route_Change(bundle, bundle->ncp.ipcp.route, myaddr, hisaddr);
+
+#ifndef NORADIUS
+ if (bundle->radius.valid)
+ route_Change(bundle, bundle->radius.routes, myaddr, hisaddr);
+#endif
+
+ if (Enabled(bundle, OPT_PROXY) || Enabled(bundle, OPT_PROXYALL)) {
+ int s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ log_Printf(LogERROR, "ipcp_SetIPaddress: socket(): %s\n",
+ strerror(errno));
+ else {
+ if (Enabled(bundle, OPT_PROXYALL))
+ ipcp_doproxyall(bundle, arp_SetProxy, s);
+ else if (Enabled(bundle, OPT_PROXY))
+ arp_SetProxy(bundle, hisaddr, s);
+ close(s);
+ }
+ }
+
+ return 0;
+}
+
+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, gw, try, 1) == 0) {
+ 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 lcp_opt *o;
+
+ o = (struct lcp_opt *)buff;
+
+ if ((p && !physical_IsSync(p)) || !REJECTED(ipcp, TY_IPADDR)) {
+ memcpy(o->data, &ipcp->my_ip.s_addr, 4);
+ INC_LCP_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_LCP_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_LCP_OPT(TY_COMPPROTO, 6, o);
+ }
+ }
+
+ if (IsEnabled(ipcp->cfg.ns.dns_neg) &&
+ !REJECTED(ipcp, TY_PRIMARY_DNS - TY_ADJUST_NS) &&
+ !REJECTED(ipcp, TY_SECONDARY_DNS - TY_ADJUST_NS)) {
+ struct in_addr dns[2];
+ getdns(ipcp, dns);
+ memcpy(o->data, &dns[0].s_addr, 4);
+ INC_LCP_OPT(TY_PRIMARY_DNS, 6, o);
+ memcpy(o->data, &dns[1].s_addr, 4);
+ INC_LCP_OPT(TY_SECONDARY_DNS, 6, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff);
+}
+
+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);
+}
+
+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;
+}
+
+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);
+}
+
+void
+ipcp_CleanInterface(struct ipcp *ipcp)
+{
+ struct iface *iface = ipcp->fsm.bundle->iface;
+
+ route_Clean(ipcp->fsm.bundle, ipcp->route);
+
+ if (iface->in_addrs && (Enabled(ipcp->fsm.bundle, OPT_PROXY) ||
+ Enabled(ipcp->fsm.bundle, OPT_PROXYALL))) {
+ int s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ log_Printf(LogERROR, "ipcp_CleanInterface: socket: %s\n",
+ strerror(errno));
+ else {
+ if (Enabled(ipcp->fsm.bundle, OPT_PROXYALL))
+ ipcp_doproxyall(ipcp->fsm.bundle, arp_ClearProxy, s);
+ else if (Enabled(ipcp->fsm.bundle, OPT_PROXY))
+ arp_ClearProxy(ipcp->fsm.bundle, iface->in_addr[0].brd, s);
+ close(s);
+ }
+ }
+
+ iface_inClear(ipcp->fsm.bundle->iface, IFACE_CLEAR_ALL);
+}
+
+static void
+IpcpLayerDown(struct fsm *fp)
+{
+ /* About to come down */
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ const char *s;
+
+ if (ipcp->fsm.bundle->iface->in_addrs)
+ s = inet_ntoa(ipcp->fsm.bundle->iface->in_addr[0].ifa);
+ else
+ s = "Interface configuration error !";
+ log_Printf(LogIPCP, "%s: LayerDown: %s\n", fp->link->name, s);
+
+ /*
+ * XXX this stuff should really live in the FSM. Our config should
+ * associate executable sections in files with events.
+ */
+ if (system_Select(fp->bundle, s, 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);
+}
+
+int
+ipcp_InterfaceUp(struct ipcp *ipcp)
+{
+ if (ipcp_SetIPaddress(ipcp->fsm.bundle, ipcp->my_ip, ipcp->peer_ip, 0) < 0) {
+ log_Printf(LogERROR, "ipcp_InterfaceUp: unable to set ip address\n");
+ return 0;
+ }
+
+#ifndef NOALIAS
+ if (ipcp->fsm.bundle->AliasEnabled)
+ 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;
+
+ /*
+ * 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 int
+AcceptableAddr(const struct in_range *prange, struct in_addr ipaddr)
+{
+ /* Is the given IP in the given range ? */
+ return (prange->ipaddr.s_addr & prange->mask.s_addr) ==
+ (ipaddr.s_addr & prange->mask.s_addr) && ipaddr.s_addr;
+}
+
+static void
+IpcpDecodeConfig(struct fsm *fp, u_char * cp, int plen, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_IPCP */
+ struct iface *iface = fp->bundle->iface;
+ struct ipcp *ipcp = fsm2ipcp(fp);
+ int type, length, gotdns, gotdnsnak, n;
+ u_int32_t compproto;
+ struct compreq *pcomp;
+ struct in_addr ipaddr, dstipaddr, have_ip, dns[2], dnsnak[2];
+ char tbuff[100], tbuff2[100];
+
+ gotdns = 0;
+ gotdnsnak = 0;
+ dnsnak[0].s_addr = dnsnak[1].s_addr = INADDR_ANY;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ type = *cp;
+ length = cp[1];
+
+ if (length == 0) {
+ log_Printf(LogIPCP, "%s: IPCP size zero\n", fp->link->name);
+ break;
+ }
+
+ if (type < NCFTYPES)
+ snprintf(tbuff, sizeof tbuff, " %s[%d] ", cftypes[type], length);
+ else if (type > 128 && type < 128 + NCFTYPES128)
+ snprintf(tbuff, sizeof tbuff, " %s[%d] ", cftypes128[type-128], length);
+ else
+ snprintf(tbuff, sizeof tbuff, " <%d>[%d] ", type, length);
+
+ switch (type) {
+ case TY_IPADDR: /* RFC1332 */
+ memcpy(&ipaddr.s_addr, cp + 2, 4);
+ log_Printf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (iplist_isvalid(&ipcp->cfg.peer_list)) {
+ if (ipaddr.s_addr == INADDR_ANY ||
+ iplist_ip2pos(&ipcp->cfg.peer_list, ipaddr) < 0 ||
+ ipcp_SetIPaddress(fp->bundle, ipcp->cfg.my_range.ipaddr,
+ ipaddr, 1)) {
+ log_Printf(LogIPCP, "%s: Address invalid or already in use\n",
+ inet_ntoa(ipaddr));
+ /*
+ * 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->in_addrs; n++)
+ if (iplist_ip2pos(&ipcp->cfg.peer_list, iface->in_addr[n].brd)
+ >=0) {
+ ipcp->peer_ip = iface->in_addr[n].brd;
+ break;
+ }
+
+ if (n == iface->in_addrs)
+ /* Just pick an IP number from our list */
+ ipcp->peer_ip = ChooseHisAddr
+ (fp->bundle, ipcp->cfg.my_range.ipaddr);
+
+ if (ipcp->peer_ip.s_addr == INADDR_ANY) {
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ } else {
+ memcpy(dec->nakend, cp, 2);
+ memcpy(dec->nakend + 2, &ipcp->peer_ip.s_addr, length - 2);
+ dec->nakend += length;
+ }
+ break;
+ }
+ } else if (!AcceptableAddr(&ipcp->cfg.peer_range, ipaddr)) {
+ /*
+ * If destination address is not acceptable, NAK with what we
+ * want to use.
+ */
+ memcpy(dec->nakend, cp, 2);
+ for (n = 0; n < iface->in_addrs; n++)
+ if ((iface->in_addr[n].brd.s_addr &
+ ipcp->cfg.peer_range.mask.s_addr)
+ == (ipcp->cfg.peer_range.ipaddr.s_addr &
+ ipcp->cfg.peer_range.mask.s_addr)) {
+ /* We prefer the already-configured address */
+ memcpy(dec->nakend + 2, &iface->in_addr[n].brd.s_addr,
+ length - 2);
+ break;
+ }
+
+ if (n == iface->in_addrs)
+ memcpy(dec->nakend + 2, &ipcp->peer_ip.s_addr, length - 2);
+
+ dec->nakend += length;
+ break;
+ }
+ ipcp->peer_ip = ipaddr;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ break;
+
+ case MODE_NAK:
+ if (AcceptableAddr(&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;
+ } 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 << type);
+ break;
+ }
+ break;
+
+ case TY_COMPPROTO:
+ pcomp = (struct compreq *)(cp + 2);
+ 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)) {
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ } else {
+ switch (length) {
+ 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;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else {
+ memcpy(dec->nakend, cp, 2);
+ pcomp->proto = htons(PROTO_VJCOMP);
+ memcpy(dec->nakend+2, &pcomp, 2);
+ dec->nakend += length;
+ }
+ 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;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else {
+ /* Get as close as we can to what he wants */
+ ipcp->heis1172 = 0;
+ memcpy(dec->nakend, cp, 2);
+ pcomp->slots = pcomp->slots < MIN_VJ_STATES ?
+ MIN_VJ_STATES : MAX_VJ_STATES;
+ memcpy(dec->nakend+2, &pcomp, sizeof pcomp);
+ dec->nakend += length;
+ }
+ } else {
+ /* What we really want */
+ memcpy(dec->nakend, cp, 2);
+ pcomp->proto = htons(PROTO_VJCOMP);
+ pcomp->slots = DEF_VJ_STATES;
+ pcomp->compcid = 1;
+ memcpy(dec->nakend+2, &pcomp, sizeof pcomp);
+ dec->nakend += length;
+ }
+ break;
+ default:
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ 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 << type);
+ break;
+ }
+ break;
+
+ case TY_IPADDRS: /* RFC1172 */
+ memcpy(&ipaddr.s_addr, cp + 2, 4);
+ memcpy(&dstipaddr.s_addr, cp + 6, 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:
+ ipcp->peer_ip = ipaddr;
+ ipcp->my_ip = dstipaddr;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ break;
+
+ case MODE_NAK:
+ 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;
+ ipcp->peer_ip = dstipaddr;
+ break;
+
+ case MODE_REJ:
+ ipcp->peer_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_DNS: /* DNS negotiation (rfc1877) */
+ case TY_SECONDARY_DNS:
+ memcpy(&ipaddr.s_addr, cp + 2, 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 << (type - TY_ADJUST_NS));
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ break;
+ }
+ if (!gotdns) {
+ dns[0] = ipcp->cfg.ns.dns[0];
+ dns[1] = ipcp->cfg.ns.dns[1];
+ if (dns[0].s_addr == INADDR_ANY && dns[1].s_addr == INADDR_ANY)
+ getdns(ipcp, dns);
+ gotdns = 1;
+ }
+ have_ip = dns[type == TY_PRIMARY_DNS ? 0 : 1];
+
+ 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
+ */
+ memcpy(dec->nakend, cp, 2); /* copy first two (type/length) */
+ memcpy(dec->nakend + 2, &have_ip.s_addr, length - 2);
+ dec->nakend += length;
+ } else {
+ /*
+ * Otherwise they have it right (this time) so we send a ack packet
+ * back confirming it... end of story
+ */
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ }
+ break;
+
+ case MODE_NAK: /* what does this mean?? */
+ if (IsEnabled(ipcp->cfg.ns.dns_neg)) {
+ gotdnsnak = 1;
+ memcpy(&dnsnak[type == TY_PRIMARY_DNS ? 0 : 1].s_addr, cp + 2, 4);
+ }
+ break;
+
+ case MODE_REJ: /* Can't do much, stop asking */
+ ipcp->peer_reject |= (1 << (type - TY_ADJUST_NS));
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_NBNS: /* M$ NetBIOS nameserver hack (rfc1877) */
+ case TY_SECONDARY_NBNS:
+ memcpy(&ipaddr.s_addr, cp + 2, 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[type == 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 << (type - TY_ADJUST_NS));
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ break;
+ }
+
+ if (ipaddr.s_addr != have_ip.s_addr) {
+ memcpy(dec->nakend, cp, 2);
+ memcpy(dec->nakend+2, &have_ip.s_addr, length);
+ dec->nakend += length;
+ } else {
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ }
+ break;
+
+ case MODE_NAK:
+ log_Printf(LogIPCP, "MS NBNS req %d - NAK??\n", type);
+ break;
+
+ case MODE_REJ:
+ log_Printf(LogIPCP, "MS NBNS req %d - REJ??\n", type);
+ break;
+ }
+ break;
+
+ default:
+ if (mode_type != MODE_NOP) {
+ ipcp->my_reject |= (1 << type);
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ }
+ break;
+ }
+ plen -= length;
+ cp += length;
+ }
+
+ if (gotdnsnak)
+ if (!setdns(ipcp, dnsnak)) {
+ ipcp->peer_reject |= (1 << (TY_PRIMARY_DNS - TY_ADJUST_NS));
+ ipcp->peer_reject |= (1 << (TY_SECONDARY_DNS - TY_ADJUST_NS));
+ }
+
+ if (mode_type != MODE_NOP) {
+ 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;
+ }
+}
+
+void
+ipcp_Input(struct ipcp *ipcp, struct bundle *bundle, struct mbuf *bp)
+{
+ /* Got PROTO_IPCP from link */
+ if (bundle_Phase(bundle) == PHASE_NETWORK)
+ fsm_Input(&ipcp->fsm, bp);
+ else {
+ if (bundle_Phase(bundle) < PHASE_NETWORK)
+ log_Printf(LogIPCP, "%s: Error: Unexpected IPCP in phase %s (ignored)\n",
+ ipcp->fsm.link->name, bundle_PhaseName(bundle));
+ mbuf_Free(bp);
+ }
+}
+
+int
+ipcp_UseHisIPaddr(struct bundle *bundle, struct in_addr hisaddr)
+{
+ struct ipcp *ipcp = &bundle->ncp.ipcp;
+
+ memset(&ipcp->cfg.peer_range, '\0', sizeof ipcp->cfg.peer_range);
+ iplist_reset(&ipcp->cfg.peer_list);
+ ipcp->peer_ip = ipcp->cfg.peer_range.ipaddr = hisaddr;
+ ipcp->cfg.peer_range.mask.s_addr = INADDR_BROADCAST;
+ ipcp->cfg.peer_range.width = 32;
+
+ if (ipcp_SetIPaddress(bundle, ipcp->cfg.my_range.ipaddr, hisaddr, 0) < 0)
+ return 0;
+
+ return 1; /* Ok */
+}
+
+int
+ipcp_UseHisaddr(struct bundle *bundle, const char *hisaddr, int setaddr)
+{
+ struct ipcp *ipcp = &bundle->ncp.ipcp;
+
+ /* 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);
+ }
+ ipcp->cfg.peer_range.ipaddr.s_addr = ipcp->peer_ip.s_addr;
+ ipcp->cfg.peer_range.mask.s_addr = INADDR_BROADCAST;
+ ipcp->cfg.peer_range.width = 32;
+ } else {
+ log_Printf(LogWARN, "%s: Invalid range !\n", hisaddr);
+ return 0;
+ }
+ } else if (ParseAddr(ipcp, hisaddr, &ipcp->cfg.peer_range.ipaddr,
+ &ipcp->cfg.peer_range.mask,
+ &ipcp->cfg.peer_range.width) != 0) {
+ ipcp->peer_ip.s_addr = ipcp->cfg.peer_range.ipaddr.s_addr;
+
+ if (setaddr && ipcp_SetIPaddress(bundle, ipcp->cfg.my_range.ipaddr,
+ ipcp->cfg.peer_range.ipaddr, 0) < 0)
+ return 0;
+ } else
+ return 0;
+
+ return 1;
+}
+
+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;
+}
diff --git a/usr.sbin/ppp/ipcp.h b/usr.sbin/ppp/ipcp.h
new file mode 100644
index 0000000..b25c41d
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.h
@@ -0,0 +1,119 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h,v 1.24 1999/02/26 21:28:12 brian Exp $
+ *
+ * TODO:
+ */
+
+#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 sticky_route;
+
+struct in_range {
+ struct in_addr ipaddr;
+ struct in_addr mask;
+ int width;
+};
+
+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 in_range my_range; /* MYADDR spec */
+ struct in_addr netmask; /* Iface netmask (unused by most OSs) */
+ struct in_range peer_range; /* HISADDR spec */
+ struct iplist peer_list; /* Ranges of HISADDR values */
+
+ u_long sendpipe; /* route sendpipe size */
+ u_long recvpipe; /* route recvpipe size */
+
+ 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; /* How often/frequently to resend requests */
+ } cfg;
+
+ struct {
+ struct slcompress cslc; /* VJ state */
+ struct slstat slstat; /* VJ statistics */
+ } vj;
+
+ struct sticky_route *route; /* List of dynamic routes */
+
+ unsigned heis1172 : 1; /* True if he is speaking rfc1172 */
+
+ 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[PRI_FAST + 1]; /* Output packet queues */
+};
+
+#define fsm2ipcp(fp) (fp->proto == PROTO_IPCP ? (struct ipcp *)fp : NULL)
+
+struct bundle;
+struct link;
+struct cmdargs;
+
+extern void ipcp_Init(struct ipcp *, struct bundle *, struct link *,
+ const struct fsm_parent *);
+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 void ipcp_Input(struct ipcp *, struct bundle *, 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_CleanInterface(struct ipcp *);
+extern int ipcp_InterfaceUp(struct ipcp *);
+extern struct in_addr addr2mask(struct in_addr);
diff --git a/usr.sbin/ppp/iplist.c b/usr.sbin/ppp/iplist.c
new file mode 100644
index 0000000..2eddeeb
--- /dev/null
+++ b/usr.sbin/ppp/iplist.c
@@ -0,0 +1,224 @@
+/*-
+ * 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.
+ *
+ * $Id: iplist.c,v 1.6 1998/06/15 19:06:47 brian Exp $
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <stdlib.h>
+#include <string.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..ce1778d
--- /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.
+ *
+ * $Id: iplist.h,v 1.2 1997/12/21 12:11:06 brian Exp $
+ */
+
+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/lcp.c b/usr.sbin/ppp/lcp.c
new file mode 100644
index 0000000..0c49bb8
--- /dev/null
+++ b/usr.sbin/ppp/lcp.c
@@ -0,0 +1,1185 @@
+/*
+ * PPP Link Control Protocol (LCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.c,v 1.70 1999/03/01 13:46:45 brian Exp $
+ *
+ * TODO:
+ * o Limit data field length by MRU
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.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 "lcp.h"
+#include "throughput.h"
+#include "lcpproto.h"
+#include "descriptor.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "ccp.h"
+#include "async.h"
+#include "link.h"
+#include "physical.h"
+#include "prompt.h"
+#include "slcompress.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 "bundle.h"
+
+/* for received LQRs */
+struct lqrreq {
+ u_char type;
+ u_char length;
+ 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 *, int, 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 *lcp_TimerNames[] =
+ {"LCP restart", "LCP openmode", "LCP stopped"};
+
+static const char *cftypes[] = {
+ /* Check out the latest ``Assigned numbers'' rfc (rfc1700.txt) */
+ "???",
+ "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 */
+};
+
+#define NCFTYPES (sizeof cftypes/sizeof cftypes[0])
+
+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);
+
+ prompt_Printf(arg->prompt, "\n Defaults: MRU = %d, ", lcp->cfg.mru);
+ 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, "\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));
+#ifdef HAVE_DES
+ prompt_Printf(arg->prompt, " MSCHAP = %s\n",
+ command_ShowNegval(lcp->cfg.chap80nt));
+ prompt_Printf(arg->prompt, " LANMan = %s\n",
+ command_ShowNegval(lcp->cfg.chap80lm));
+#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 = DEF_MRU;
+ 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;
+#ifdef HAVE_DES
+ lcp->cfg.chap80nt = NEG_ACCEPTED;
+ lcp->cfg.chap80lm = NEG_ACCEPTED;
+#endif
+ lcp->cfg.lqr = NEG_ACCEPTED;
+ lcp->cfg.pap = NEG_ACCEPTED;
+ lcp->cfg.protocomp = NEG_ENABLED|NEG_ACCEPTED;
+
+ lcp_Setup(lcp, lcp->cfg.openmode);
+}
+
+void
+lcp_Setup(struct lcp *lcp, int openmode)
+{
+ lcp->fsm.open_mode = openmode;
+
+ lcp->his_mru = lcp->fsm.bundle->cfg.mtu;
+ if (!lcp->his_mru || lcp->his_mru > DEF_MRU)
+ 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->want_mru = lcp->cfg.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) {
+ struct physical *p = link2physical(lcp->fsm.link);
+
+ 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;
+#ifdef HAVE_DES
+ } else if (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+#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 lcp_opt *o;
+ struct mp *mp;
+ u_int16_t proto;
+
+ if (!p) {
+ log_Printf(LogERROR, "%s: LcpSendConfigReq: Not a physical link !\n",
+ fp->link->name);
+ return;
+ }
+
+ o = (struct lcp_opt *)buff;
+ if (!physical_IsSync(p)) {
+ if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP))
+ INC_LCP_OPT(TY_ACFCOMP, 2, o);
+
+ if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP))
+ INC_LCP_OPT(TY_PROTOCOMP, 2, o);
+
+ if (!REJECTED(lcp, TY_ACCMAP)) {
+ ua_htonl(&lcp->want_accmap, o->data);
+ INC_LCP_OPT(TY_ACCMAP, 6, o);
+ }
+ }
+
+ if (!REJECTED(lcp, TY_MRU)) {
+ ua_htons(&lcp->want_mru, o->data);
+ INC_LCP_OPT(TY_MRU, 4, o);
+ }
+
+ if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM)) {
+ ua_htonl(&lcp->want_magic, o->data);
+ INC_LCP_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_LCP_OPT(TY_QUALPROTO, 8, o);
+ }
+
+ switch (lcp->want_auth) {
+ case PROTO_PAP:
+ proto = PROTO_PAP;
+ ua_htons(&proto, o->data);
+ INC_LCP_OPT(TY_AUTHPROTO, 4, o);
+ break;
+
+ case PROTO_CHAP:
+ proto = PROTO_CHAP;
+ ua_htons(&proto, o->data);
+ o->data[2] = lcp->want_authtype;
+ INC_LCP_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_LCP_OPT(TY_CALLBACK, 3, o);
+ } else if (lcp->want_callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ *o->data = CALLBACK_CBCP;
+ INC_LCP_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_LCP_OPT(TY_CALLBACK, sz + 3, o);
+ }
+ }
+
+ if (lcp->want_mrru && !REJECTED(lcp, TY_MRRU)) {
+ ua_htons(&lcp->want_mrru, o->data);
+ INC_LCP_OPT(TY_MRRU, 4, o);
+
+ if (lcp->want_shortseq && !REJECTED(lcp, TY_SHORTSEQ))
+ INC_LCP_OPT(TY_SHORTSEQ, 2, o);
+ }
+
+ mp = &lcp->fsm.bundle->ncp.mp;
+ if (mp->cfg.enddisc.class != 0 && !REJECTED(lcp, TY_ENDDISC)) {
+ *o->data = mp->cfg.enddisc.class;
+ memcpy(o->data+1, mp->cfg.enddisc.address, mp->cfg.enddisc.len);
+ INC_LCP_OPT(TY_ENDDISC, mp->cfg.enddisc.len + 3, o);
+ }
+
+ fsm_Output(fp, CODE_CONFIGREQ, fp->reqid, buff, (u_char *)o - buff);
+}
+
+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);
+}
+
+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);
+}
+
+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;
+}
+
+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);
+ async_SetLinkParams(&p->async, lcp);
+ lqr_Start(lcp);
+ hdlc_StartTimer(&p->hdlc);
+ fp->more.reqs = fp->more.naks = fp->more.rejs = lcp->cfg.fsm.maxreq * 3;
+
+ 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 void
+LcpDecodeConfig(struct fsm *fp, u_char *cp, int plen, int mode_type,
+ struct fsm_decode *dec)
+{
+ /* Deal with incoming PROTO_LCP */
+ struct lcp *lcp = fsm2lcp(fp);
+ int type, length, sz, pos, op, callback_req;
+ u_int32_t magic, accmap;
+ u_short mtu, mru, proto;
+ struct lqrreq *req;
+ char request[20], desc[22];
+ struct mp *mp;
+ struct physical *p = link2physical(fp->link);
+
+ callback_req = 0;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ type = *cp;
+ length = cp[1];
+
+ if (type < 0 || type >= NCFTYPES)
+ snprintf(request, sizeof request, " <%d>[%d]", type, length);
+ else
+ snprintf(request, sizeof request, " %s[%d]", cftypes[type], length);
+
+ if (length < 2) {
+ log_Printf(LogLCP, "%s:%s: Bad LCP length\n", fp->link->name, request);
+ break;
+ }
+
+ switch (type) {
+ case TY_MRRU:
+ mp = &lcp->fsm.bundle->ncp.mp;
+ ua_ntohs(cp + 2, &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 << type);
+
+ mtu = lcp->fsm.bundle->cfg.mtu;
+ if (mru < MIN_MRU || mru < mtu) {
+ /* Push him up to MTU or MIN_MRU */
+ lcp->his_mrru = mru < mtu ? mtu : MIN_MRU;
+ memcpy(dec->nakend, cp, 2);
+ ua_htons(&lcp->his_mrru, dec->nakend + 2);
+ dec->nakend += 4;
+ } else {
+ lcp->his_mrru = mtu ? mtu : mru;
+ memcpy(dec->ackend, cp, 4);
+ dec->ackend += 4;
+ }
+ break;
+ } else
+ goto reqreject;
+ break;
+ case MODE_NAK:
+ if (mp->cfg.mrru) {
+ if (REJECTED(lcp, TY_MRRU))
+ /* Must have changed his mind ! */
+ lcp->his_reject &= ~(1 << type);
+
+ 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 << type);
+ lcp->want_mrru = 0; /* Ah well, no multilink :-( */
+ break;
+ }
+ break;
+
+ case TY_MRU:
+ ua_ntohs(cp + 2, &mru);
+ log_Printf(LogLCP, "%s %d\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ mtu = lcp->fsm.bundle->cfg.mtu;
+ if (mru < MIN_MRU || (!lcp->want_mrru && mru < mtu)) {
+ /* Push him up to MTU or MIN_MRU */
+ lcp->his_mru = mru < mtu ? mtu : MIN_MRU;
+ memcpy(dec->nakend, cp, 2);
+ ua_htons(&lcp->his_mru, dec->nakend + 2);
+ dec->nakend += 4;
+ } else {
+ lcp->his_mru = mtu ? mtu : mru;
+ memcpy(dec->ackend, cp, 4);
+ dec->ackend += 4;
+ }
+ break;
+ case MODE_NAK:
+ if (mru > MAX_MRU)
+ lcp->want_mru = MAX_MRU;
+ else if (mru < MIN_MRU)
+ lcp->want_mru = MIN_MRU;
+ else
+ lcp->want_mru = mru;
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ case TY_ACCMAP:
+ ua_ntohl(cp + 2, &accmap);
+ log_Printf(LogLCP, "%s 0x%08lx\n", request, (u_long)accmap);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ lcp->his_accmap = accmap;
+ if ((lcp->want_accmap | accmap) != lcp->want_accmap) {
+ lcp->want_accmap |= accmap; /* restrict our requested map */
+ lcp->fsm.reqid++; /* Invalidate the current REQ */
+ /*
+ * If we've already sent a REQ, we want to make sure that
+ * we don't end up sending out a new REQ that doesn't contain
+ * the data that the last one with the same id contained.
+ * This also means that we ignore the peers response to our
+ * last REQ due to an invalid fsm id (even though it's really
+ * correct), probably resulting in a REQ timeout and a resend
+ * with the new accmap and the new id.
+ * If we're already in ST_ACKRCVD at this point, we simply end
+ * up thinking that we negotiated the new accmap - which is ok
+ * as we just end up escaping stuff that the peer probably
+ * can't receive anyway.
+ */
+ }
+ if (lcp->want_accmap == accmap) {
+ memcpy(dec->ackend, cp, 6);
+ dec->ackend += 6;
+ } else {
+ /* NAK with what we now want */
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 6;
+ ua_htonl(&lcp->want_accmap, dec->nakend);
+ dec->nakend += 4;
+ }
+ break;
+ case MODE_NAK:
+ lcp->want_accmap |= accmap;
+ break;
+ case MODE_REJ:
+ if (lcp->want_accmap)
+ log_Printf(LogWARN, "Peer is rejecting our ACCMAP.... bad news !\n");
+ else
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ case TY_AUTHPROTO:
+ ua_ntohs(cp + 2, &proto);
+ log_Printf(LogLCP, "%s 0x%04x (%s)\n", request, proto,
+ Auth2Nam(proto, length > 4 ? cp[4] : 0));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ switch (proto) {
+ case PROTO_PAP:
+ if (length != 4) {
+ log_Printf(LogLCP, " Bad length!\n");
+ goto reqreject;
+ }
+ if (IsAccepted(lcp->cfg.pap)) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = 0;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else if (IsAccepted(lcp->cfg.chap05)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 5;
+ *dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
+ *dec->nakend++ = (unsigned char) PROTO_CHAP;
+ *dec->nakend++ = 0x05;
+#ifdef HAVE_DES
+ } else if (IsAccepted(lcp->cfg.chap80nt) ||
+ IsAccepted(lcp->cfg.chap80lm)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 5;
+ *dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
+ *dec->nakend++ = (unsigned char) PROTO_CHAP;
+ *dec->nakend++ = 0x80;
+#endif
+ } else
+ goto reqreject;
+ break;
+
+ case PROTO_CHAP:
+ if (length != 5) {
+ log_Printf(LogLCP, " Bad length!\n");
+ goto reqreject;
+ }
+ if ((cp[4] == 0x05 && IsAccepted(lcp->cfg.chap05))
+#ifdef HAVE_DES
+ || (cp[4] == 0x80 && (IsAccepted(lcp->cfg.chap80nt) ||
+ (IsAccepted(lcp->cfg.chap80lm))))
+#endif
+ ) {
+ lcp->his_auth = proto;
+ lcp->his_authtype = cp[4];
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else {
+#ifndef HAVE_DES
+ if (cp[4] == 0x80)
+ log_Printf(LogWARN, "CHAP 0x80 not available without DES\n");
+ else
+#endif
+ if (cp[4] != 0x05)
+ log_Printf(LogWARN, "%s not supported\n",
+ Auth2Nam(PROTO_CHAP, cp[4]));
+
+ if (IsAccepted(lcp->cfg.chap05)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 5;
+ *dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
+ *dec->nakend++ = (unsigned char) PROTO_CHAP;
+ *dec->nakend++ = 0x05;
+#ifdef HAVE_DES
+ } else if (IsAccepted(lcp->cfg.chap80nt) ||
+ IsAccepted(lcp->cfg.chap80lm)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 5;
+ *dec->nakend++ = (unsigned char) (PROTO_CHAP >> 8);
+ *dec->nakend++ = (unsigned char) PROTO_CHAP;
+ *dec->nakend++ = 0x80;
+#endif
+ } else if (IsAccepted(lcp->cfg.pap)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 4;
+ *dec->nakend++ = (unsigned char) (PROTO_PAP >> 8);
+ *dec->nakend++ = (unsigned char) PROTO_PAP;
+ } else
+ goto reqreject;
+ }
+ break;
+
+ default:
+ log_Printf(LogLCP, "%s 0x%04x - not recognised, NAK\n",
+ request, proto);
+ memcpy(dec->nakend, cp, length);
+ dec->nakend += length;
+ 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 << type);
+ }
+ break;
+ case PROTO_CHAP:
+ if (cp[4] == 0x05 && IsEnabled(lcp->cfg.chap05)) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x05;
+#ifdef HAVE_DES
+ } else if (cp[4] == 0x80 && (IsEnabled(lcp->cfg.chap80nt) ||
+ IsEnabled(lcp->cfg.chap80lm))) {
+ lcp->want_auth = PROTO_CHAP;
+ lcp->want_authtype = 0x80;
+#endif
+ } else {
+#ifndef HAVE_DES
+ if (cp[4] == 0x80)
+ log_Printf(LogLCP, "Peer will only send MSCHAP (not available"
+ " without DES)\n");
+ else
+#endif
+ log_Printf(LogLCP, "Peer will only send %s (not %s)\n",
+ Auth2Nam(PROTO_CHAP, cp[4]),
+#ifdef HAVE_DES
+ cp[4] == 0x80 ? "configured" :
+#endif
+ "supported");
+ lcp->his_reject |= (1 << type);
+ }
+ break;
+ default:
+ /* We've been NAK'd with something we don't understand :-( */
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ case TY_QUALPROTO:
+ req = (struct lqrreq *)cp;
+ 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))
+ goto reqreject;
+ 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);
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ }
+ break;
+ case MODE_NAK:
+ break;
+ case MODE_REJ:
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ case TY_MAGICNUM:
+ ua_ntohl(cp + 2, &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) {
+ log_Printf(LogLCP, "Magic is same (%08lx) - %d times\n",
+ (u_long)magic, ++lcp->LcpFailedMagic);
+ lcp->want_magic = GenerateMagic();
+ memcpy(dec->nakend, cp, 6);
+ dec->nakend += 6;
+ ualarm(TICKUNIT * (4 + 4 * lcp->LcpFailedMagic), 0);
+ sigpause(0);
+ } else {
+ lcp->his_magic = magic;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ lcp->LcpFailedMagic = 0;
+ }
+ } else {
+ goto reqreject;
+ }
+ 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 << type);
+ 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;
+ memcpy(dec->ackend, cp, 2);
+ dec->ackend += 2;
+ } else {
+#ifdef OLDMST
+ /*
+ * MorningStar before v1.3 needs NAK
+ */
+ memcpy(dec->nakend, cp, 2);
+ dec->nakend += 2;
+#else
+ goto reqreject;
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_protocomp = 0;
+ lcp->his_reject |= (1 << type);
+ 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;
+ memcpy(dec->ackend, cp, 2);
+ dec->ackend += 2;
+ } else {
+#ifdef OLDMST
+ /*
+ * MorningStar before v1.3 needs NAK
+ */
+ memcpy(dec->nakend, cp, 2);
+ dec->nakend += 2;
+#else
+ goto reqreject;
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ lcp->want_acfcomp = 0;
+ lcp->his_reject |= (1 << type);
+ 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 (length == 2)
+ op = CALLBACK_NONE;
+ else
+ op = (int)cp[2];
+ sz = length - 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, cp + 3);
+ break;
+ case CALLBACK_LOCATION:
+ log_Printf(LogLCP, "%s Location %.*s\n", request, sz, cp + 3);
+ break;
+ case CALLBACK_E164:
+ log_Printf(LogLCP, "%s E.164 (%.*s)\n", request, sz, cp + 3);
+ break;
+ case CALLBACK_NAME:
+ log_Printf(LogLCP, "%s Name %.*s\n", request, sz, cp + 3);
+ 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)
+ goto reqreject;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(op)) &&
+ (op != CALLBACK_AUTH || p->link.lcp.auth_ineed) &&
+ (op != CALLBACK_E164 ||
+ E164ok(&p->dl->cfg.callback, cp + 3, 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, cp + 3, sz);
+ lcp->his_callback.msg[sz] = '\0';
+ memcpy(dec->ackend, cp, sz + 3);
+ dec->ackend += sz + 3;
+ } else if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.auth_ineed) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 3;
+ *dec->nakend++ = CALLBACK_AUTH;
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 3;
+ *dec->nakend++ = CALLBACK_CBCP;
+ } else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164)) {
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 3;
+ *dec->nakend++ = CALLBACK_E164;
+ } 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");
+ *dec->nakend++ = *cp;
+ *dec->nakend++ = 2;
+ } else
+ goto reqreject;
+ 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 << type);
+ 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;
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else
+ goto reqreject;
+ 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 << type);
+ lcp->want_shortseq = 0; /* For when we hit MP */
+ break;
+ }
+ break;
+
+ case TY_ENDDISC:
+ log_Printf(LogLCP, "%s %s\n", request,
+ mp_Enddisc(cp[2], cp + 3, length - 3));
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!p) {
+ log_Printf(LogLCP, " ENDDISC rejected - not a physical link\n");
+ goto reqreject;
+ } else if (length-3 < sizeof p->dl->peer.enddisc.address &&
+ cp[2] <= MAX_ENDDISC_CLASS) {
+ p->dl->peer.enddisc.class = cp[2];
+ p->dl->peer.enddisc.len = length-3;
+ memcpy(p->dl->peer.enddisc.address, cp + 3, length - 3);
+ p->dl->peer.enddisc.address[length - 3] = '\0';
+ /* XXX: If mp->active, compare and NAK with mp->peer ? */
+ memcpy(dec->ackend, cp, length);
+ dec->ackend += length;
+ } else {
+ if (cp[2] > MAX_ENDDISC_CLASS)
+ log_Printf(LogLCP, " ENDDISC rejected - unrecognised class %d\n",
+ cp[2]);
+ else
+ log_Printf(LogLCP, " ENDDISC rejected - local max length is %ld\n",
+ (long)(sizeof p->dl->peer.enddisc.address - 1));
+ goto reqreject;
+ }
+ break;
+
+ case MODE_NAK: /* Treat this as a REJ, we don't vary our disc */
+ case MODE_REJ:
+ lcp->his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ default:
+ sz = (sizeof desc - 2) / 2;
+ if (sz > length - 2)
+ sz = length - 2;
+ pos = 0;
+ desc[0] = sz ? ' ' : '\0';
+ for (pos = 0; sz--; pos++)
+ sprintf(desc+(pos<<1)+1, "%02x", cp[pos+2]);
+
+ log_Printf(LogLCP, "%s%s\n", request, desc);
+
+ if (mode_type == MODE_REQ) {
+reqreject:
+ if (length > sizeof dec->rej - (dec->rejend - dec->rej)) {
+ length = sizeof dec->rej - (dec->rejend - dec->rej);
+ log_Printf(LogLCP, "Can't REJ length %d - trunating to %d\n",
+ cp[1], length);
+ }
+ memcpy(dec->rejend, cp, length);
+ dec->rejend += length;
+ lcp->my_reject |= (1 << type);
+ if (length != cp[1])
+ length = 0; /* force our way out of the loop */
+ }
+ break;
+ }
+ plen -= length;
+ cp += length;
+ }
+
+ 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 */
+ *dec->nakend++ = TY_CALLBACK;
+ *dec->nakend++ = 3;
+ if ((p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_AUTH)) &&
+ p->link.lcp.auth_ineed)
+ *dec->nakend++ = CALLBACK_AUTH;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_CBCP))
+ *dec->nakend++ = CALLBACK_CBCP;
+ else if (p->dl->cfg.callback.opmask & CALLBACK_BIT(CALLBACK_E164))
+ *dec->nakend++ = CALLBACK_E164;
+ else {
+ log_Printf(LogWARN, "Cannot insist on auth callback without"
+ " PAP or CHAP enabled !\n");
+ dec->nakend[-1] = 2; /* XXX: Silly ! */
+ }
+ }
+ 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;
+ }
+}
+
+void
+lcp_Input(struct lcp *lcp, struct mbuf *bp)
+{
+ /* Got PROTO_LCP from link */
+ fsm_Input(&lcp->fsm, bp);
+}
diff --git a/usr.sbin/ppp/lcp.h b/usr.sbin/ppp/lcp.h
new file mode 100644
index 0000000..47a984d
--- /dev/null
+++ b/usr.sbin/ppp/lcp.h
@@ -0,0 +1,140 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h,v 1.20 1999/02/18 00:52:15 brian Exp $
+ *
+ * TODO:
+ */
+
+/* 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 */
+
+ 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_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 */
+#ifdef HAVE_DES
+ unsigned chap80nt : 2; /* Microsoft (NT) CHAP */
+ unsigned chap80lm : 2; /* Microsoft (LANMan) CHAP */
+#endif
+ unsigned lqr : 2; /* Link Quality Report */
+ unsigned pap : 2; /* Password Authentication protocol */
+ unsigned protocomp : 2; /* Protocol field compression */
+ } cfg;
+};
+
+#define LCP_MAXCODE CODE_DISCREQ
+#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 */
+
+#define MAX_LCP_OPT_LEN 20
+struct lcp_opt {
+ u_char id;
+ u_char len;
+ u_char data[MAX_LCP_OPT_LEN-2];
+};
+
+#define INC_LCP_OPT(ty, length, o) \
+ do { \
+ (o)->id = (ty); \
+ (o)->len = (length); \
+ (o) = (struct lcp_opt *)((char *)(o) + (length)); \
+ } while (0)
+
+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_ReportStatus(struct cmdargs const *);
+extern void lcp_Input(struct lcp *, struct mbuf *);
+extern void lcp_SetupCallbacks(struct lcp *);
diff --git a/usr.sbin/ppp/lcpproto.h b/usr.sbin/ppp/lcpproto.h
new file mode 100644
index 0000000..8434822
--- /dev/null
+++ b/usr.sbin/ppp/lcpproto.h
@@ -0,0 +1,43 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcpproto.h,v 1.11 1998/05/21 21:46:05 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * 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 */
+#define PROTO_ICOMPD 0x00fb /* Individual link compressed */
+#define PROTO_COMPD 0x00fd /* Compressed datagram */
+
+#define PROTO_COMPRESSIBLE(p) (((p) & 0xffe1) == 0x21)
+
+#define PROTO_IPCP 0x8021
+#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
diff --git a/usr.sbin/ppp/link.c b/usr.sbin/ppp/link.c
new file mode 100644
index 0000000..1473969
--- /dev/null
+++ b/usr.sbin/ppp/link.c
@@ -0,0 +1,210 @@
+/*-
+ * 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.
+ *
+ * $Id: link.c,v 1.7 1999/02/06 02:54:46 brian Exp $
+ *
+ */
+
+#include <sys/types.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "defs.h"
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "lcpproto.h"
+#include "fsm.h"
+#include "descriptor.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "prompt.h"
+
+void
+link_AddInOctets(struct link *l, int n)
+{
+ throughput_addin(&l->throughput, n);
+}
+
+void
+link_AddOutOctets(struct link *l, int n)
+{
+ throughput_addout(&l->throughput, n);
+}
+
+void
+link_SequenceQueue(struct link *l)
+{
+ log_Printf(LogDEBUG, "link_SequenceQueue\n");
+ while (l->Queue[PRI_NORMAL].qlen)
+ mbuf_Enqueue(l->Queue + PRI_LINK, mbuf_Dequeue(l->Queue + PRI_NORMAL));
+}
+
+void
+link_DeleteQueue(struct link *l)
+{
+ struct mqueue *queue;
+
+ for (queue = l->Queue; queue < l->Queue + LINK_QUEUES; queue++)
+ while (queue->top)
+ mbuf_Free(mbuf_Dequeue(queue));
+}
+
+int
+link_QueueLen(struct link *l)
+{
+ int i, len;
+
+ for (i = 0, len = 0; i < LINK_QUEUES; i++)
+ len += l->Queue[i].qlen;
+
+ return len;
+}
+
+int
+link_QueueBytes(struct link *l)
+{
+ int i, len, bytes;
+ struct mbuf *m;
+
+ bytes = 0;
+ for (i = 0, len = 0; i < LINK_QUEUES; i++) {
+ len = l->Queue[i].qlen;
+ m = l->Queue[i].top;
+ while (len--) {
+ bytes += mbuf_Length(m);
+ m = m->pnext;
+ }
+ }
+
+ return bytes;
+}
+
+struct mbuf *
+link_Dequeue(struct link *l)
+{
+ int pri;
+ struct mbuf *bp;
+
+ for (bp = (struct mbuf *)0, pri = LINK_QUEUES - 1; pri >= 0; pri--)
+ if (l->Queue[pri].qlen) {
+ bp = mbuf_Dequeue(l->Queue + pri);
+ log_Printf(LogDEBUG, "link_Dequeue: Dequeued from queue %d,"
+ " containing %d more packets\n", pri, l->Queue[pri].qlen);
+ break;
+ }
+
+ return bp;
+}
+
+/*
+ * Write to the link. Actualy, requested packets are queued, and go out
+ * at some later time depending on the physical link implementation.
+ */
+void
+link_Write(struct link *l, int pri, const char *ptr, int count)
+{
+ struct mbuf *bp;
+
+ if(pri < 0 || pri >= LINK_QUEUES)
+ pri = 0;
+
+ bp = mbuf_Alloc(count, MB_LINK);
+ memcpy(MBUF_CTOP(bp), ptr, count);
+
+ mbuf_Enqueue(l->Queue + pri, bp);
+}
+
+void
+link_Output(struct link *l, int pri, struct mbuf *bp)
+{
+ struct mbuf *wp;
+ int len;
+
+ if(pri < 0 || pri >= LINK_QUEUES)
+ pri = 0;
+
+ len = mbuf_Length(bp);
+ wp = mbuf_Alloc(len, MB_LINK);
+ mbuf_Read(bp, MBUF_CTOP(wp), len);
+ mbuf_Enqueue(l->Queue + pri, wp);
+}
+
+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");
+}
diff --git a/usr.sbin/ppp/link.h b/usr.sbin/ppp/link.h
new file mode 100644
index 0000000..6a3858b
--- /dev/null
+++ b/usr.sbin/ppp/link.h
@@ -0,0 +1,69 @@
+/*-
+ * 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.
+ *
+ * $Id: link.h,v 1.3 1998/05/23 17:05:27 brian Exp $
+ *
+ */
+
+
+#define PHYSICAL_LINK 1
+#define MP_LINK 2
+
+#define LINK_QUEUES (PRI_MAX + 1)
+#define NPROTOSTAT 13
+
+struct bundle;
+struct prompt;
+
+struct link {
+ int type; /* _LINK type */
+ const char *name; /* Points to datalink::name */
+ int len; /* full size of parent struct */
+ struct pppThroughput throughput; /* Link throughput statistics */
+ struct mqueue Queue[LINK_QUEUES]; /* 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 */
+};
+
+extern void link_AddInOctets(struct link *, int);
+extern void link_AddOutOctets(struct link *, int);
+
+extern void link_SequenceQueue(struct link *);
+extern void link_DeleteQueue(struct link *);
+extern int link_QueueLen(struct link *);
+extern int link_QueueBytes(struct link *);
+extern struct mbuf *link_Dequeue(struct link *);
+extern void link_Write(struct link *, int, const char *, int);
+extern void link_StartOutput(struct link *, struct bundle *);
+extern void link_Output(struct link *, int, struct mbuf *);
+
+#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 *);
diff --git a/usr.sbin/ppp/log.c b/usr.sbin/ppp/log.c
new file mode 100644
index 0000000..93cea2e
--- /dev/null
+++ b/usr.sbin/ppp/log.c
@@ -0,0 +1,473 @@
+/*-
+ * 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.
+ *
+ * $Id: log.c,v 1.35 1998/08/21 18:10:14 brian Exp $
+ */
+
+#include <sys/types.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 *LogNames[] = {
+ "Async",
+ "CBCP",
+ "CCP",
+ "Chat",
+ "Command",
+ "Connect",
+ "Debug",
+ "HDLC",
+ "ID0",
+ "IPCP",
+ "LCP",
+ "LQM",
+ "Phase",
+ "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 */
+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 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)
+{
+ 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 < 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;
+
+ va_start(ap, fmt);
+ if (log_IsKept(lev)) {
+ char nfmt[200];
+
+ if ((log_IsKept(lev) & LOG_KEPT_LOCAL) && promptlist) {
+ 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);
+
+ for (prompt = promptlist; prompt; prompt = prompt->next)
+ if (lev > LogMAXCONF || (prompt->logmask & MSK(lev)))
+ prompt_vPrintf(prompt, nfmt, ap);
+ }
+
+ if ((log_IsKept(lev) & LOG_KEPT_SYSLOG) &&
+ (lev != LogWARN || !promptlist)) {
+ 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[50];
+ char *b;
+ const u_char *ptr;
+ int f;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+
+ b = buf;
+ do {
+ f = bp->cnt;
+ ptr = CONST_MBUF_CTOP(bp);
+ while (f--) {
+ sprintf(b, " %02x", (int) *ptr++);
+ b += 3;
+ if (b == buf + sizeof buf - 2) {
+ strcpy(b, "\n");
+ log_Printf(lev, buf);
+ b = buf;
+ }
+ }
+ } while ((bp = bp->next) != NULL);
+
+ if (b > buf) {
+ strcpy(b, "\n");
+ log_Printf(lev, buf);
+ }
+ }
+}
+
+void
+log_DumpBuff(int lev, const char *hdr, const u_char * ptr, int n)
+{
+ if (log_IsKept(lev)) {
+ char buf[50];
+ char *b;
+
+ if (hdr && *hdr)
+ log_Printf(lev, "%s\n", hdr);
+ while (n > 0) {
+ b = buf;
+ for (b = buf; b != buf + sizeof buf - 2 && n--; b += 3)
+ sprintf(b, " %02x", (int) *ptr++);
+ strcpy(b, "\n");
+ log_Printf(lev, 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;
+ 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..1da385c
--- /dev/null
+++ b/usr.sbin/ppp/log.h
@@ -0,0 +1,96 @@
+/*-
+ * 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.
+ *
+ * $Id: log.h,v 1.22 1998/08/07 18:42:49 brian Exp $
+ */
+
+#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 LogHDLC (8)
+#define LogID0 (9)
+#define LogIPCP (10)
+#define LogLCP (11)
+#define LogLQM (12)
+#define LogPHASE (13)
+#define LogTCPIP (14)
+#define LogTIMER (15) /* syslog(LOG_DEBUG, ....) */
+#define LogTUN (16) /* If set, tun%d is output with each message */
+#define LogMAXCONF (16)
+#define LogWARN (17) /* Sent to VarTerm else syslog(LOG_WARNING, ) */
+#define LogERROR (18) /* syslog(LOG_ERR, ....), + sent to VarTerm */
+#define LogALERT (19) /* syslog(LOG_ALERT, ....) */
+#define LogMAX (19)
+
+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 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..82bf6df
--- /dev/null
+++ b/usr.sbin/ppp/lqr.c
@@ -0,0 +1,321 @@
+/*
+ * PPP Line Quality Monitoring (LQM) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lqr.c,v 1.31 1999/01/28 01:56:33 brian Exp $
+ *
+ * o LQR based on RFC1333
+ *
+ * TODO:
+ * o LQM policy
+ * o Allow user to configure LQM method and interval.
+ */
+
+#include <sys/param.h>
+#include <sys/un.h>
+
+#include <string.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "lqr.h"
+#include "hdlc.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);
+}
+
+void
+lqr_RecvEcho(struct fsm *fp, struct mbuf * bp)
+{
+ struct hdlc *hdlc = &link2physical(fp->link)->hdlc;
+ struct echolqr *lqr;
+ u_int32_t seq;
+
+ if (mbuf_Length(bp) == sizeof(struct echolqr)) {
+ lqr = (struct echolqr *) MBUF_CTOP(bp);
+ if (ntohl(lqr->signature) == SIGNATURE) {
+ seq = ntohl(lqr->sequence);
+ /* careful not to update lqm.echo.seq_recv with older values */
+ if ((hdlc->lqm.echo.seq_recv > (u_int32_t)0 - 5 && seq < 5) ||
+ (hdlc->lqm.echo.seq_recv <= (u_int32_t)0 - 5 &&
+ seq > hdlc->lqm.echo.seq_recv))
+ hdlc->lqm.echo.seq_recv = seq;
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got sig 0x%08lx, not 0x%08lx !\n",
+ (u_long)ntohl(lqr->signature), (u_long)SIGNATURE);
+ } else
+ log_Printf(LogWARN, "lqr_RecvEcho: Got packet size %d, expecting %ld !\n",
+ mbuf_Length(bp), (long)sizeof(struct echolqr));
+}
+
+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++)
+ *dp++ = ntohl(*sp++);
+}
+
+static void
+SendLqrData(struct lcp *lcp)
+{
+ struct mbuf *bp;
+
+ bp = mbuf_Alloc(sizeof(struct lqrdata), MB_LQR);
+ hdlc_Output(lcp->fsm.link, PRI_LINK, PROTO_LQR, bp);
+}
+
+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);
+}
+
+void
+lqr_Input(struct physical *physical, struct mbuf *bp)
+{
+ int len;
+
+ len = mbuf_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(physical->link.lcp.cfg.lqr) &&
+ !(physical->hdlc.lqm.method & LQM_LQR)) {
+ bp->offset -= 2; /* XXX: We have a bit too much knowledge here ! */
+ bp->cnt += 2;
+ lcp_SendProtoRej(physical->hdlc.lqm.owner, MBUF_CTOP(bp), bp->cnt);
+ } else {
+ struct lqrdata *lqr;
+ struct lcp *lcp;
+ u_int32_t lastLQR;
+
+ bp = mbuf_Contiguous(bp);
+ lqr = (struct lqrdata *)MBUF_CTOP(bp);
+ lcp = physical->hdlc.lqm.owner;
+ if (ntohl(lqr->MagicNumber) != physical->hdlc.lqm.owner->his_magic)
+ log_Printf(LogWARN, "lqr_Input: magic 0x%08lx is wrong,"
+ " expecting 0x%08lx\n",
+ (u_long)ntohl(lqr->MagicNumber),
+ (u_long)physical->hdlc.lqm.owner->his_magic);
+ else {
+ /*
+ * Remember our PeerInLQRs, then convert byte order and save
+ */
+ lastLQR = physical->hdlc.lqm.lqr.peer.PeerInLQRs;
+
+ lqr_ChangeOrder(lqr, &physical->hdlc.lqm.lqr.peer);
+ lqr_Dump(physical->link.name, "Input", &physical->hdlc.lqm.lqr.peer);
+ /* we have received an LQR from peer */
+ physical->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 (physical->hdlc.lqm.timer.load == 0 ||
+ !(physical->hdlc.lqm.method & LQM_LQR) ||
+ (lastLQR && lastLQR == physical->hdlc.lqm.lqr.peer.PeerInLQRs) ||
+ (physical->hdlc.lqm.lqr.peer_timeout &&
+ physical->hdlc.lqm.timer.rest * 100 / SECTICKS >
+ physical->hdlc.lqm.lqr.peer_timeout))
+ SendLqrData(physical->hdlc.lqm.owner);
+ }
+ }
+ mbuf_Free(bp);
+}
+
+/*
+ * 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);
+ }
+}
diff --git a/usr.sbin/ppp/lqr.h b/usr.sbin/ppp/lqr.h
new file mode 100644
index 0000000..f562828
--- /dev/null
+++ b/usr.sbin/ppp/lqr.h
@@ -0,0 +1,59 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lqr.h,v 1.12.2.6 1998/05/08 01:15:09 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * 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;
+
+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 void lqr_RecvEcho(struct fsm *, struct mbuf *);
+extern void lqr_Input(struct physical *, struct mbuf *);
diff --git a/usr.sbin/ppp/main.c b/usr.sbin/ppp/main.c
new file mode 100644
index 0000000..4eb99d4
--- /dev/null
+++ b/usr.sbin/ppp/main.c
@@ -0,0 +1,578 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: main.c,v 1.151 1999/03/07 01:02:38 brian Exp $
+ *
+ * TODO:
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+
+#ifndef NOALIAS
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#endif
+#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 "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#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 const char *ex_desc(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)
+{
+ server_Close(SignalBundle);
+ log_Printf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
+ 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)
+{
+ 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 ! */
+ server_Close(SignalBundle);
+}
+
+static const char *
+ex_desc(int ex)
+{
+ static char num[12]; /* Used immediately if returned */
+ static const char *desc[] = {
+ "normal", "start", "sock", "modem", "dial", "dead", "done",
+ "reboot", "errdead", "hangup", "term", "nodial", "nologin"
+ };
+
+ if (ex >= 0 && ex < sizeof desc / sizeof *desc)
+ return desc[ex];
+ snprintf(num, sizeof num, "%d", ex);
+ return num;
+}
+
+static void
+Usage(void)
+{
+ fprintf(stderr,
+ "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ]"
+#ifndef NOALIAS
+ " [ -alias ]"
+#endif
+ " [system ...]\n");
+ exit(EX_START);
+}
+
+static int
+ProcessArgs(int argc, char **argv, int *mode, int *alias)
+{
+ int optc, newmode, arg;
+ char *cp;
+
+ optc = 0;
+ *mode = PHYS_INTERACTIVE;
+ *alias = 0;
+ for (arg = 1; arg < argc && *argv[arg] == '-'; arg++, optc++) {
+ cp = argv[arg] + 1;
+ newmode = Nam2mode(cp);
+ switch (newmode) {
+ case PHYS_NONE:
+ if (strcmp(cp, "alias") == 0) {
+#ifdef NOALIAS
+ log_Printf(LogWARN, "Cannot load alias library (compiled out)\n");
+#else
+ *alias = 1;
+#endif
+ optc--; /* this option isn't exclusive */
+ } else
+ Usage();
+ break;
+
+ case PHYS_ALL:
+ Usage();
+ break;
+
+ default:
+ *mode = newmode;
+ }
+ }
+
+ if (optc > 1) {
+ fprintf(stderr, "You may specify only one mode.\n");
+ exit(EX_START);
+ }
+
+ if (*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 nfds, mode, alias, label, arg;
+ struct bundle *bundle;
+ struct prompt *prompt;
+
+ nfds = getdtablesize();
+ if (nfds >= FD_SETSIZE)
+ /*
+ * If we've got loads of file descriptors, make sure they're all
+ * closed. If they aren't, we may end up with a seg fault when our
+ * `fd_set's get too big when select()ing !
+ */
+ while (--nfds > 2)
+ close(nfds);
+
+ name = strrchr(argv[0], '/');
+ log_Open(name ? name + 1 : argv[0]);
+
+#ifndef NOALIAS
+ PacketAliasInit();
+#endif
+ label = ProcessArgs(argc, argv, &mode, &alias);
+
+ /*
+ * 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 (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 (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", _PATH_PPP, CONFFILE);
+ do {
+ if (!access(conf, W_OK)) {
+ 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, mode);
+ else
+ CheckLabel("default", prompt, mode);
+
+ prompt_Printf(prompt, "Working in %s mode\n", mode2Nam(mode));
+
+ if ((bundle = bundle_Create(TUN_PREFIX, mode, (const char **)argv)) == NULL) {
+ log_Printf(LogWARN, "bundle_Create: %s\n", strerror(errno));
+ 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 */
+ prompt_Printf(prompt, "Using interface: %s\n", bundle->iface->name);
+ }
+ SignalBundle = bundle;
+ bundle->AliasEnabled = alias;
+ if (alias)
+ 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 (mode == PHYS_INTERACTIVE)
+ sig_signal(SIGTSTP, TerminalStop);
+
+ sig_signal(SIGUSR2, BringDownServer);
+
+ lastlabel = argc == 2 ? bundle->argv1 : 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, arg == 1 ? bundle->argv1 : argv[arg],
+ CONFFILE, prompt, NULL);
+ }
+
+ if (label < argc)
+ /* In case the last label did a ``load'' */
+ bundle_SetLabel(bundle, lastlabel);
+
+ if (mode == PHYS_AUTO &&
+ bundle->ncp.ipcp.cfg.peer_range.ipaddr.s_addr == INADDR_ANY) {
+ prompt_Printf(prompt, "You must ``set ifaddr'' with a peer address "
+ "in auto mode.\n");
+ AbortProgram(EX_START);
+ }
+
+ if (mode != PHYS_INTERACTIVE) {
+ if (mode != PHYS_DIRECT) {
+ int bgpipe[2];
+ pid_t bgpid;
+
+ if (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;
+
+ if (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 */
+ if (read(bgpipe[0], &c, 1) != 1) {
+ prompt_Printf(prompt, "Child exit, no status.\n");
+ log_Printf(LogPHASE, "Parent: Child exit, no status.\n");
+ } else if (c == EX_NORMAL) {
+ prompt_Printf(prompt, "PPP enabled.\n");
+ log_Printf(LogPHASE, "Parent: PPP enabled.\n");
+ } else {
+ prompt_Printf(prompt, "Child failed (%s).\n", ex_desc((int) c));
+ log_Printf(LogPHASE, "Parent: Child failed (%s).\n",
+ ex_desc((int) c));
+ }
+ close(bgpipe[0]);
+ }
+ return c;
+ } else if (mode == PHYS_BACKGROUND) {
+ close(bgpipe[0]);
+ bundle->notify.fd = bgpipe[1];
+ }
+
+ bundle_LockTun(bundle); /* we have a new pid */
+
+ /* -auto, -dedicated, -ddial & -background */
+ prompt_Destroy(prompt, 0);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ close(STDIN_FILENO);
+ setsid();
+ } else {
+ /* -direct: STDIN_FILENO gets used by modem_Open */
+ prompt_TtyInit(NULL);
+ close(STDOUT_FILENO);
+ close(STDERR_FILENO);
+ }
+ } else {
+ /* Interactive mode */
+ close(STDERR_FILENO);
+ prompt_TtyInit(prompt);
+ prompt_TtyCommandMode(prompt);
+ prompt_Required(prompt);
+ }
+
+ log_Printf(LogPHASE, "PPP Started (%s mode).\n", mode2Nam(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;
+ struct probe probe;
+
+ probe_Init(&probe);
+
+ do {
+ nfds = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&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);
+
+ 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) && nothing_done) {
+ /*
+ * This is disasterous. 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);
+ }
+
+ } while (bundle_CleanDatalinks(bundle), !bundle_IsDead(bundle));
+
+ 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..8d84caf
--- /dev/null
+++ b/usr.sbin/ppp/main.h
@@ -0,0 +1,25 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: main.h,v 1.9.2.9 1998/04/07 00:54:08 brian Exp $
+ *
+ */
+
+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..0e29c81
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.c
@@ -0,0 +1,232 @@
+/*
+ * PPP Memory handling module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: mbuf.c,v 1.23 1999/02/06 02:54:47 brian Exp $
+ *
+ */
+#include <sys/types.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"
+
+static struct memmap {
+ struct mbuf *queue;
+ int fragments, octets;
+} MemMap[MB_MAX + 2];
+
+static int totalalloced;
+
+int
+mbuf_Length(struct mbuf * bp)
+{
+ int len;
+
+ for (len = 0; bp; bp = bp->next)
+ len += bp->cnt;
+ return (len);
+}
+
+struct mbuf *
+mbuf_Alloc(int cnt, int type)
+{
+ struct mbuf *bp;
+
+ if (type > MB_MAX)
+ log_Printf(LogERROR, "Bad mbuf type %d\n", type);
+ bp = malloc(sizeof(struct mbuf) + cnt);
+ if (bp == NULL) {
+ log_Printf(LogALERT, "failed to allocate memory: %ld\n",
+ (long)sizeof(struct mbuf));
+ AbortProgram(EX_OSERR);
+ }
+ memset(bp, '\0', sizeof(struct mbuf));
+ MemMap[type].fragments++;
+ MemMap[type].octets += cnt;
+ totalalloced += cnt;
+ bp->size = bp->cnt = cnt;
+ bp->type = type;
+ return bp;
+}
+
+struct mbuf *
+mbuf_FreeSeg(struct mbuf * bp)
+{
+ struct mbuf *nbp;
+
+ if (bp) {
+ nbp = bp->next;
+ MemMap[bp->type].fragments--;
+ MemMap[bp->type].octets -= bp->size;
+ totalalloced -= bp->size;
+ free(bp);
+ bp = nbp;
+ }
+
+ return bp;
+}
+
+void
+mbuf_Free(struct mbuf * bp)
+{
+ while (bp)
+ bp = mbuf_FreeSeg(bp);
+}
+
+struct mbuf *
+mbuf_Read(struct mbuf * bp, u_char * ptr, int len)
+{
+ int nb;
+
+ while (bp && len > 0) {
+ if (len > bp->cnt)
+ nb = bp->cnt;
+ else
+ nb = len;
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ bp->cnt -= nb;
+ len -= nb;
+ bp->offset += nb;
+ if (bp->cnt == 0)
+ bp = mbuf_FreeSeg(bp);
+ }
+ return (bp);
+}
+
+void
+mbuf_Write(struct mbuf * bp, u_char * ptr, int cnt)
+{
+ int plen;
+ int nb;
+
+ plen = mbuf_Length(bp);
+ if (plen < cnt)
+ cnt = plen;
+
+ while (cnt > 0) {
+ nb = (cnt < bp->cnt) ? cnt : bp->cnt;
+ memcpy(MBUF_CTOP(bp), ptr, nb);
+ cnt -= bp->cnt;
+ bp = bp->next;
+ }
+}
+
+int
+mbuf_Show(struct cmdargs const *arg)
+{
+ int i;
+ static const char *mbuftype[] = {
+ "async", "fsm", "cbcp", "hdlcout", "ipin", "echo", "lqr", "link",
+ "vjcomp", "ipq", "mp" };
+
+ prompt_Printf(arg->prompt, "Fragments (octets) in use:\n");
+ for (i = 1; i < MB_MAX; i += 2)
+ prompt_Printf(arg->prompt, "%10.10s: %04d (%06d)\t%10.10s: %04d (%06d)\n",
+ mbuftype[i-1], MemMap[i].fragments, MemMap[i].octets, mbuftype[i],
+ MemMap[i+1].fragments, MemMap[i+1].octets);
+
+ if (i == MB_MAX)
+ prompt_Printf(arg->prompt, "%10.10s: %04d (%06d)\n",
+ mbuftype[i-1], MemMap[i].fragments, MemMap[i].octets);
+
+ return 0;
+}
+
+void
+mbuf_Log()
+{
+ log_Printf(LogDEBUG, "mbuf_Log: mem alloced: %d\n", totalalloced);
+ log_Printf(LogDEBUG, "mbuf_Log: 1: %d 2: %d 3: %d 4: %d\n",
+ MemMap[1].octets, MemMap[2].octets, MemMap[3].octets, MemMap[4].octets);
+ log_Printf(LogDEBUG, "mbuf_Log: 5: %d 6: %d 7: %d 8: %d\n",
+ MemMap[5].octets, MemMap[6].octets, MemMap[7].octets, MemMap[8].octets);
+ log_Printf(LogDEBUG, "mbuf_Log: 9: %d 10: %d 11: %d\n",
+ MemMap[9].octets, MemMap[10].octets, MemMap[11].octets);
+}
+
+struct mbuf *
+mbuf_Dequeue(struct mqueue *q)
+{
+ struct mbuf *bp;
+
+ log_Printf(LogDEBUG, "mbuf_Dequeue: queue len = %d\n", q->qlen);
+ bp = q->top;
+ if (bp) {
+ q->top = q->top->pnext;
+ q->qlen--;
+ if (q->top == NULL) {
+ q->last = q->top;
+ if (q->qlen)
+ log_Printf(LogERROR, "mbuf_Dequeue: Not zero (%d)!!!\n", q->qlen);
+ }
+ bp->pnext = NULL;
+ }
+
+ return bp;
+}
+
+void
+mbuf_Enqueue(struct mqueue *queue, struct mbuf *bp)
+{
+ if (queue->last) {
+ queue->last->pnext = bp;
+ queue->last = bp;
+ } else
+ queue->last = queue->top = bp;
+ queue->qlen++;
+ log_Printf(LogDEBUG, "mbuf_Enqueue: len = %d\n", queue->qlen);
+}
+
+struct mbuf *
+mbuf_Contiguous(struct mbuf *bp)
+{
+ /* Put it all in one contigous (aligned) mbuf */
+
+ if (bp->next != NULL) {
+ struct mbuf *nbp;
+ u_char *cp;
+
+ nbp = mbuf_Alloc(mbuf_Length(bp), bp->type);
+
+ for (cp = MBUF_CTOP(nbp); bp; bp = mbuf_FreeSeg(bp)) {
+ memcpy(cp, MBUF_CTOP(bp), bp->cnt);
+ cp += bp->cnt;
+ }
+ bp = nbp;
+ }
+#ifndef __i386__ /* Do any other archs not care about alignment ? */
+ else if ((bp->offset & 0x03) != 0) {
+ bcopy(MBUF_CTOP(bp), bp + 1, bp->cnt);
+ bp->offset = 0;
+ }
+#endif
+
+ return bp;
+}
diff --git a/usr.sbin/ppp/mbuf.h b/usr.sbin/ppp/mbuf.h
new file mode 100644
index 0000000..1ee8e6f
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.h
@@ -0,0 +1,67 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: mbuf.h,v 1.14 1998/08/21 18:10:15 brian Exp $
+ *
+ * TODO:
+ */
+
+struct mbuf {
+ short size; /* size allocated (excluding header) */
+ short offset; /* offset from header end to start position */
+ short cnt; /* available byte count in buffer */
+ short type; /* MB_* below */
+ struct mbuf *next; /* link to next mbuf */
+ struct mbuf *pnext; /* link to next packet */
+ /* buffer space is malloc()d directly after the header */
+};
+
+struct mqueue {
+ struct mbuf *top;
+ struct mbuf *last;
+ int qlen;
+};
+
+#define MBUF_CTOP(bp) ((u_char *)((bp)+1) + (bp)->offset)
+#define CONST_MBUF_CTOP(bp) ((const u_char *)((bp)+1) + (bp)->offset)
+
+#define MB_ASYNC 1
+#define MB_FSM 2
+#define MB_CBCP 3
+#define MB_HDLCOUT 4
+#define MB_IPIN 5
+#define MB_ECHO 6
+#define MB_LQR 7
+#define MB_LINK 8
+#define MB_VJCOMP 9
+#define MB_IPQ 10
+#define MB_MP 11
+#define MB_MAX MB_MP
+
+struct cmdargs;
+
+extern int mbuf_Length(struct mbuf *);
+extern struct mbuf *mbuf_Alloc(int, int);
+extern struct mbuf *mbuf_FreeSeg(struct mbuf *);
+extern void mbuf_Free(struct mbuf *);
+extern void mbuf_Write(struct mbuf *, u_char *, int);
+extern struct mbuf *mbuf_Read(struct mbuf *, u_char *, int);
+extern void mbuf_Log(void);
+extern int mbuf_Show(struct cmdargs const *);
+extern void mbuf_Enqueue(struct mqueue *, struct mbuf *);
+extern struct mbuf *mbuf_Dequeue(struct mqueue *);
+extern struct mbuf *mbuf_Contiguous(struct mbuf *);
diff --git a/usr.sbin/ppp/modem.c b/usr.sbin/ppp/modem.c
new file mode 100644
index 0000000..b7908c9
--- /dev/null
+++ b/usr.sbin/ppp/modem.c
@@ -0,0 +1,1131 @@
+/*
+ * PPP Modem handling module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: modem.c,v 1.108 1999/04/03 11:54:00 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.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/tty.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <sys/ioctl.h>
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#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 "modem.h"
+#include "throughput.h"
+#include "async.h"
+#include "iplist.h"
+#include "slcompress.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 "bundle.h"
+#include "prompt.h"
+#include "chat.h"
+#include "auth.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+
+static int modem_DescriptorWrite(struct descriptor *, struct bundle *,
+ const fd_set *);
+static void modem_DescriptorRead(struct descriptor *, struct bundle *,
+ const fd_set *);
+static int modem_UpdateSet(struct descriptor *, fd_set *, fd_set *, fd_set *,
+ int *);
+
+struct physical *
+modem_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;
+ throughput_init(&p->link.throughput);
+ memset(&p->Timer, '\0', sizeof p->Timer);
+ 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);
+
+ p->desc.type = PHYSICAL_DESCRIPTOR;
+ p->desc.UpdateSet = modem_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = modem_DescriptorRead;
+ p->desc.Write = modem_DescriptorWrite;
+ p->type = type;
+
+ hdlc_Init(&p->hdlc, &p->link.lcp);
+ async_Init(&p->async);
+
+ p->fd = -1;
+ p->mbits = 0;
+ p->isatty = 0;
+ 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;
+ strncpy(p->cfg.devlist, MODEM_LIST, sizeof p->cfg.devlist - 1);
+ p->cfg.devlist[sizeof p->cfg.devlist - 1] = '\0';
+ p->cfg.cd.required = 0;
+ p->cfg.cd.delay = DEF_CDDELAY;
+
+ lcp_Init(&p->link.lcp, dl->bundle, &p->link, &dl->fsmp);
+ ccp_Init(&p->link.ccp, dl->bundle, &p->link, &dl->fsmp);
+
+ return p;
+}
+
+/* XXX-ML this should probably change when we add support for other
+ types of devices */
+#define Online(modem) ((modem)->mbits & TIOCM_CD)
+
+static void modem_LogicalClose(struct physical *);
+
+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 EXTA
+ { 19200, EXTA, },
+#endif
+#ifdef EXTB
+ { 38400, EXTB, },
+#endif
+#endif /* _POSIX_SOURCE */
+ { 0, 0 }
+};
+
+static 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;
+}
+
+static void
+modem_SetDevice(struct physical *physical, const char *name)
+{
+ int len = strlen(_PATH_DEV);
+
+ strncpy(physical->name.full, name, sizeof physical->name.full - 1);
+ physical->name.full[sizeof physical->name.full - 1] = '\0';
+ physical->name.base = strncmp(physical->name.full, _PATH_DEV, len) ?
+ physical->name.full : physical->name.full + len;
+}
+
+/*
+ * modem_Timeout() watches DCD signal and notifies if it's status is changed.
+ *
+ */
+static void
+modem_Timeout(void *data)
+{
+ struct physical *modem = data;
+ int ombits = modem->mbits;
+ int change;
+
+ timer_Stop(&modem->Timer);
+ modem->Timer.load = SECTICKS; /* Once a second please */
+ timer_Start(&modem->Timer);
+
+ if (modem->isatty || physical_IsSync(modem)) {
+ ombits = modem->mbits;
+
+ if (modem->fd >= 0) {
+ if (ioctl(modem->fd, TIOCMGET, &modem->mbits) < 0) {
+ log_Printf(LogPHASE, "%s: ioctl error (%s)!\n", modem->link.name,
+ strerror(errno));
+ datalink_Down(modem->dl, CLOSE_NORMAL);
+ return;
+ }
+ } else
+ modem->mbits = 0;
+
+ if (ombits == -1) {
+ /* First time looking for carrier */
+ if (Online(modem))
+ log_Printf(LogDEBUG, "%s: %s: CD detected\n",
+ modem->link.name, modem->name.full);
+ else if (modem->cfg.cd.required) {
+ log_Printf(LogPHASE, "%s: %s: Required CD not detected\n",
+ modem->link.name, modem->name.full);
+ datalink_Down(modem->dl, CLOSE_NORMAL);
+ } else {
+ log_Printf(LogPHASE, "%s: %s doesn't support CD\n",
+ modem->link.name, modem->name.full);
+ timer_Stop(&modem->Timer);
+ modem->mbits = TIOCM_CD;
+ }
+ } else {
+ change = ombits ^ modem->mbits;
+ if (change & TIOCM_CD) {
+ if (modem->mbits & TIOCM_CD)
+ log_Printf(LogDEBUG, "%s: offline -> online\n", modem->link.name);
+ else {
+ log_Printf(LogDEBUG, "%s: online -> offline\n", modem->link.name);
+ log_Printf(LogPHASE, "%s: Carrier lost\n", modem->link.name);
+ datalink_Down(modem->dl, CLOSE_NORMAL);
+ }
+ } else
+ log_Printf(LogDEBUG, "%s: Still %sline\n", modem->link.name,
+ Online(modem) ? "on" : "off");
+ }
+ } else if (!Online(modem)) {
+ /* mbits was set to zero in modem_Open() */
+ modem->mbits = TIOCM_CD;
+ }
+}
+
+static void
+modem_StartTimer(struct bundle *bundle, struct physical *modem)
+{
+ timer_Stop(&modem->Timer);
+ modem->Timer.load = SECTICKS * modem->cfg.cd.delay;
+ modem->Timer.func = modem_Timeout;
+ modem->Timer.name = "modem CD";
+ modem->Timer.arg = modem;
+ log_Printf(LogDEBUG, "%s: Using modem_Timeout [%p]\n",
+ modem->link.name, modem_Timeout);
+ modem->mbits = -1; /* So we know it's the first time */
+ timer_Start(&modem->Timer);
+}
+
+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
+modem_SetParity(struct physical *modem, const char *str)
+{
+ struct termios rstio;
+ int val;
+
+ val = GetParityValue(str);
+ if (val > 0) {
+ modem->cfg.parity = val;
+ tcgetattr(modem->fd, &rstio);
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= val;
+ tcsetattr(modem->fd, TCSADRAIN, &rstio);
+ return 0;
+ }
+ log_Printf(LogWARN, "%s: %s: Invalid parity\n", modem->link.name, str);
+ return -1;
+}
+
+static int
+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.s_addr = inet_addr(host);
+ dest.sin_addr = GetIpAddr(host);
+ if (dest.sin_addr.s_addr == INADDR_NONE) {
+ log_Printf(LogWARN, "%s: %s: unknown host\n", name, host);
+ return (-1);
+ }
+ 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 (-1);
+ }
+ }
+ log_Printf(LogPHASE, "%s: Connecting to %s:%s\n", name, host, port);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ return (sock);
+ }
+ if (connect(sock, (struct sockaddr *)&dest, sizeof dest) < 0) {
+ log_Printf(LogWARN, "%s: connect: %s\n", name, strerror(errno));
+ close(sock);
+ return (-1);
+ }
+ return (sock);
+}
+
+static int
+modem_lock(struct physical *modem, int tunno)
+{
+ int res;
+ FILE *lockfile;
+ char fn[MAXPATHLEN];
+
+ if (*modem->name.full != '/')
+ return 0;
+
+ if (modem->type != PHYS_DIRECT &&
+ (res = ID0uu_lock(modem->name.base)) != UU_LOCK_OK) {
+ if (res == UU_LOCK_INUSE)
+ log_Printf(LogPHASE, "%s: %s is in use\n",
+ modem->link.name, modem->name.full);
+ else
+ log_Printf(LogPHASE, "%s: %s is in use: uu_lock: %s\n",
+ modem->link.name, modem->name.full, uu_lockerr(res));
+ return (-1);
+ }
+
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, modem->name.base);
+ lockfile = ID0fopen(fn, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%s%d\n", TUN_NAME, tunno);
+ fclose(lockfile);
+ }
+#ifndef RELEASE_CRUNCH
+ else
+ log_Printf(LogALERT, "%s: Can't create %s: %s\n",
+ modem->link.name, fn, strerror(errno));
+#endif
+
+ return 0;
+}
+
+static void
+modem_Unlock(struct physical *modem)
+{
+ char fn[MAXPATHLEN];
+
+ if (*modem->name.full != '/')
+ return;
+
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, modem->name.base);
+#ifndef RELEASE_CRUNCH
+ if (ID0unlink(fn) == -1)
+ log_Printf(LogALERT, "%s: Can't remove %s: %s\n",
+ modem->link.name, fn, strerror(errno));
+#else
+ ID0unlink(fn);
+#endif
+
+ if (modem->type != PHYS_DIRECT && ID0uu_unlock(modem->name.base) == -1)
+ log_Printf(LogALERT, "%s: Can't uu_unlock %s\n", modem->link.name, fn);
+}
+
+static void
+modem_Found(struct physical *modem, struct bundle *bundle)
+{
+ throughput_start(&modem->link.throughput, "modem throughput",
+ Enabled(bundle, OPT_THROUGHPUT));
+ modem->connect_count++;
+ modem->input.sz = 0;
+ log_Printf(LogPHASE, "%s: Connected!\n", modem->link.name);
+}
+
+int
+modem_Open(struct physical *modem, struct bundle *bundle)
+{
+ struct termios rstio;
+ int oldflag;
+ char *host, *port;
+ char *cp;
+ char tmpDeviceList[sizeof modem->cfg.devlist];
+ char *tmpDevice;
+
+ if (modem->fd >= 0)
+ log_Printf(LogDEBUG, "%s: Open: Modem is already open!\n", modem->link.name);
+ /* We're going back into "term" mode */
+ else if (modem->type == PHYS_DIRECT) {
+ if (isatty(STDIN_FILENO)) {
+ log_Printf(LogDEBUG, "%s: Open(direct): Modem is a tty\n",
+ modem->link.name);
+ modem_SetDevice(modem, ttyname(STDIN_FILENO));
+ if (modem_lock(modem, bundle->unit) == -1) {
+ close(STDIN_FILENO);
+ return -1;
+ }
+ modem->fd = STDIN_FILENO;
+ modem_Found(modem, bundle);
+ } else {
+ log_Printf(LogDEBUG, "%s: Open(direct): Modem is not a tty\n",
+ modem->link.name);
+ modem_SetDevice(modem, "");
+ /* We don't call modem_Timeout() with this type of connection */
+ modem_Found(modem, bundle);
+ return modem->fd = STDIN_FILENO;
+ }
+ } else {
+ strncpy(tmpDeviceList, modem->cfg.devlist, sizeof tmpDeviceList - 1);
+ tmpDeviceList[sizeof tmpDeviceList - 1] = '\0';
+
+ for(tmpDevice=strtok(tmpDeviceList, ", "); tmpDevice && modem->fd < 0;
+ tmpDevice=strtok(NULL,", ")) {
+ modem_SetDevice(modem, tmpDevice);
+
+ if (*modem->name.full == '/') {
+ if (modem_lock(modem, bundle->unit) != -1) {
+ modem->fd = ID0open(modem->name.full, O_RDWR | O_NONBLOCK);
+ if (modem->fd < 0) {
+ log_Printf(LogPHASE, "%s: Open(\"%s\"): %s\n",
+ modem->link.name, modem->name.full, strerror(errno));
+ modem_Unlock(modem);
+ } else {
+ modem_Found(modem, bundle);
+ log_Printf(LogDEBUG, "%s: Opened %s\n",
+ modem->link.name, modem->name.full);
+ }
+ }
+ } else if (*modem->name.full == '!') {
+ /* PPP via an external program */
+ /*
+ * XXX: Fix me - this should be another sort of link (similar to a
+ * physical
+ */
+ int fids[2];
+
+ modem->name.base = modem->name.full + 1;
+ if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, fids) < 0)
+ log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
+ strerror(errno));
+ else {
+ int stat;
+ pid_t pid;
+
+ stat = fcntl(fids[0], F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ fcntl(fids[0], F_SETFL, stat);
+ }
+ switch ((pid = fork())) {
+ case -1:
+ log_Printf(LogPHASE, "Unable to create pipe for line exec: %s\n",
+ strerror(errno));
+ break;
+ case 0:
+ close(fids[0]);
+ timer_TermService();
+
+ fids[1] = fcntl(fids[1], F_DUPFD, 3);
+ dup2(fids[1], STDIN_FILENO);
+ dup2(fids[1], STDOUT_FILENO);
+ dup2(fids[1], STDERR_FILENO);
+ setuid(geteuid());
+ if (fork())
+ exit(127);
+ execlp(modem->name.base, modem->name.base, NULL);
+ fprintf(stderr, "execvp failed: %s: %s\n", modem->name.base,
+ strerror(errno));
+ exit(127);
+ break;
+ default:
+ close(fids[1]);
+ modem->fd = fids[0];
+ waitpid(pid, &stat, 0);
+ log_Printf(LogDEBUG, "Using descriptor %d for child\n",
+ modem->fd);
+ modem_Found(modem, bundle);
+ break;
+ }
+ }
+ } else {
+ /* PPP over TCP */
+ /*
+ * XXX: Fix me - this should be another sort of link (similar to a
+ * physical
+ */
+ cp = strchr(modem->name.full, ':');
+ if (cp) {
+ *cp = '\0';
+ host = modem->name.full;
+ port = cp + 1;
+ if (*host && *port) {
+ modem->fd = OpenConnection(modem->link.name, host, port);
+ *cp = ':'; /* Don't destroy name.full */
+ if (modem->fd >= 0) {
+ modem_Found(modem, bundle);
+ log_Printf(LogDEBUG, "%s: Opened socket %s\n", modem->link.name,
+ modem->name.full);
+ }
+ } else {
+ *cp = ':'; /* Don't destroy name.full */
+ log_Printf(LogWARN, "%s: Invalid host:port: \"%s\"\n",
+ modem->link.name, modem->name.full);
+ }
+ } else {
+ log_Printf(LogWARN, "%s: Device (%s) must begin with a '/',"
+ " a '!' or be a host:port pair\n", modem->link.name,
+ modem->name.full);
+ }
+ }
+ }
+
+ if (modem->fd < 0)
+ return modem->fd;
+ }
+
+ /*
+ * If we are working on tty device, change it's mode into the one desired
+ * for further operation.
+ */
+ modem->mbits = 0;
+ modem->isatty = isatty(modem->fd);
+ if (modem->isatty) {
+ tcgetattr(modem->fd, &rstio);
+ modem->ios = rstio;
+ log_Printf(LogDEBUG, "%s: Open: modem (get): fd = %d, iflag = %lx, "
+ "oflag = %lx, cflag = %lx\n", modem->link.name, modem->fd,
+ (u_long)rstio.c_iflag, (u_long)rstio.c_oflag,
+ (u_long)rstio.c_cflag);
+ cfmakeraw(&rstio);
+ if (modem->cfg.rts_cts)
+ rstio.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else {
+ rstio.c_cflag |= CLOCAL;
+ rstio.c_iflag |= IXOFF;
+ }
+ rstio.c_iflag |= IXON;
+ if (modem->type != PHYS_DEDICATED)
+ rstio.c_cflag |= HUPCL;
+
+ if (modem->type != PHYS_DIRECT) {
+ /* Change tty speed when we're not in -direct mode */
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= modem->cfg.parity;
+ if (cfsetspeed(&rstio, IntToSpeed(modem->cfg.speed)) == -1)
+ log_Printf(LogWARN, "%s: %s: Unable to set speed to %d\n",
+ modem->link.name, modem->name.full, modem->cfg.speed);
+ }
+ tcsetattr(modem->fd, TCSADRAIN, &rstio);
+ log_Printf(LogDEBUG, "%s: modem (put): iflag = %lx, oflag = %lx, "
+ "cflag = %lx\n", modem->link.name, (u_long)rstio.c_iflag,
+ (u_long)rstio.c_oflag, (u_long)rstio.c_cflag);
+
+ if (ioctl(modem->fd, TIOCMGET, &modem->mbits) == -1) {
+ if (modem->type != PHYS_DIRECT) {
+ log_Printf(LogWARN, "%s: Open: Cannot get modem status: %s\n",
+ modem->link.name, strerror(errno));
+ modem_LogicalClose(modem);
+ return (-1);
+ } else
+ modem->mbits = TIOCM_CD;
+ }
+ log_Printf(LogDEBUG, "%s: Open: modem control = %o\n",
+ modem->link.name, modem->mbits);
+
+ oldflag = fcntl(modem->fd, F_GETFL, 0);
+ if (oldflag < 0) {
+ log_Printf(LogWARN, "%s: Open: Cannot get modem flags: %s\n",
+ modem->link.name, strerror(errno));
+ modem_LogicalClose(modem);
+ return (-1);
+ }
+ fcntl(modem->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+ }
+
+ return modem->fd;
+}
+
+int
+modem_Speed(struct physical *modem)
+{
+ struct termios rstio;
+
+ if (!modem->isatty)
+ return 115200;
+
+ tcgetattr(modem->fd, &rstio);
+ return (SpeedToInt(cfgetispeed(&rstio)));
+}
+
+/*
+ * Put modem tty line into raw mode which is necessary in packet mode operation
+ */
+int
+modem_Raw(struct physical *modem, struct bundle *bundle)
+{
+ struct termios rstio;
+ int oldflag;
+
+ log_Printf(LogDEBUG, "%s: Entering modem_Raw\n", modem->link.name);
+
+ if (!modem->isatty || physical_IsSync(modem))
+ return 0;
+
+ if (modem->type != PHYS_DIRECT && modem->fd >= 0 && !Online(modem))
+ log_Printf(LogDEBUG, "%s: Raw: modem = %d, mbits = %x\n",
+ modem->link.name, modem->fd, modem->mbits);
+
+ tcgetattr(modem->fd, &rstio);
+ cfmakeraw(&rstio);
+ if (modem->cfg.rts_cts)
+ rstio.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else
+ rstio.c_cflag |= CLOCAL;
+
+ if (modem->type != PHYS_DEDICATED)
+ rstio.c_cflag |= HUPCL;
+
+ tcsetattr(modem->fd, TCSANOW, &rstio);
+
+ oldflag = fcntl(modem->fd, F_GETFL, 0);
+ if (oldflag < 0)
+ return (-1);
+ fcntl(modem->fd, F_SETFL, oldflag | O_NONBLOCK);
+
+ modem_StartTimer(bundle, modem);
+
+ return 0;
+}
+
+static void
+modem_Unraw(struct physical *modem)
+{
+ int oldflag;
+
+ if (modem->isatty && !physical_IsSync(modem)) {
+ tcsetattr(modem->fd, TCSAFLUSH, &modem->ios);
+ oldflag = fcntl(modem->fd, F_GETFL, 0);
+ if (oldflag < 0)
+ return;
+ (void) fcntl(modem->fd, F_SETFL, oldflag & ~O_NONBLOCK);
+ }
+}
+
+static void
+modem_PhysicalClose(struct physical *modem)
+{
+ int newsid;
+
+ log_Printf(LogDEBUG, "%s: Physical Close\n", modem->link.name);
+ timer_Stop(&modem->Timer);
+ newsid = tcgetpgrp(modem->fd) == getpgrp();
+ close(modem->fd);
+ modem->fd = -1;
+ log_SetTtyCommandMode(modem->dl);
+ throughput_stop(&modem->link.throughput);
+ throughput_log(&modem->link.throughput, LogPHASE, modem->link.name);
+ if (modem->session_owner != (pid_t)-1) {
+ ID0kill(modem->session_owner, SIGHUP);
+ modem->session_owner = (pid_t)-1;
+ }
+ if (newsid)
+ bundle_setsid(modem->dl->bundle, 0);
+}
+
+void
+modem_Offline(struct physical *modem)
+{
+ if (modem->fd >= 0) {
+ struct termios tio;
+
+ timer_Stop(&modem->Timer);
+ modem->mbits &= ~TIOCM_DTR;
+ if (modem->isatty && Online(modem)) {
+ tcgetattr(modem->fd, &tio);
+ if (cfsetspeed(&tio, B0) == -1)
+ log_Printf(LogWARN, "%s: Unable to set modem to speed 0\n",
+ modem->link.name);
+ else
+ tcsetattr(modem->fd, TCSANOW, &tio);
+ /* nointr_sleep(1); */
+ }
+ log_Printf(LogPHASE, "%s: Disconnected!\n", modem->link.name);
+ }
+}
+
+void
+modem_Close(struct physical *modem)
+{
+ if (modem->fd < 0)
+ return;
+
+ log_Printf(LogDEBUG, "%s: Close\n", modem->link.name);
+
+ if (!modem->isatty) {
+ modem_PhysicalClose(modem);
+ if (*modem->name.full == '/')
+ modem_Unlock(modem);
+ *modem->name.full = '\0';
+ modem->name.base = modem->name.full;
+ return;
+ }
+
+ if (modem->fd >= 0) {
+ tcflush(modem->fd, TCIOFLUSH);
+ modem_Unraw(modem);
+ modem_LogicalClose(modem);
+ }
+}
+
+void
+modem_Destroy(struct physical *modem)
+{
+ modem_Close(modem);
+ free(modem);
+}
+
+static void
+modem_LogicalClose(struct physical *modem)
+{
+ log_Printf(LogDEBUG, "%s: Logical Close\n", modem->link.name);
+ if (modem->fd >= 0) {
+ physical_Logout(modem);
+ modem_PhysicalClose(modem);
+ modem_Unlock(modem);
+ }
+ *modem->name.full = '\0';
+ modem->name.base = modem->name.full;
+}
+
+static int
+modem_DescriptorWrite(struct descriptor *d, struct bundle *bundle,
+ const fd_set *fdset)
+{
+ struct physical *modem = descriptor2physical(d);
+ int nw, result = 0;
+
+ if (modem->out == NULL)
+ modem->out = link_Dequeue(&modem->link);
+
+ if (modem->out) {
+ nw = physical_Write(modem, MBUF_CTOP(modem->out), modem->out->cnt);
+ log_Printf(LogDEBUG, "%s: DescriptorWrite: wrote %d(%d) to %d\n",
+ modem->link.name, nw, modem->out->cnt, modem->fd);
+ if (nw > 0) {
+ modem->out->cnt -= nw;
+ modem->out->offset += nw;
+ if (modem->out->cnt == 0)
+ modem->out = mbuf_FreeSeg(modem->out);
+ result = 1;
+ } else if (nw < 0) {
+ if (errno != EAGAIN) {
+ log_Printf(LogPHASE, "%s: write (%d): %s\n", modem->link.name,
+ modem->fd, strerror(errno));
+ datalink_Down(modem->dl, CLOSE_NORMAL);
+ }
+ result = 1;
+ }
+ /* else we shouldn't really have been called ! select() is broken ! */
+ }
+
+ return result;
+}
+
+int
+modem_ShowStatus(struct cmdargs const *arg)
+{
+ struct physical *modem = arg->cx->physical;
+#ifdef TIOCOUTQ
+ int nb;
+#endif
+
+ prompt_Printf(arg->prompt, "Name: %s\n", modem->link.name);
+ prompt_Printf(arg->prompt, " State: ");
+ if (modem->fd >= 0) {
+ if (modem->isatty)
+ prompt_Printf(arg->prompt, "open, %s carrier\n",
+ Online(modem) ? "with" : "no");
+ else
+ prompt_Printf(arg->prompt, "open\n");
+ } else
+ prompt_Printf(arg->prompt, "closed\n");
+ prompt_Printf(arg->prompt, " Device: %s",
+ *modem->name.full ? modem->name.full :
+ modem->type == PHYS_DIRECT ? "unknown" : "N/A");
+ if (modem->session_owner != (pid_t)-1)
+ prompt_Printf(arg->prompt, " (session owner: %d)",
+ (int)modem->session_owner);
+
+ prompt_Printf(arg->prompt, "\n Link Type: %s\n", mode2Nam(modem->type));
+ prompt_Printf(arg->prompt, " Connect Count: %d\n",
+ modem->connect_count);
+#ifdef TIOCOUTQ
+ if (modem->fd >= 0 && ioctl(modem->fd, TIOCOUTQ, &nb) >= 0)
+ prompt_Printf(arg->prompt, " Physical outq: %d\n", nb);
+#endif
+
+ prompt_Printf(arg->prompt, " Queued Packets: %d\n",
+ link_QueueLen(&modem->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: %s\n", modem->cfg.devlist);
+ prompt_Printf(arg->prompt, " Characteristics: ");
+ if (physical_IsSync(arg->cx->physical))
+ prompt_Printf(arg->prompt, "sync");
+ else
+ prompt_Printf(arg->prompt, "%dbps", modem->cfg.speed);
+
+ switch (modem->cfg.parity & CSIZE) {
+ case CS7:
+ prompt_Printf(arg->prompt, ", cs7");
+ break;
+ case CS8:
+ prompt_Printf(arg->prompt, ", cs8");
+ break;
+ }
+ if (modem->cfg.parity & PARENB) {
+ if (modem->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",
+ (modem->cfg.rts_cts ? "on" : "off"));
+
+ prompt_Printf(arg->prompt, " CD check delay: %d second%s",
+ modem->cfg.cd.delay, modem->cfg.cd.delay == 1 ? "" : "s");
+ if (modem->cfg.cd.required)
+ prompt_Printf(arg->prompt, " (required!)\n\n");
+ else
+ prompt_Printf(arg->prompt, "\n\n");
+
+ throughput_disp(&modem->link.throughput, arg->prompt);
+
+ return 0;
+}
+
+static void
+modem_DescriptorRead(struct descriptor *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 from modem */
+ 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;
+ }
+
+ log_DumpBuff(LogASYNC, "ReadFromModem", rbuff, n);
+ 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);
+ async_Input(bundle, rbuff, p->input.sz, p);
+ 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)
+ async_Input(bundle, rbuff, n, p);
+}
+
+static int
+modem_UpdateSet(struct descriptor *d, fd_set *r, fd_set *w, fd_set *e, int *n)
+{
+ return physical_UpdateSet(d, r, w, e, n, 0);
+}
+
+struct physical *
+iov2modem(struct datalink *dl, struct iovec *iov, int *niov, int maxiov, int fd)
+{
+ struct physical *p;
+ int len;
+
+ p = (struct physical *)iov[(*niov)++].iov_base;
+ p->link.name = dl->name;
+ throughput_init(&p->link.throughput);
+ memset(&p->Timer, '\0', sizeof p->Timer);
+ memset(p->link.Queue, '\0', sizeof p->link.Queue);
+
+ p->desc.UpdateSet = modem_UpdateSet;
+ p->desc.IsSet = physical_IsSet;
+ p->desc.Read = modem_DescriptorRead;
+ p->desc.Write = modem_DescriptorWrite;
+ p->type = PHYS_DIRECT;
+ p->dl = dl;
+ len = strlen(_PATH_DEV);
+ p->name.base = strncmp(p->name.full, _PATH_DEV, len) ?
+ p->name.full : p->name.full + len;
+ p->out = NULL;
+ p->connect_count = 1;
+
+ 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;
+
+ if (p->hdlc.lqm.method && p->hdlc.lqm.timer.load)
+ lqr_reStart(&p->link.lcp);
+ hdlc_StartTimer(&p->hdlc);
+
+ throughput_start(&p->link.throughput, "modem throughput",
+ Enabled(dl->bundle, OPT_THROUGHPUT));
+ p->input.sz = 0;
+ if (p->Timer.state != TIMER_STOPPED) {
+ p->Timer.state = TIMER_STOPPED; /* Special - see modem2iov() */
+ modem_StartTimer(dl->bundle, p); /* XXX: Should we set cd.required ? */
+ }
+
+ return p;
+}
+
+int
+modem2iov(struct physical *p, struct iovec *iov, int *niov, int maxiov,
+ pid_t newpid)
+{
+ 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->Timer.state != TIMER_STOPPED) {
+ timer_Stop(&p->Timer);
+ p->Timer.state = TIMER_RUNNING; /* Special - see iov2modem() */
+ }
+ if (tcgetpgrp(p->fd) == getpgrp())
+ p->session_owner = getpid(); /* So I'll eventually get HUP'd */
+ timer_Stop(&p->link.throughput.Timer);
+ modem_ChangedPid(p, newpid);
+ }
+
+ if (*niov >= maxiov) {
+ log_Printf(LogERROR, "modem2iov: No room for physical !\n");
+ if (p)
+ free(p);
+ return -1;
+ }
+
+ iov[*niov].iov_base = p ? p : malloc(sizeof *p);
+ iov[*niov].iov_len = sizeof *p;
+ (*niov)++;
+
+ return p ? p->fd : 0;
+}
+
+void
+modem_ChangedPid(struct physical *p, pid_t newpid)
+{
+ if (p->fd >= 0 && p->type != PHYS_DIRECT) {
+ 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));
+ }
+}
diff --git a/usr.sbin/ppp/modem.h b/usr.sbin/ppp/modem.h
new file mode 100644
index 0000000..58f54dd
--- /dev/null
+++ b/usr.sbin/ppp/modem.h
@@ -0,0 +1,43 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: modem.h,v 1.17 1998/05/21 21:47:02 brian Exp $
+ *
+ * TODO:
+ */
+
+struct iovec;
+struct datalink;
+struct physical;
+struct bundle;
+struct ccp;
+struct cmdargs;
+
+extern int modem_Raw(struct physical *, struct bundle *);
+extern struct physical *modem_Create(struct datalink *, int);
+extern int modem_Open(struct physical *, struct bundle *);
+extern int modem_Speed(struct physical *);
+extern speed_t IntToSpeed(int);
+extern int modem_SetParity(struct physical *, const char *);
+extern int modem_ShowStatus(struct cmdargs const *);
+extern void modem_Close(struct physical *);
+extern void modem_Offline(struct physical *);
+extern void modem_Destroy(struct physical *);
+extern struct physical *iov2modem(struct datalink *, struct iovec *, int *,
+ int, int);
+extern int modem2iov(struct physical *, struct iovec *, int *, int, pid_t);
+extern void modem_ChangedPid(struct physical *, pid_t);
diff --git a/usr.sbin/ppp/mp.c b/usr.sbin/ppp/mp.c
new file mode 100644
index 0000000..c2e5515
--- /dev/null
+++ b/usr.sbin/ppp/mp.c
@@ -0,0 +1,1038 @@
+/*-
+ * 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.
+ *
+ * $Id: mp.c,v 1.17 1998/10/24 01:08:45 brian Exp $
+ */
+
+#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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <termios.h>
+#include <unistd.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 "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 "lcpproto.h"
+#include "filter.h"
+#include "mp.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "ip.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 */
+}
+
+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 */
+}
+
+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->seq.min_in = 0;
+ mp->seq.next_in = 0;
+ mp->inbufs = NULL;
+ mp->bundle = bundle;
+
+ mp->link.type = MP_LINK;
+ mp->link.name = "mp";
+ mp->link.len = sizeof *mp;
+
+ throughput_init(&mp->link.throughput);
+ 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.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);
+}
+
+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);
+ 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_init(&mp->link.throughput);
+ 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->out.seq = 0;
+ mp->out.link = 0;
+ 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 IPCP layer at our MP link */
+ ipcp_SetLink(&mp->bundle->ncp.ipcp, &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;
+
+ /* 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->pnext;
+ mbuf_Free(mp->inbufs);
+ mp->inbufs = next;
+ }
+
+ peerid_Init(&mp->peer);
+ mp->active = 0;
+ }
+}
+
+void
+mp_linkInit(struct mp_link *mplink)
+{
+ mplink->seq = 0;
+ mplink->weight = 1500;
+}
+
+void
+mp_Input(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) {
+ mbuf_Free(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.
+ */
+
+ if (!mp->inbufs) {
+ mp->inbufs = m;
+ m = NULL;
+ }
+
+ last = NULL;
+ seq = mp->seq.next_in;
+ q = mp->inbufs;
+ while (q) {
+ 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->pnext = m;
+ else
+ mp->inbufs = m;
+ m->pnext = q;
+ q = m;
+ h = mh;
+ m = NULL;
+ }
+
+ if (h.seq != seq) {
+ /* we're missing something :-( */
+ if (mp->seq.min_in > seq) {
+ /* 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->pnext;
+ mbuf_Free(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->pnext;
+ log_Printf(LogDEBUG, "Drop frag %u\n", h.seq);
+ mbuf_Free(mp->inbufs);
+ mp->inbufs = next;
+ } while (mp->inbufs && (h.seq >= mp->seq.min_in || 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->pnext;
+ len = mp_ReadHeader(mp, *frag, &h);
+ if (first == -1)
+ first = h.seq;
+ (*frag)->offset += len;
+ (*frag)->cnt -= len;
+ (*frag)->pnext = NULL;
+ if (frag == &q && !h.begin) {
+ log_Printf(LogWARN, "Oops - MP frag %lu should have a begin flag\n",
+ (u_long)h.seq);
+ mbuf_Free(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-assembed packet.
+ */
+ (*frag)->pnext = mp->inbufs;
+ mp->inbufs = *frag;
+ *frag = NULL;
+ mbuf_Free(q);
+ q = NULL;
+ frag = &q;
+ h.end = 0; /* just in case it's a whole packet */
+ } else
+ do
+ frag = &(*frag)->next;
+ while (*frag != NULL);
+ } while (!h.end);
+
+ if (q) {
+ u_short proto;
+ u_char ch;
+
+ q = mbuf_Read(q, &ch, 1);
+ proto = ch;
+ if (!(proto & 1)) {
+ q = mbuf_Read(q, &ch, 1);
+ proto <<= 8;
+ proto += ch;
+ }
+ if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "MP: Reassembled frags %ld-%lu, length %d\n",
+ first, (u_long)h.seq, mbuf_Length(q));
+ hdlc_DecodePacket(mp->bundle, proto, q, &mp->link);
+ }
+
+ 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->pnext;
+ }
+ }
+
+ if (m) {
+ /* We still have to find a home for our new fragment */
+ last = NULL;
+ for (q = mp->inbufs; q; last = q, q = q->pnext) {
+ mp_ReadHeader(mp, q, &h);
+ if (isbefore(mp->local_is12bit, mh.seq, h.seq))
+ break;
+ }
+ /* Our received fragment fits in here */
+ if (last)
+ last->pnext = m;
+ else
+ mp->inbufs = m;
+ m->pnext = q;
+ }
+}
+
+static void
+mp_Output(struct mp *mp, struct link *l, struct mbuf *m, u_int32_t begin,
+ u_int32_t end)
+{
+ struct mbuf *mo;
+
+ /* Stuff an MP header on the front of our packet and send it */
+ mo = mbuf_Alloc(4, MB_MP);
+ mo->next = m;
+ if (mp->peer_is12bit) {
+ u_int16_t val;
+
+ val = (begin << 15) | (end << 14) | (u_int16_t)mp->out.seq;
+ ua_htons(&val, MBUF_CTOP(mo));
+ mo->cnt = 2;
+ } else {
+ u_int32_t val;
+
+ val = (begin << 31) | (end << 30) | (u_int32_t)mp->out.seq;
+ ua_htonl(&val, MBUF_CTOP(mo));
+ mo->cnt = 4;
+ }
+ if (log_IsKept(LogDEBUG))
+ log_Printf(LogDEBUG, "MP[frag %d]: Send %d bytes on link `%s'\n",
+ mp->out.seq, mbuf_Length(mo), l->name);
+ mp->out.seq = inc_seq(mp->peer_is12bit, mp->out.seq);
+
+ if (!ccp_Compress(&l->ccp, l, PRI_NORMAL, PROTO_MP, mo))
+ hdlc_Output(l, PRI_NORMAL, PROTO_MP, mo);
+}
+
+int
+mp_FillQueues(struct bundle *bundle)
+{
+ struct mp *mp = &bundle->ncp.mp;
+ struct datalink *dl, *fdl;
+ int total, add, len, thislink, nlinks;
+ u_int32_t begin, end;
+ struct mbuf *m, *mo;
+
+ thislink = nlinks = 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 (!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);
+ total += add;
+ if (add)
+ /* this link has got stuff already queued. Let it continue */
+ continue;
+
+ if (!link_QueueLen(&mp->link) && !ip_FlushPacket(&mp->link, bundle))
+ /* Nothing else to send */
+ break;
+
+ m = link_Dequeue(&mp->link);
+ len = mbuf_Length(m);
+ begin = 1;
+ end = 0;
+
+ while (!end) {
+ if (dl->state == DATALINK_OPEN) {
+ if (len <= dl->mp.weight + LINK_MINWEIGHT) {
+ /*
+ * XXX: Should we remember how much of our `weight' wasn't sent
+ * so that we can compensate next time ?
+ */
+ mo = m;
+ end = 1;
+ } else {
+ mo = mbuf_Alloc(dl->mp.weight, MB_MP);
+ mo->cnt = dl->mp.weight;
+ len -= mo->cnt;
+ m = mbuf_Read(m, MBUF_CTOP(mo), mo->cnt);
+ }
+ mp_Output(mp, &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_SetDatalinkWeight(struct cmdargs const *arg)
+{
+ int val;
+
+ if (arg->argc != arg->argn+1)
+ return -1;
+
+ val = atoi(arg->argv[arg->argn]);
+ if (val < LINK_MINWEIGHT) {
+ log_Printf(LogWARN, "Link weights must not be less than %d\n",
+ LINK_MINWEIGHT);
+ return 1;
+ }
+ arg->cx->mp.weight = val;
+ 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;
+ int bufs = 0;
+
+ prompt_Printf(arg->prompt, "Socket: %s\n",
+ mp->server.socket.sun_path);
+ for (m = mp->inbufs; m; m = m->pnext)
+ bufs++;
+ prompt_Printf(arg->prompt, "Pending frags: %d\n", bufs);
+ }
+
+ prompt_Printf(arg->prompt, "\nMy Side:\n");
+ if (mp->active) {
+ 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, " Next SEQ: %u\n", mp->out.seq);
+ 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));
+
+ 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)
+ addr = arg->bundle->ncp.ipcp.cfg.my_range.ipaddr;
+ 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)
+ addr = arg->bundle->ncp.ipcp.cfg.my_range.ipaddr;
+ else
+ addr = arg->bundle->ncp.ipcp.my_ip;
+
+ s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "set enddisc: socket(): %s\n", strerror(errno));
+ return 2;
+ }
+ if (get_ether_addr(s, addr, &hwaddr)) {
+ 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 descriptor *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 descriptor *d, const fd_set *fdset)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ return s->fd >= 0 && FD_ISSET(s->fd, fdset);
+}
+
+static void
+mpserver_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct mpserver *s = descriptor2mpserver(d);
+ struct sockaddr in;
+ int fd, size;
+
+ size = sizeof in;
+ fd = accept(s->fd, &in, &size);
+ if (fd < 0) {
+ log_Printf(LogERROR, "mpserver_Read: accept(): %s\n", strerror(errno));
+ return;
+ }
+
+ if (in.sa_family == AF_LOCAL)
+ bundle_ReceiveDatalink(bundle, fd, (struct sockaddr_un *)&in);
+ else
+ close(fd);
+}
+
+static int
+mpserver_Write(struct descriptor *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);
+
+ 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_STREAM, 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);
+ 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;
+ }
+ 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, " Has 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;
+ }
+
+ /* Listen for other ppp invocations that want to donate links */
+ if (listen(s->fd, 5) != 0) {
+ log_Printf(LogERROR, "mpserver: Unable to listen to socket"
+ " - BUNDLE overload?\n");
+ mpserver_Close(s);
+ }
+
+ 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_Input(mp, NULL, NULL);
+}
+
+void
+mp_DeleteQueue(struct mp *mp)
+{
+ link_DeleteQueue(&mp->link);
+}
diff --git a/usr.sbin/ppp/mp.h b/usr.sbin/ppp/mp.h
new file mode 100644
index 0000000..c421b4b
--- /dev/null
+++ b/usr.sbin/ppp/mp.h
@@ -0,0 +1,136 @@
+/*-
+ * 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.
+ *
+ * $Id: mp.h,v 1.3 1998/05/23 17:05:28 brian Exp $
+ */
+
+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[50]; /* Peers name (authenticated) */
+};
+
+struct mpserver {
+ struct descriptor 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 */
+ } 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 */
+ struct enddisc enddisc; /* endpoint discriminator */
+ } 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 */
+ int weight; /* bytes to send with each write */
+};
+
+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 void mp_Input(struct mp *, struct mbuf *, struct physical *);
+extern int mp_FillQueues(struct bundle *);
+extern int mp_SetDatalinkWeight(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_DeleteQueue(struct mp *);
diff --git a/usr.sbin/ppp/nat_cmd.c b/usr.sbin/ppp/nat_cmd.c
new file mode 100644
index 0000000..430ab47
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.c
@@ -0,0 +1,306 @@
+/*-
+ * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
+ * who places it in the public domain without restriction.
+ *
+ * $Id: alias_cmd.c,v 1.21 1999/03/07 18:13:44 brian Exp $
+ */
+
+#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/un.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#ifdef __OpenBSD__
+#include "alias.h"
+#else
+#include <alias.h>
+#endif
+#include "defs.h"
+#include "command.h"
+#include "log.h"
+#include "alias_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 "ipcp.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#include "filter.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+
+
+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 *);
+
+
+int
+alias_RedirectPort(struct cmdargs const *arg)
+{
+ if (!arg->bundle->AliasEnabled) {
+ prompt_Printf(arg->prompt, "Alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn + 3) {
+ char proto_constant;
+ const char *proto;
+ u_short hlocalport;
+ u_short llocalport;
+ u_short haliasport;
+ u_short laliasport;
+ u_short port;
+ int error;
+ struct in_addr local_addr;
+ struct in_addr null_addr;
+ struct alias_link *link;
+
+ 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], &local_addr, &llocalport,
+ &hlocalport, proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "alias port: error reading localaddr:port\n");
+ return -1;
+ }
+ error = StrToPortRange(arg->argv[arg->argn+2], &laliasport, &haliasport,
+ proto);
+ if (error) {
+ prompt_Printf(arg->prompt, "alias port: error reading alias port\n");
+ return -1;
+ }
+ null_addr.s_addr = INADDR_ANY;
+
+ if (llocalport > hlocalport) {
+ port = llocalport;
+ llocalport = hlocalport;
+ hlocalport = port;
+ }
+
+ if (laliasport > haliasport) {
+ port = laliasport;
+ laliasport = haliasport;
+ haliasport = port;
+ }
+
+ if (haliasport - laliasport != hlocalport - llocalport) {
+ prompt_Printf(arg->prompt, "alias port: Port ranges must be equal\n");
+ return -1;
+ }
+
+ for (port = laliasport; port <= haliasport; port++) {
+ link = PacketAliasRedirectPort(local_addr,
+ htons(llocalport + (port - laliasport)),
+ null_addr, 0, null_addr, htons(port),
+ proto_constant);
+
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "alias port: %d: error %d\n", port, error);
+ return 1;
+ }
+ }
+ } else
+ return -1;
+
+ return 0;
+}
+
+
+int
+alias_RedirectAddr(struct cmdargs const *arg)
+{
+ if (!arg->bundle->AliasEnabled) {
+ prompt_Printf(arg->prompt, "alias not enabled\n");
+ return 1;
+ } else if (arg->argc == arg->argn+2) {
+ int error;
+ struct in_addr local_addr;
+ struct in_addr alias_addr;
+ struct alias_link *link;
+
+ error = StrToAddr(arg->argv[arg->argn], &local_addr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(arg->argv[arg->argn+1], &alias_addr);
+ if (error) {
+ prompt_Printf(arg->prompt, "address redirect: invalid alias address\n");
+ prompt_Printf(arg->prompt, "Usage: alias %s %s\n", arg->cmd->name,
+ arg->cmd->syntax);
+ return 1;
+ }
+ link = PacketAliasRedirectAddr(local_addr, alias_addr);
+ if (link == NULL) {
+ prompt_Printf(arg->prompt, "address redirect: packet aliasing"
+ " engine error\n");
+ prompt_Printf(arg->prompt, "Usage: alias %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
+alias_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 + (f ? 1 : 0))
+ break;
+ if (f)
+ cmd[pos++] = ' ';
+ strcpy(cmd + pos, arg->argv[f]);
+ pos += len;
+ }
+
+ return PacketAliasProxyRule(cmd);
+}
+
+int
+alias_Pptp(struct cmdargs const *arg)
+{
+ struct in_addr addr;
+
+ if (arg->argc == arg->argn) {
+ addr.s_addr = INADDR_NONE;
+ PacketAliasPptp(addr);
+ return 0;
+ }
+
+ if (arg->argc != arg->argn + 1)
+ return -1;
+
+ 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;
+ }
+
+ PacketAliasPptp(addr);
+ return 0;
+}
diff --git a/usr.sbin/ppp/nat_cmd.h b/usr.sbin/ppp/nat_cmd.h
new file mode 100644
index 0000000..cf96bcf
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.h
@@ -0,0 +1,13 @@
+/*-
+ * The code in this file was written by Eivind Eklund <perhaps@yes.no>,
+ * who places it in the public domain without restriction.
+ *
+ * $Id: alias_cmd.h,v 1.9 1999/03/07 15:02:37 brian Exp $
+ */
+
+struct cmdargs;
+
+extern int alias_RedirectPort(struct cmdargs const *);
+extern int alias_RedirectAddr(struct cmdargs const *);
+extern int alias_ProxyRule(struct cmdargs const *);
+extern int alias_Pptp(struct cmdargs const *);
diff --git a/usr.sbin/ppp/pap.c b/usr.sbin/ppp/pap.c
new file mode 100644
index 0000000..5b3e17a
--- /dev/null
+++ b/usr.sbin/ppp/pap.c
@@ -0,0 +1,249 @@
+/*
+ * PPP PAP Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993-94, Internet Initiative Japan, Inc.
+ * All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pap.c,v 1.33 1999/03/31 14:21:45 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "auth.h"
+#include "pap.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcpproto.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 "ipcp.h"
+#include "filter.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "chat.h"
+#include "chap.h"
+#include "cbcp.h"
+#include "datalink.h"
+
+static const char *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 = mbuf_Alloc(plen + sizeof(struct fsmheader), MB_FSM);
+ 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);
+
+ hdlc_Output(&authp->physical->link, PRI_LINK, PROTO_PAP, bp);
+}
+
+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 = mbuf_Alloc(plen + sizeof(struct fsmheader), MB_FSM);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ *cp++ = mlen;
+ memcpy(cp, message, mlen);
+ log_Printf(LogPHASE, "Pap Output: %s\n", papcodes[code]);
+
+ hdlc_Output(&authp->physical->link, PRI_LINK, PROTO_PAP, bp);
+}
+
+static void
+pap_Success(struct authinfo *authp)
+{
+ datalink_GotAuthname(authp->physical->dl, authp->in.name);
+ SendPapCode(authp, PAP_ACK, "Greetings!!");
+ authp->physical->link.lcp.auth_ineed = 0;
+ if (Enabled(authp->physical->dl->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);
+}
+
+void
+pap_Input(struct physical *p, struct mbuf *bp)
+{
+ struct authinfo *authp = &p->dl->pap;
+ u_char nlen, klen, *key;
+
+ if (bundle_Phase(p->dl->bundle) != PHASE_NETWORK &&
+ bundle_Phase(p->dl->bundle) != PHASE_AUTHENTICATE) {
+ log_Printf(LogPHASE, "Unexpected pap input - dropped !\n");
+ mbuf_Free(bp);
+ return;
+ }
+
+ if ((bp = auth_ReadHeader(authp, bp)) == NULL &&
+ ntohs(authp->in.hdr.length) == 0) {
+ log_Printf(LogWARN, "Pap Input: Truncated header !\n");
+ return;
+ }
+
+ 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);
+ mbuf_Free(bp);
+ return;
+ }
+
+ if (authp->in.hdr.code != PAP_REQUEST && authp->id != authp->in.hdr.id &&
+ Enabled(p->dl->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);
+ mbuf_Free(bp);
+ return;
+ }
+ authp->id = authp->in.hdr.id; /* We respond with this id */
+
+ if (bp) {
+ bp = mbuf_Read(bp, &nlen, 1);
+ bp = auth_ReadName(authp, bp, nlen);
+ }
+
+ log_Printf(LogPHASE, "Pap Input: %s (%s)\n",
+ papcodes[authp->in.hdr.code], authp->in.name);
+
+ 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 (mbuf_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 (*p->dl->bundle->radius.cfg.file)
+ radius_Authenticate(&p->dl->bundle->radius, authp, authp->in.name,
+ key, NULL);
+ else
+#endif
+ if (auth_Validate(p->dl->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;
+ }
+
+ mbuf_Free(bp);
+}
diff --git a/usr.sbin/ppp/pap.h b/usr.sbin/ppp/pap.h
new file mode 100644
index 0000000..bef8292
--- /dev/null
+++ b/usr.sbin/ppp/pap.h
@@ -0,0 +1,32 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pap.h,v 1.8 1999/02/02 09:35:17 brian Exp $
+ *
+ * TODO:
+ */
+
+#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 void pap_Input(struct physical *, struct mbuf *);
diff --git a/usr.sbin/ppp/physical.c b/usr.sbin/ppp/physical.c
new file mode 100644
index 0000000..875c459
--- /dev/null
+++ b/usr.sbin/ppp/physical.c
@@ -0,0 +1,234 @@
+/*
+ * 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.
+ *
+ * $Id: physical.c,v 1.6 1998/08/25 17:48:43 brian Exp $
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <sys/tty.h>
+
+#include "defs.h"
+#include "mbuf.h"
+#include "timer.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "throughput.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "async.h"
+#include "ccp.h"
+#include "link.h"
+#include "descriptor.h"
+#include "physical.h"
+#include "log.h"
+#include "id.h"
+
+/* External calls - should possibly be moved inline */
+extern int IntToSpeed(int);
+
+
+int
+physical_GetFD(struct physical *phys) {
+ return phys->fd;
+}
+
+int
+physical_IsSync(struct physical *phys) {
+ return phys->cfg.speed == 0;
+}
+
+const char *physical_GetDevice(struct physical *phys)
+{
+ return phys->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++] = ' ';
+ strncpy(p->cfg.devlist + pos, argv[f], sizeof p->cfg.devlist - pos - 1);
+ pos += strlen(p->cfg.devlist + pos);
+ }
+}
+
+
+int
+physical_SetSpeed(struct physical *phys, int speed) {
+ if (IntToSpeed(speed) != B0) {
+ phys->cfg.speed = speed;
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void
+physical_SetSync(struct physical *phys) {
+ phys->cfg.speed = 0;
+}
+
+
+int
+physical_SetRtsCts(struct physical *phys, int enable) {
+ phys->cfg.rts_cts = enable ? 1 : 0;
+ return 1;
+}
+
+/* Encapsulation for a read on the FD. Avoids some exposure, and
+ concentrates control. */
+ssize_t
+physical_Read(struct physical *phys, void *buf, size_t nbytes) {
+ return read(phys->fd, buf, nbytes);
+}
+
+ssize_t
+physical_Write(struct physical *phys, const void *buf, size_t nbytes) {
+ return write(phys->fd, buf, nbytes);
+}
+
+int
+physical_UpdateSet(struct descriptor *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)
+{
+ 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 descriptor *d, const fd_set *fdset)
+{
+ struct physical *p = descriptor2physical(d);
+ return p->fd >= 0 && FD_ISSET(p->fd, fdset);
+}
+
+void
+physical_Login(struct physical *phys, const char *name)
+{
+ if (phys->type == PHYS_DIRECT && phys->isatty) {
+ if (phys->Utmp)
+ log_Printf(LogERROR, "Oops, already logged in on %s\n", phys->name.base);
+ else {
+ struct utmp ut;
+ const char *connstr;
+
+ memset(&ut, 0, sizeof ut);
+ time(&ut.ut_time);
+ strncpy(ut.ut_name, name, sizeof ut.ut_name);
+ strncpy(ut.ut_line, phys->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);
+ phys->Utmp = 1;
+ }
+ }
+}
+
+void
+physical_Logout(struct physical *phys)
+{
+ if (phys->Utmp) {
+ ID0logout(phys->name.base);
+ phys->Utmp = 0;
+ }
+}
+
+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))) {
+ 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) {
+ mbuf_Free(p->out);
+ p->out = NULL;
+ }
+ link_DeleteQueue(&p->link);
+}
diff --git a/usr.sbin/ppp/physical.h b/usr.sbin/ppp/physical.h
new file mode 100644
index 0000000..b233c75
--- /dev/null
+++ b/usr.sbin/ppp/physical.h
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ *
+ * $Id: physical.h,v 1.6 1999/02/16 00:16:56 brian Exp $
+ *
+ */
+
+struct bundle;
+
+struct physical {
+ struct link link;
+ struct descriptor 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 */
+ int mbits; /* Current DCD status */
+ unsigned isatty : 1;
+ 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[40];
+ char *base;
+ } name;
+
+ unsigned Utmp : 1; /* Are we in utmp ? */
+ pid_t session_owner; /* HUP this when closing the link */
+
+ /* XXX-ML Most of the below is device specific, and probably do not
+ belong in the generic physical struct. It comes from modem.c. */
+
+ struct {
+ unsigned rts_cts : 1; /* Is rts/cts enabled? */
+ unsigned parity; /* What parity is enabled? (TTY flags) */
+ unsigned speed; /* Modem speed */
+ char devlist[LINE_LEN]; /* Comma-separated list of devices */
+ struct {
+ unsigned required : 1; /* Is cd *REQUIRED* on this device */
+ int delay; /* Wait this many seconds after login script */
+ } cd;
+ } cfg;
+
+ struct termios ios; /* To be able to reset from raw mode */
+
+ struct pppTimer Timer; /* CD checks */
+};
+
+#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)
+
+extern int physical_GetFD(struct physical *);
+extern int physical_IsSync(struct physical *);
+extern const char *physical_GetDevice(struct physical *);
+extern void physical_SetDeviceList(struct physical *, int, const char *const *);
+extern int physical_SetSpeed(struct physical *, int);
+
+/*
+ * XXX-ML I'm not certain this is the right way to handle this, but we
+ * can solve that later.
+ */
+extern void physical_SetSync(struct physical *);
+
+/*
+ * Can this be set? (Might not be a relevant attribute for this
+ * device, for instance)
+ */
+extern int physical_SetRtsCts(struct physical *, int);
+
+extern ssize_t physical_Read(struct physical *, void *, size_t);
+extern ssize_t physical_Write(struct physical *, const void *, size_t);
+extern int physical_UpdateSet(struct descriptor *, fd_set *, fd_set *,
+ fd_set *, int *, int);
+extern int physical_IsSet(struct descriptor *, const fd_set *);
+extern void physical_Login(struct physical *, const char *);
+extern void physical_Logout(struct physical *);
+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 *);
diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8
new file mode 100644
index 0000000..ad28a00
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8
@@ -0,0 +1,4499 @@
+.\" $Id: ppp.8,v 1.163 1999/03/19 09:00:08 brian Exp $
+.Dd 20 September 1995
+.nr XX \w'\fC00'
+.Os FreeBSD
+.Dt PPP 8
+.Sh NAME
+.Nm ppp
+.Nd Point to Point Protocol (a.k.a. user-ppp)
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl auto |
+.Fl background |
+.Fl ddial |
+.Fl direct |
+.Fl dedicated
+.Oc
+.Op Fl alias
+.Op Ar system Ns
+.No ...
+.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).
+.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 your modem directly. When your
+modem is 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 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 aliased for outgoing packets and de-aliased 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) 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) 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. If
+.Pa libradius
+is available at compile time,
+.Nm
+will use it to make
+.Em RADIUS
+requests when configured to do so.
+.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 capability.
+If a device name is specified as
+.Em host Ns No : Ns Em port ,
+.Nm
+will open a TCP connection for transporting data rather than using a
+conventional serial device.
+.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.
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (ie. 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.
+.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 ppp .
+Refer to the
+.Sq ID0
+logging facility if you're interested in what exactly is done as user id
+zero.
+.Sh GETTING STARTED
+The following command line switches are understood by
+.Nm ppp :
+.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
+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 direct
+This is used for receiving incoming connections.
+.Nm
+ignores the
+.Dq set device
+line and uses descriptor 0 as the link.
+.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 never use any configured
+chat scripts.
+.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
+flags have been specified.
+.Nm
+loads any systems specified on the command line then provides an
+interactive prompt.
+.It Fl alias
+This flag doesn't control
+.Nm ppp Ns No 's
+mode. It does the equivalent of an
+.Dq enable alias yes .
+Additionally, if the
+.Fl auto
+flag is also specified, an implicit
+.Dq enable iface-alias
+is done.
+See below for details.
+.Pp
+Enabling IP aliasing allows
+.Nm ppp
+to act as a NAT or masquerading engine for all machines on an internal
+LAN. Refer to
+.Xr libalias 3
+for details.
+.El
+.Pp
+Additionally, one or more systems may be specified on the command line.
+A
+.Sq system
+is a configuration entry in
+.Pa /etc/ppp/ppp.conf .
+.Nm
+will read the
+.Dq default
+system from
+.Pa /etc/ppp/ppp.conf
+at startup, followed by each of the systems specifed on the command line.
+.Pp
+Only one of the
+.Fl auto ,
+.Fl background ,
+.Fl ddial ,
+.Fl direct ,
+.Fl dedicated
+and
+.Fl interactive
+switches may be specified.
+.Nm ppp Ns No 's
+.Sq mode
+may subsequently be changed with the
+.Dq set mode
+command (see below).
+.Pp
+For now, we'll stick to using interactive mode.
+.Pp
+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 that group contains the names of all users expected to use
+.Nm ppp .
+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 ppp Ns No s
+operation, you should configure your resolver so that it works correctly.
+This can be done by configuring a local DNS
+.Pq using Xr named 8
+or by adding the correct
+.Sq name-server
+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
+command 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 ppp 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
+.Pq 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 modem settings look like:
+.Bd -literal -offset indent
+ppp ON awfulhak> show modem
+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 your modem:
+.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 modem
+* 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 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 ppp .
+.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 ppp .
+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
+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 substition 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 ppp ,
+it was necessary to re-add routes such as the default route in the
+.Pa ppp.linkup
+file.
+.Nm
+now supports
+.Sq sticky routes ,
+where all routes that contain the
+.Dv HISADDR
+or
+.Dv MYADDR
+literals will automatically be updated when the values of
+.Dv HISADDR
+and/or
+.Dv MYADDR
+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 ,
+.Pq for example, Dq 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 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 maxinc
+is the maximun number of times
+.Nm
+should increment
+.Ar secs .
+The default value for
+.Ar maxinc
+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 "/usr/libexec/getty std.38400" 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
+.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 ppp .
+.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
+.Pp
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+This method differs in that we use
+.Nm ppp
+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
+.Pq 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 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
+.Pq 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 ppp Ns No s
+subsequent CBCP phase.
+.Sh PPP OVER TCP (a.k.a Tunnelling)
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying a host and port as the
+device:
+.Pp
+.Dl set device ui-gate:6669
+.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
+ add 10.0.1.0/24 10.0.4.2
+.Ed
+.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
+.Pq 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
+ set dial
+ set timeout 30
+ set log Phase Chat Connect hdlc LCP IPCP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+ add 10.0.2.0/24 10.0.4.1
+.Ed
+.Pp
+Again, if you're enabling PAP, you'll also need:
+.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.
+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.
+.Sh PACKET ALIASING
+The
+.Fl alias
+command line option enables 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 aliased so that
+they appear to come from the
+.Nm
+host, and incoming packets are de-aliased so that they are routed
+to the correct machine on the local area network.
+Packet aliasing 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 packet aliasing disabled.
+Then, the
+.Fl alias
+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
+.Oo
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc
+.Oo Ar proto Op src Ar cmp port
+.Op dst Ar cmp port
+.Op estab
+.Op syn
+.Op finrst
+.Oc
+.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 19
+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
+is either
+.Sq permit
+or
+.Sq deny .
+If a given packet
+matches the rule, the associated action is taken immediately.
+.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.
+.It
+.Ar Proto
+must be one of
+.Sq icmp ,
+.Sq udp
+or
+.Sq tcp .
+.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.
+.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,
+ie. the default is to allow everything through.
+.It
+If no rule is matched to a packet, that packet will be discarded
+(blocked).
+.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
+.Pq 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
+.Pq or requests them .
+The deflate protocol is preferred by
+.Nm ppp .
+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
+.Pq 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
+.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 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 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 -literal -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
+try to insist that 192.244.177.2 be used first.
+.El
+.Pp
+.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 -literal -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 ******** Ns
+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 FreeBSD. If you're running
+.Nm
+on OpenBSD, 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 .
+.Pp
+This tells
+.Nm
+to add a default route to whatever the peer address is
+.Pq 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.
+.Pp
+Previous versions of
+.Nm
+required a similar entry in the
+.Pa /etc/ppp/ppp.linkup
+file. Since the advent of
+.Sq sticky routes ,
+this is no longer required.
+.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
+Do
+.Em NOT
+do this if you are running an local DNS, 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 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
+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 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 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
+(ie. 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 XX
+.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 USR2
+This signal, tells
+.Nm
+to close any existing server socket, dropping all existing diagnostic
+connections.
+.El
+.Pp
+.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
+.Pq 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
+.Pq 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. It's 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
+ set authname ppp
+ set authkey ppppassword
+
+ set mrru 1500
+ clone 1,2,3
+ link deflink remove
+.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 ppp 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 XX
+.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 XX
+.It acfcomp
+Default: Enabled and Accepted. ACFComp stands for Address and Control
+Field Compression. Non LCP packets usually have very similar address
+and control fields - making them easily compressible.
+.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 Ns No !
+.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 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,
+ppp 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 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
+.Pq 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 XX
+.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 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. 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
+or
+.Dv MYADDR
+values, entries are stored in the
+.Sq stick route
+list. Each time
+.Dv HISADDR
+or
+.Dv MYADDR
+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 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 modem
+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.
+.It iface-alias
+Default: Enabled if
+.Fl alias
+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 IP aliasing is enabled
+.Pq Dq alias enable yes .
+.Pp
+With this option enabled,
+.Nm
+will pass traffic for old interface addresses through the IP alias engine
+.Pq see Xr libalias 5 ,
+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 IP aliasing with
+.Dq alias enable off
+will also disable
+.Sq iface-alias .
+.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
+or
+.Sq HISADDR
+as the destination, and
+.Sq HISADDR
+as the
+.Ar gateway .
+.Sq MYADDR
+is replaced with the interface address and
+.Sq HISADDR
+is replaced with the interface destination (peer) address.
+.Pp
+If the
+.Ar add!
+command is used
+.Pq 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
+or
+.Dq MYADDR
+constants are considered
+.Sq sticky .
+They are stored in a list (use
+.Dq show ipcp
+to see the list), and each time the value of
+.Dv HISADDR
+or
+.Dv MYADDR
+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 XX
+.It allow user Ns Xo
+.Op s
+.Ar logname Ns No ...
+.Xc
+By default, only user id 0 is allowed access to
+.Nm ppp .
+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). Each successive
+.Dq allow users
+command overrides the previous one, 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 alias Ar command Op Ar args
+This command allows the control of the aliasing (or masquerading)
+facilities that are built into
+.Nm ppp .
+If aliasing is enabled on your system (it may be omitted at compile time),
+the following commands are possible:
+.Bl -tag -width XX
+.It alias enable Op yes|no
+This command either switches aliasing on or turns it off.
+The
+.Fl alias
+command line flag is synonymous with
+.Dq alias enable yes .
+.It alias 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 alias deny_incoming Op yes|no
+If set to yes, this command will refuse all incoming connections
+by dropping the packets in much the same way as a firewall would.
+.It alias help|?
+This command gives a summary of available alias commands.
+.It alias log Op yes|no
+This option causes various aliasing statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+.It alias port Ar proto Ar targetIP Ns Xo
+.No : Ns Ar port Ns
+.Oo
+.No - Ns Ar port
+.Oc Ar aliasport Ns
+.Oo
+.No - Ns Ar aliasport Ns
+.Oc
+.Xc
+This command causes incoming
+.Ar proto
+connections to port
+.Ar aliasport
+to be redirected to port
+.Ar port
+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
+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 port are possible.
+.It alias pptp Op Ar addr
+This tells
+.Nm
+to alias any
+.Em G Ns No eneral
+.Em R Ns No outing
+.Em E Ns No encapsulated
+.Pq Dv IPPROTO_GRE
+packets using
+.Ar addr
+rather than the local interface address. This allows the uses of the
+.Em P Ns No oint
+to
+.Em P Ns No oint
+.Em T Ns No unneling
+.Em P Ns No rotocol
+on a machine on the internal network.
+.Pp
+If
+.Ar addr
+is not specified,
+.Dv PPTP
+aliasing is disabled.
+.It "alias proxy cmd" Ar arg Ns No ...
+This command tells
+.Nm
+to proxy certain connections, redirecting them to a given server. Refer
+to the description of
+.Fn PacketAliasProxyRule
+in
+.Xr libalias 3
+for details of the available commands.
+.It alias same_ports Op yes|no
+When enabled, this command will tell the alias library 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 alias use_sockets Op yes|no
+When enabled, this option tells the alias library to create a
+socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+.It alias unregistered_only Op yes|no
+Only alter outgoing packets with an unregistered source ad-
+dress. 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.alias
+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 PEER_ENDDISC
+.It Li AUTHNAME
+This is replaced with the local
+.Ar authname
+value. See the
+.Dq set authname
+command below.
+.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 INTERFACE
+This is replaced with the name of the interface that's in use.
+.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 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 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.
+.El
+.Pp
+These substitutions are also done by the
+.Dq set proctitle
+command.
+.Pp
+If you wish to pause
+.Nm
+while the command executes, use the
+.Dq shell
+command instead.
+.It clear modem|ipcp Op current|overall|peak...
+Clear the specified throughput values at either the
+.Dq modem
+or
+.Dq ipcp
+level. If
+.Dq modem
+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
+.Pq 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 ppp .
+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
+.Pq note the trailing Dq \&! ,
+.Nm
+will not complain if the route does not already exist.
+.It dial|call Op Ar label
+When used with no argument, this command is the same as the
+.Dq open
+command. When one or more
+.Ar label
+is specified, a
+.Dq load
+will be done first.
+.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 modem 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 iface Ar command Op args
+This command is used to control the interface used by
+.Nm ppp .
+.Ar Command
+may be one of the following:
+.Bl -tag -width XX
+.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
+.Pq 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
+If this command is used while
+.Nm
+is in the OPENED state or while in
+.Fl auto
+mode, all addresses except for the IPCP 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
+.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 possbile
+.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(s)
+from the
+.Pa ppp.conf
+file. If
+.Ar label
+is not given, the
+.Ar default
+label is used.
+.It open Op lcp|ccp|ipcp
+This is the opposite of the
+.Dq close
+command. Using
+.Dq open
+with no arguments is the same as using
+.Dq dial
+with no arguments, where all closed links are brought up (some auto
+links may not come up based on the
+.Dq set autoload
+command) using the current configuration.
+.Pp
+If the
+.Dq lcp
+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 save
+This option is not (yet) implemented.
+.It set Ns Xo
+.No Op up
+.Ar var value
+.Xc
+This option allows the setting of any of the following variables:
+.Bl -tag -width XX
+.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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\P
+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 exclaimation 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
+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 max-duration max-load Op Ar min-duration min-load
+.Xc
+These settings apply only in multi-link mode and all default to zero.
+When more than one
+.Ar demand-dial
+.Pq 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 at least
+.Ar max-load
+packets have been in the send queue for
+.Ar max-duration
+seconds. Because both values default to zero,
+.Ar demand-dial
+links will simply come up one at a time by default.
+.Pp
+If two or more links are open, at least one of which is a
+.Ar demand-dial
+link, a
+.Ar demand-dial
+link will be closed when there is less than
+.Ar min-packets
+in the queue for more than
+.Ar min-duration .
+If
+.Ar min-duration
+is zero, this timer is disabled. Because both values default to zero,
+.Ar demand-dial
+links will stay active until the bundle idle timer expires.
+.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 Ns No ).
+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
+.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
+Microsofts callback control protocol is used. See
+.Dq set cbcp
+below.
+.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 seperated 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 if you wish callback to be optional.
+.El
+.Pp
+.It set cbcp Oo Xo
+.No *| Ns Ar number Ns No
+.Oo
+.No , Ns Ar number Ns
+.Oc
+.No ...
+.Op Ar delay Op Ar retry
+.Oc
+.Xc
+If no arguments are given, CBCP (Microsofts 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 Ar seconds Ns Op \&!
+Normally,
+.Nm
+checks for the existence of carrier 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 NULL-modem cables), logs the fact and stops checking
+for carrier. However, some modems take some time to assert the carrier
+signal, resulting in
+.Nm ppp Ns No s
+inability to detect when the link is dropped.
+.Ar Seconds
+specifies the number of seconds that
+.Nm
+should wait after the login script has finished before first checking for
+carrier.
+.Pp
+If
+.Ar seconds
+is followed immediately by an exclaimation mark
+.Pq Dq \&! ,
+.Nm
+will
+.Em require
+carrier. If carrier is not detected at the first check, the link will
+be considered disconnected.
+.Pp
+Carrier
+.Em require Ns No ment
+is ignored when the link is not a tty device.
+.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 20
+packets (or
+.Em 20 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 .
+All serial device names are expected to begin with
+.Pa /dev/ .
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must either begin with an exclamation mark
+.Pq Dq \&!
+or be of the format
+.Dq host:port .
+.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
+.Dq host:port
+pair is given,
+.Nm
+will attempt to connect to the given
+.Dq host
+on the given
+.Dq port .
+Refer to the section on
+.Em PPP OVER TCP
+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:
+.Bd -unfilled -offset indent
+.It Li \\\\\\\\\\\\\\\\c
+When used as the last character in a
+.Sq send
+string, this indicates that a newline should not be appended.
+.It Li \\\\\\\\\\\\\\\\d
+When the chat script encounters this sequence, it delays two seconds.
+.It Li \\\\\\\\\\\\\\\\p
+When the chat script encounters this sequence, it delays for one quarter of
+a second.
+.It Li \\\\\\\\\\\\\\\\n
+This is replaced with a newline character.
+.It Li \\\\\\\\\\\\\\\\r
+This is replaced with a carriage return character.
+.It Li \\\\\\\\\\\\\\\\s
+This is replaced with a space character.
+.It Li \\\\\\\\\\\\\\\\t
+This is replaced with a tab character.
+.It Li \\\\\\\\\\\\\\\\T
+This is replaced by the current phone number (see
+.Dq set phone
+below).
+.It Li \\\\\\\\\\\\\\\\P
+This is replaced by the current
+.Ar authkey
+value (see
+.Dq set authkey
+above).
+.It Li \\\\\\\\\\\\\\\\U
+This is replaced by the current
+.Ar authname
+value (see
+.Dq set authname
+above).
+.Ed
+.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 \&! .
+When the command is executed, standard input and standard output are
+directed to the modem 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,
+.Nm
+will send the information to the peer using the LCP endpoint discriminator
+option. The following discriminators may be set:
+.Bd -unfilled -offset 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.
+.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.
+.Ed
+.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
+.Oo Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc Oo tcp|udp|icmp Op src lt|eq|gt Ar port
+.Op dst lt|eq|gt Ar port
+.Op estab
+.Op syn
+.Op finrst
+.Oc
+.Xc
+.Nm
+supports four filter sets. The
+.Em alive
+filter specifies packets that keep the connection alive - reseting 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
+alias engine. By default all filter sets allow all packets to pass.
+Rules are processed in order according to
+.Ar rule-no .
+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 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 modem
+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
+.Ar Hisaddr
+may also be specified as a range of IP numbers in the format
+.Bd -literal -offset indent
+.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Xo
+.Oc Oo , Ns Ar \&IP Ns
+.Op \&- Ns Ar \&IP Ns
+.Oc No ...
+.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 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
+.Pq 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 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 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 netogiations 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 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 IP aliasing 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 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 Op Ar value
+The default MRU (Maximum Receive Unit) is 1500. If it is increased, the
+other side *may* increase its mtu. There is no point in decreasing the
+MRU to below the default as the
+.Em PPP
+protocol *must* be able to accept packets of at least 1500 octets. If
+no argument is given, 1500 is assumed.
+.It set mtu Op Ar value
+The default MTU is 1500. At negotiation time,
+.Nm
+will accept whatever MRU or MRRU that the peer wants (assuming it's
+not less than 296 bytes). If the MTU is set,
+.Nm
+will not accept MRU/MRRU values less than
+.Ar value .
+When negotiations are complete, the MTU is assigned to the interface, even
+if the peer requested a higher value MRU/MRRU. This can be useful for
+limiting your packet size (giving better bandwidth sharing at the expense
+of more header data).
+.Pp
+If no
+.Ar value
+is given, 1500, or whatever the peer asks for is used.
+.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 or CHAP 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 neither PAP or CHAP 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_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
+.El
+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 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 maxinc
+times.
+.Ar maxinc
+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|LocalName|none password Op Ar mask
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections.
+.Pp
+The word
+.Ar none
+instructs
+.Nm
+to close any existing socket.
+.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 must specify the octal umask
+that should be used with local domain sockets as a four character octal
+number beginning with
+.Sq 0 .
+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
+command 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 not be relied upon.
+.It set speed Ar value
+This sets the speed of the serial device.
+.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
+This command allows the setting of the idle timer. Refer to the
+section titled
+.Sx SETTING THE IDLE TIMER
+for further details.
+.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
+commanad 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 XX
+.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
+.Pq the same \&as Dq iface show .
+.It show ipcp
+Show the current IPCP statistics.
+.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 modem
+Show low level 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 ppp .
+.El
+.Pp
+.It term
+Go into terminal mode. Characters typed at the keyboard are sent to
+the modem. Characters read from the modem are displayed on the
+screen. When a
+.Nm
+peer is detected on the other side of the modem,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+.El
+.Pp
+.Sh MORE DETAILS
+.Bl -bullet
+.It
+Read the example configuration files. They are a good source of information.
+.It
+Use
+.Dq help ,
+.Dq alias ? ,
+.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/FAQ/userppp.html
+.It
+http://www.FreeBSD.org/handbook/userppp.html
+.El
+.Pp
+.El
+.Pp
+.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 XX
+.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/syslogd.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 ppp .
+.El
+.Pp
+.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 libalias 3 ,
+.Xr syslog 3 ,
+.Xr uucplock 3 ,
+.Xr crontab 5 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr radius.conf 5 ,
+.Xr resolv.conf 5 ,
+.Xr syslog.conf 5 ,
+.Xr adduser 8 ,
+.Xr chat 8 ,
+.Xr getty 8 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr named 8 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr route 8 ,
+.Xr syslogd 8 ,
+.Xr traceroute 8 ,
+.Xr vipw 8
+.Sh HISTORY
+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).
+.Pp
+It was substantially modified during 1997 by Brian Somers
+(brian@Awfulhak.org), and was ported to OpenBSD in November that year
+(just after the 2.2 release).
+.Pp
+Most of the code was rewritten by Brian Somers in early 1998 when
+multi-link ppp support was added.
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
new file mode 100644
index 0000000..ad28a00
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -0,0 +1,4499 @@
+.\" $Id: ppp.8,v 1.163 1999/03/19 09:00:08 brian Exp $
+.Dd 20 September 1995
+.nr XX \w'\fC00'
+.Os FreeBSD
+.Dt PPP 8
+.Sh NAME
+.Nm ppp
+.Nd Point to Point Protocol (a.k.a. user-ppp)
+.Sh SYNOPSIS
+.Nm
+.Oo
+.Fl auto |
+.Fl background |
+.Fl ddial |
+.Fl direct |
+.Fl dedicated
+.Oc
+.Op Fl alias
+.Op Ar system Ns
+.No ...
+.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).
+.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 your modem directly. When your
+modem is 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 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 aliased for outgoing packets and de-aliased 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) 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) 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. If
+.Pa libradius
+is available at compile time,
+.Nm
+will use it to make
+.Em RADIUS
+requests when configured to do so.
+.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 capability.
+If a device name is specified as
+.Em host Ns No : Ns Em port ,
+.Nm
+will open a TCP connection for transporting data rather than using a
+conventional serial device.
+.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.
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (ie. 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.
+.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 ppp .
+Refer to the
+.Sq ID0
+logging facility if you're interested in what exactly is done as user id
+zero.
+.Sh GETTING STARTED
+The following command line switches are understood by
+.Nm ppp :
+.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
+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 direct
+This is used for receiving incoming connections.
+.Nm
+ignores the
+.Dq set device
+line and uses descriptor 0 as the link.
+.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 never use any configured
+chat scripts.
+.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
+flags have been specified.
+.Nm
+loads any systems specified on the command line then provides an
+interactive prompt.
+.It Fl alias
+This flag doesn't control
+.Nm ppp Ns No 's
+mode. It does the equivalent of an
+.Dq enable alias yes .
+Additionally, if the
+.Fl auto
+flag is also specified, an implicit
+.Dq enable iface-alias
+is done.
+See below for details.
+.Pp
+Enabling IP aliasing allows
+.Nm ppp
+to act as a NAT or masquerading engine for all machines on an internal
+LAN. Refer to
+.Xr libalias 3
+for details.
+.El
+.Pp
+Additionally, one or more systems may be specified on the command line.
+A
+.Sq system
+is a configuration entry in
+.Pa /etc/ppp/ppp.conf .
+.Nm
+will read the
+.Dq default
+system from
+.Pa /etc/ppp/ppp.conf
+at startup, followed by each of the systems specifed on the command line.
+.Pp
+Only one of the
+.Fl auto ,
+.Fl background ,
+.Fl ddial ,
+.Fl direct ,
+.Fl dedicated
+and
+.Fl interactive
+switches may be specified.
+.Nm ppp Ns No 's
+.Sq mode
+may subsequently be changed with the
+.Dq set mode
+command (see below).
+.Pp
+For now, we'll stick to using interactive mode.
+.Pp
+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 that group contains the names of all users expected to use
+.Nm ppp .
+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 ppp Ns No s
+operation, you should configure your resolver so that it works correctly.
+This can be done by configuring a local DNS
+.Pq using Xr named 8
+or by adding the correct
+.Sq name-server
+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
+command 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 ppp 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
+.Pq 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 modem settings look like:
+.Bd -literal -offset indent
+ppp ON awfulhak> show modem
+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 your modem:
+.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 modem
+* 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 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 ppp .
+.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 ppp .
+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
+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 substition 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 ppp ,
+it was necessary to re-add routes such as the default route in the
+.Pa ppp.linkup
+file.
+.Nm
+now supports
+.Sq sticky routes ,
+where all routes that contain the
+.Dv HISADDR
+or
+.Dv MYADDR
+literals will automatically be updated when the values of
+.Dv HISADDR
+and/or
+.Dv MYADDR
+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 ,
+.Pq for example, Dq 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 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 maxinc
+is the maximun number of times
+.Nm
+should increment
+.Ar secs .
+The default value for
+.Ar maxinc
+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 "/usr/libexec/getty std.38400" 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
+.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 ppp .
+.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
+.Pp
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+This method differs in that we use
+.Nm ppp
+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
+.Pq 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 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
+.Pq 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 ppp Ns No s
+subsequent CBCP phase.
+.Sh PPP OVER TCP (a.k.a Tunnelling)
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying a host and port as the
+device:
+.Pp
+.Dl set device ui-gate:6669
+.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
+ add 10.0.1.0/24 10.0.4.2
+.Ed
+.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
+.Pq 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
+ set dial
+ set timeout 30
+ set log Phase Chat Connect hdlc LCP IPCP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+ add 10.0.2.0/24 10.0.4.1
+.Ed
+.Pp
+Again, if you're enabling PAP, you'll also need:
+.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.
+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.
+.Sh PACKET ALIASING
+The
+.Fl alias
+command line option enables 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 aliased so that
+they appear to come from the
+.Nm
+host, and incoming packets are de-aliased so that they are routed
+to the correct machine on the local area network.
+Packet aliasing 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 packet aliasing disabled.
+Then, the
+.Fl alias
+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
+.Oo
+.Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc
+.Oo Ar proto Op src Ar cmp port
+.Op dst Ar cmp port
+.Op estab
+.Op syn
+.Op finrst
+.Oc
+.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 19
+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
+is either
+.Sq permit
+or
+.Sq deny .
+If a given packet
+matches the rule, the associated action is taken immediately.
+.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.
+.It
+.Ar Proto
+must be one of
+.Sq icmp ,
+.Sq udp
+or
+.Sq tcp .
+.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.
+.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,
+ie. the default is to allow everything through.
+.It
+If no rule is matched to a packet, that packet will be discarded
+(blocked).
+.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
+.Pq 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
+.Pq or requests them .
+The deflate protocol is preferred by
+.Nm ppp .
+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
+.Pq 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
+.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 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 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 -literal -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
+try to insist that 192.244.177.2 be used first.
+.El
+.Pp
+.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 -literal -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 ******** Ns
+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 FreeBSD. If you're running
+.Nm
+on OpenBSD, 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 .
+.Pp
+This tells
+.Nm
+to add a default route to whatever the peer address is
+.Pq 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.
+.Pp
+Previous versions of
+.Nm
+required a similar entry in the
+.Pa /etc/ppp/ppp.linkup
+file. Since the advent of
+.Sq sticky routes ,
+this is no longer required.
+.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
+Do
+.Em NOT
+do this if you are running an local DNS, 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 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
+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 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 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
+(ie. 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 XX
+.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 USR2
+This signal, tells
+.Nm
+to close any existing server socket, dropping all existing diagnostic
+connections.
+.El
+.Pp
+.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
+.Pq 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
+.Pq 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. It's 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
+ set authname ppp
+ set authkey ppppassword
+
+ set mrru 1500
+ clone 1,2,3
+ link deflink remove
+.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 ppp 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 XX
+.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 XX
+.It acfcomp
+Default: Enabled and Accepted. ACFComp stands for Address and Control
+Field Compression. Non LCP packets usually have very similar address
+and control fields - making them easily compressible.
+.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 Ns No !
+.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 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,
+ppp 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 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
+.Pq 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 XX
+.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 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. 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
+or
+.Dv MYADDR
+values, entries are stored in the
+.Sq stick route
+list. Each time
+.Dv HISADDR
+or
+.Dv MYADDR
+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 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 modem
+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.
+.It iface-alias
+Default: Enabled if
+.Fl alias
+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 IP aliasing is enabled
+.Pq Dq alias enable yes .
+.Pp
+With this option enabled,
+.Nm
+will pass traffic for old interface addresses through the IP alias engine
+.Pq see Xr libalias 5 ,
+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 IP aliasing with
+.Dq alias enable off
+will also disable
+.Sq iface-alias .
+.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
+or
+.Sq HISADDR
+as the destination, and
+.Sq HISADDR
+as the
+.Ar gateway .
+.Sq MYADDR
+is replaced with the interface address and
+.Sq HISADDR
+is replaced with the interface destination (peer) address.
+.Pp
+If the
+.Ar add!
+command is used
+.Pq 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
+or
+.Dq MYADDR
+constants are considered
+.Sq sticky .
+They are stored in a list (use
+.Dq show ipcp
+to see the list), and each time the value of
+.Dv HISADDR
+or
+.Dv MYADDR
+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 XX
+.It allow user Ns Xo
+.Op s
+.Ar logname Ns No ...
+.Xc
+By default, only user id 0 is allowed access to
+.Nm ppp .
+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). Each successive
+.Dq allow users
+command overrides the previous one, 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 alias Ar command Op Ar args
+This command allows the control of the aliasing (or masquerading)
+facilities that are built into
+.Nm ppp .
+If aliasing is enabled on your system (it may be omitted at compile time),
+the following commands are possible:
+.Bl -tag -width XX
+.It alias enable Op yes|no
+This command either switches aliasing on or turns it off.
+The
+.Fl alias
+command line flag is synonymous with
+.Dq alias enable yes .
+.It alias 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 alias deny_incoming Op yes|no
+If set to yes, this command will refuse all incoming connections
+by dropping the packets in much the same way as a firewall would.
+.It alias help|?
+This command gives a summary of available alias commands.
+.It alias log Op yes|no
+This option causes various aliasing statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+.It alias port Ar proto Ar targetIP Ns Xo
+.No : Ns Ar port Ns
+.Oo
+.No - Ns Ar port
+.Oc Ar aliasport Ns
+.Oo
+.No - Ns Ar aliasport Ns
+.Oc
+.Xc
+This command causes incoming
+.Ar proto
+connections to port
+.Ar aliasport
+to be redirected to port
+.Ar port
+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
+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 port are possible.
+.It alias pptp Op Ar addr
+This tells
+.Nm
+to alias any
+.Em G Ns No eneral
+.Em R Ns No outing
+.Em E Ns No encapsulated
+.Pq Dv IPPROTO_GRE
+packets using
+.Ar addr
+rather than the local interface address. This allows the uses of the
+.Em P Ns No oint
+to
+.Em P Ns No oint
+.Em T Ns No unneling
+.Em P Ns No rotocol
+on a machine on the internal network.
+.Pp
+If
+.Ar addr
+is not specified,
+.Dv PPTP
+aliasing is disabled.
+.It "alias proxy cmd" Ar arg Ns No ...
+This command tells
+.Nm
+to proxy certain connections, redirecting them to a given server. Refer
+to the description of
+.Fn PacketAliasProxyRule
+in
+.Xr libalias 3
+for details of the available commands.
+.It alias same_ports Op yes|no
+When enabled, this command will tell the alias library 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 alias use_sockets Op yes|no
+When enabled, this option tells the alias library to create a
+socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+.It alias unregistered_only Op yes|no
+Only alter outgoing packets with an unregistered source ad-
+dress. 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.alias
+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 PEER_ENDDISC
+.It Li AUTHNAME
+This is replaced with the local
+.Ar authname
+value. See the
+.Dq set authname
+command below.
+.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 INTERFACE
+This is replaced with the name of the interface that's in use.
+.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 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 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.
+.El
+.Pp
+These substitutions are also done by the
+.Dq set proctitle
+command.
+.Pp
+If you wish to pause
+.Nm
+while the command executes, use the
+.Dq shell
+command instead.
+.It clear modem|ipcp Op current|overall|peak...
+Clear the specified throughput values at either the
+.Dq modem
+or
+.Dq ipcp
+level. If
+.Dq modem
+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
+.Pq 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 ppp .
+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
+.Pq note the trailing Dq \&! ,
+.Nm
+will not complain if the route does not already exist.
+.It dial|call Op Ar label
+When used with no argument, this command is the same as the
+.Dq open
+command. When one or more
+.Ar label
+is specified, a
+.Dq load
+will be done first.
+.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 modem 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 iface Ar command Op args
+This command is used to control the interface used by
+.Nm ppp .
+.Ar Command
+may be one of the following:
+.Bl -tag -width XX
+.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
+.Pq 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
+If this command is used while
+.Nm
+is in the OPENED state or while in
+.Fl auto
+mode, all addresses except for the IPCP 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
+.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 possbile
+.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(s)
+from the
+.Pa ppp.conf
+file. If
+.Ar label
+is not given, the
+.Ar default
+label is used.
+.It open Op lcp|ccp|ipcp
+This is the opposite of the
+.Dq close
+command. Using
+.Dq open
+with no arguments is the same as using
+.Dq dial
+with no arguments, where all closed links are brought up (some auto
+links may not come up based on the
+.Dq set autoload
+command) using the current configuration.
+.Pp
+If the
+.Dq lcp
+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 save
+This option is not (yet) implemented.
+.It set Ns Xo
+.No Op up
+.Ar var value
+.Xc
+This option allows the setting of any of the following variables:
+.Bl -tag -width XX
+.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 \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\P
+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 exclaimation 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
+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 max-duration max-load Op Ar min-duration min-load
+.Xc
+These settings apply only in multi-link mode and all default to zero.
+When more than one
+.Ar demand-dial
+.Pq 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 at least
+.Ar max-load
+packets have been in the send queue for
+.Ar max-duration
+seconds. Because both values default to zero,
+.Ar demand-dial
+links will simply come up one at a time by default.
+.Pp
+If two or more links are open, at least one of which is a
+.Ar demand-dial
+link, a
+.Ar demand-dial
+link will be closed when there is less than
+.Ar min-packets
+in the queue for more than
+.Ar min-duration .
+If
+.Ar min-duration
+is zero, this timer is disabled. Because both values default to zero,
+.Ar demand-dial
+links will stay active until the bundle idle timer expires.
+.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 Ns No ).
+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
+.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
+Microsofts callback control protocol is used. See
+.Dq set cbcp
+below.
+.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 seperated 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 if you wish callback to be optional.
+.El
+.Pp
+.It set cbcp Oo Xo
+.No *| Ns Ar number Ns No
+.Oo
+.No , Ns Ar number Ns
+.Oc
+.No ...
+.Op Ar delay Op Ar retry
+.Oc
+.Xc
+If no arguments are given, CBCP (Microsofts 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 Ar seconds Ns Op \&!
+Normally,
+.Nm
+checks for the existence of carrier 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 NULL-modem cables), logs the fact and stops checking
+for carrier. However, some modems take some time to assert the carrier
+signal, resulting in
+.Nm ppp Ns No s
+inability to detect when the link is dropped.
+.Ar Seconds
+specifies the number of seconds that
+.Nm
+should wait after the login script has finished before first checking for
+carrier.
+.Pp
+If
+.Ar seconds
+is followed immediately by an exclaimation mark
+.Pq Dq \&! ,
+.Nm
+will
+.Em require
+carrier. If carrier is not detected at the first check, the link will
+be considered disconnected.
+.Pp
+Carrier
+.Em require Ns No ment
+is ignored when the link is not a tty device.
+.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 20
+packets (or
+.Em 20 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 .
+All serial device names are expected to begin with
+.Pa /dev/ .
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must either begin with an exclamation mark
+.Pq Dq \&!
+or be of the format
+.Dq host:port .
+.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
+.Dq host:port
+pair is given,
+.Nm
+will attempt to connect to the given
+.Dq host
+on the given
+.Dq port .
+Refer to the section on
+.Em PPP OVER TCP
+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:
+.Bd -unfilled -offset indent
+.It Li \\\\\\\\\\\\\\\\c
+When used as the last character in a
+.Sq send
+string, this indicates that a newline should not be appended.
+.It Li \\\\\\\\\\\\\\\\d
+When the chat script encounters this sequence, it delays two seconds.
+.It Li \\\\\\\\\\\\\\\\p
+When the chat script encounters this sequence, it delays for one quarter of
+a second.
+.It Li \\\\\\\\\\\\\\\\n
+This is replaced with a newline character.
+.It Li \\\\\\\\\\\\\\\\r
+This is replaced with a carriage return character.
+.It Li \\\\\\\\\\\\\\\\s
+This is replaced with a space character.
+.It Li \\\\\\\\\\\\\\\\t
+This is replaced with a tab character.
+.It Li \\\\\\\\\\\\\\\\T
+This is replaced by the current phone number (see
+.Dq set phone
+below).
+.It Li \\\\\\\\\\\\\\\\P
+This is replaced by the current
+.Ar authkey
+value (see
+.Dq set authkey
+above).
+.It Li \\\\\\\\\\\\\\\\U
+This is replaced by the current
+.Ar authname
+value (see
+.Dq set authname
+above).
+.Ed
+.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 \&! .
+When the command is executed, standard input and standard output are
+directed to the modem 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,
+.Nm
+will send the information to the peer using the LCP endpoint discriminator
+option. The following discriminators may be set:
+.Bd -unfilled -offset 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.
+.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.
+.Ed
+.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
+.Oo Ar src_addr Ns Op / Ns Ar width
+.Op Ar dst_addr Ns Op / Ns Ar width
+.Oc Oo tcp|udp|icmp Op src lt|eq|gt Ar port
+.Op dst lt|eq|gt Ar port
+.Op estab
+.Op syn
+.Op finrst
+.Oc
+.Xc
+.Nm
+supports four filter sets. The
+.Em alive
+filter specifies packets that keep the connection alive - reseting 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
+alias engine. By default all filter sets allow all packets to pass.
+Rules are processed in order according to
+.Ar rule-no .
+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 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 modem
+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
+.Ar Hisaddr
+may also be specified as a range of IP numbers in the format
+.Bd -literal -offset indent
+.Ar \&IP Ns Oo \&- Ns Ar \&IP Ns Xo
+.Oc Oo , Ns Ar \&IP Ns
+.Op \&- Ns Ar \&IP Ns
+.Oc No ...
+.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 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
+.Pq 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 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 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 netogiations 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 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 IP aliasing 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 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 Op Ar value
+The default MRU (Maximum Receive Unit) is 1500. If it is increased, the
+other side *may* increase its mtu. There is no point in decreasing the
+MRU to below the default as the
+.Em PPP
+protocol *must* be able to accept packets of at least 1500 octets. If
+no argument is given, 1500 is assumed.
+.It set mtu Op Ar value
+The default MTU is 1500. At negotiation time,
+.Nm
+will accept whatever MRU or MRRU that the peer wants (assuming it's
+not less than 296 bytes). If the MTU is set,
+.Nm
+will not accept MRU/MRRU values less than
+.Ar value .
+When negotiations are complete, the MTU is assigned to the interface, even
+if the peer requested a higher value MRU/MRRU. This can be useful for
+limiting your packet size (giving better bandwidth sharing at the expense
+of more header data).
+.Pp
+If no
+.Ar value
+is given, 1500, or whatever the peer asks for is used.
+.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 or CHAP 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 neither PAP or CHAP 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_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
+.El
+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 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 maxinc
+times.
+.Ar maxinc
+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|LocalName|none password Op Ar mask
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections.
+.Pp
+The word
+.Ar none
+instructs
+.Nm
+to close any existing socket.
+.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 must specify the octal umask
+that should be used with local domain sockets as a four character octal
+number beginning with
+.Sq 0 .
+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
+command 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 not be relied upon.
+.It set speed Ar value
+This sets the speed of the serial device.
+.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
+This command allows the setting of the idle timer. Refer to the
+section titled
+.Sx SETTING THE IDLE TIMER
+for further details.
+.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
+commanad 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 XX
+.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
+.Pq the same \&as Dq iface show .
+.It show ipcp
+Show the current IPCP statistics.
+.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 modem
+Show low level 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 ppp .
+.El
+.Pp
+.It term
+Go into terminal mode. Characters typed at the keyboard are sent to
+the modem. Characters read from the modem are displayed on the
+screen. When a
+.Nm
+peer is detected on the other side of the modem,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+.El
+.Pp
+.Sh MORE DETAILS
+.Bl -bullet
+.It
+Read the example configuration files. They are a good source of information.
+.It
+Use
+.Dq help ,
+.Dq alias ? ,
+.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/FAQ/userppp.html
+.It
+http://www.FreeBSD.org/handbook/userppp.html
+.El
+.Pp
+.El
+.Pp
+.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 XX
+.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/syslogd.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 ppp .
+.El
+.Pp
+.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 libalias 3 ,
+.Xr syslog 3 ,
+.Xr uucplock 3 ,
+.Xr crontab 5 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr radius.conf 5 ,
+.Xr resolv.conf 5 ,
+.Xr syslog.conf 5 ,
+.Xr adduser 8 ,
+.Xr chat 8 ,
+.Xr getty 8 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr named 8 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr route 8 ,
+.Xr syslogd 8 ,
+.Xr traceroute 8 ,
+.Xr vipw 8
+.Sh HISTORY
+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).
+.Pp
+It was substantially modified during 1997 by Brian Somers
+(brian@Awfulhak.org), and was ported to OpenBSD in November that year
+(just after the 2.2 release).
+.Pp
+Most of the code was rewritten by 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..1919a8e
--- /dev/null
+++ b/usr.sbin/ppp/pred.c
@@ -0,0 +1,340 @@
+/*-
+ * 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.
+ *
+ * $Id: pred.c,v 1.23 1999/03/11 01:49:15 brian Exp $
+ */
+
+#include <sys/types.h>
+
+#include <stdlib.h>
+#include <string.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 "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 void
+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");
+}
+
+static void *
+Pred1InitInput(struct lcp_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 lcp_opt *o)
+{
+ struct pred1_state *state;
+ state = (struct pred1_state *)malloc(sizeof(struct pred1_state));
+ if (state != NULL)
+ Pred1ResetOutput(state);
+ return state;
+}
+
+static int
+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 = mbuf_Length(bp) + 2; /* add count of proto */
+ mwp = mbuf_Alloc((orglen + 2) / 8 * 9 + 12, MB_HDLCOUT);
+ 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(INITFCS, 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->cnt = wp - MBUF_CTOP(mwp);
+ hdlc_Output(l, PRI_NORMAL, ccp_Proto(ccp), mwp);
+ return 1;
+}
+
+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 = mbuf_Alloc(MAX_MRU + 2, MB_IPIN);
+ cp = MBUF_CTOP(bp);
+ olen = mbuf_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);
+ mbuf_Free(bp);
+ mbuf_Free(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);
+ mbuf_Free(wp);
+ mbuf_Free(bp);
+ return NULL;
+ } else {
+ ccp->compin += len;
+ SyncTable(state, cp, pp, len);
+ cp += len;
+ pp += len;
+ }
+ *pp++ = *cp++; /* CRC */
+ *pp++ = *cp++;
+ fcs = hdlc_Fcs(INITFCS, bufp, wp->cnt = pp - bufp);
+ if (fcs == GOODFCS) {
+ wp->offset += 2; /* skip length */
+ wp->cnt -= 4; /* skip length & CRC */
+ pp = MBUF_CTOP(wp);
+ *proto = *pp++;
+ if (*proto & 1) {
+ wp->offset++;
+ wp->cnt--;
+ } else {
+ wp->offset += 2;
+ wp->cnt -= 2;
+ *proto = (*proto << 8) | *pp++;
+ }
+ mbuf_Free(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);
+ mbuf_Free(wp);
+ }
+ mbuf_Free(bp);
+ return NULL;
+}
+
+static void
+Pred1DictSetup(void *v, struct ccp *ccp, u_short proto, struct mbuf * bp)
+{
+}
+
+static const char *
+Pred1DispOpts(struct lcp_opt *o)
+{
+ return NULL;
+}
+
+static void
+Pred1InitOptsOutput(struct lcp_opt *o, const struct ccp_config *cfg)
+{
+ o->len = 2;
+}
+
+static int
+Pred1SetOptsOutput(struct lcp_opt *o)
+{
+ if (o->len != 2) {
+ o->len = 2;
+ return MODE_NAK;
+ }
+ return MODE_ACK;
+}
+
+static int
+Pred1SetOptsInput(struct lcp_opt *o, const struct ccp_config *cfg)
+{
+ return Pred1SetOptsOutput(o);
+}
+
+const struct ccp_algorithm Pred1Algorithm = {
+ TY_PRED1,
+ CCP_NEG_PRED1,
+ Pred1DispOpts,
+ {
+ Pred1SetOptsInput,
+ Pred1InitInput,
+ Pred1Term,
+ Pred1ResetInput,
+ Pred1Input,
+ Pred1DictSetup
+ },
+ {
+ Pred1InitOptsOutput,
+ Pred1SetOptsOutput,
+ Pred1InitOutput,
+ Pred1Term,
+ Pred1ResetOutput,
+ Pred1Output
+ },
+};
diff --git a/usr.sbin/ppp/pred.h b/usr.sbin/ppp/pred.h
new file mode 100644
index 0000000..08a0911
--- /dev/null
+++ b/usr.sbin/ppp/pred.h
@@ -0,0 +1,23 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pred.h,v 1.6 1997/10/26 01:03:35 brian Exp $
+ *
+ * TODO:
+ */
+
+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..027fe1b
--- /dev/null
+++ b/usr.sbin/ppp/probe.c
@@ -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.
+ *
+ * $Id: probe.c,v 1.1 1998/06/24 19:36:36 brian Exp $
+ */
+
+#include <sys/time.h>
+#include <unistd.h>
+
+#include "probe.h"
+#include "log.h"
+
+/* 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;
+}
+
+void
+probe_Init(struct probe *p)
+{
+ p->select_changes_time = select_changes_time() ? 1 : 0;
+ log_Printf(LogDEBUG, "Select changes time: %s\n",
+ p->select_changes_time ? "yes" : "no");
+}
diff --git a/usr.sbin/ppp/probe.h b/usr.sbin/ppp/probe.h
new file mode 100644
index 0000000..866283b
--- /dev/null
+++ b/usr.sbin/ppp/probe.h
@@ -0,0 +1,33 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+struct probe {
+ unsigned select_changes_time : 1;
+};
+
+extern void probe_Init(struct probe *);
diff --git a/usr.sbin/ppp/prompt.c b/usr.sbin/ppp/prompt.c
new file mode 100644
index 0000000..1cd5f7b
--- /dev/null
+++ b/usr.sbin/ppp/prompt.c
@@ -0,0 +1,555 @@
+/*-
+ * 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.
+ *
+ * $Id: prompt.c,v 1.13 1999/01/28 01:56:34 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.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/fcntl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "defs.h"
+#include "timer.h"
+#include "command.h"
+#include "log.h"
+#include "descriptor.h"
+#include "prompt.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "auth.h"
+#include "iplist.h"
+#include "throughput.h"
+#include "slcompress.h"
+#include "mbuf.h"
+#include "lqr.h"
+#include "hdlc.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 "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";
+ 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))
+ 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 descriptor *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 descriptor *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 descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct prompt *p = descriptor2prompt(d);
+ 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)
+ command_Decode(bundle, linebuff, n, p, p->src.from);
+ } 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 */
+ /* Fall through */
+
+ 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 descriptor *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->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->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..13a5fcc
--- /dev/null
+++ b/usr.sbin/ppp/prompt.h
@@ -0,0 +1,91 @@
+/*-
+ * 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.
+ *
+ * $Id: prompt.h,v 1.3 1998/05/23 22:24:49 brian Exp $
+ */
+
+#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 descriptor 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
+extern void prompt_vPrintf(struct prompt *, const char *, _BSD_VA_LIST_);
+#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/radius.c b/usr.sbin/ppp/radius.c
new file mode 100644
index 0000000..86f3a83
--- /dev/null
+++ b/usr.sbin/ppp/radius.c
@@ -0,0 +1,423 @@
+/*
+ * 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.
+ *
+ * $Id: radius.c,v 1.3 1999/02/06 02:54:47 brian Exp $
+ *
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <errno.h>
+#include <radlib.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.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 "ipcp.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 "bundle.h"
+
+/*
+ * 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 len, argc, addrs;
+ struct in_range dest;
+ struct in_addr gw;
+ const void *data;
+
+ r->cx.fd = -1; /* Stop select()ing */
+
+ switch (got) {
+ case RAD_ACCESS_ACCEPT:
+ log_Printf(LogPHASE, "Radius: ACCEPT received\n");
+ break;
+
+ case RAD_ACCESS_REJECT:
+ log_Printf(LogPHASE, "Radius: REJECT received\n");
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ case RAD_ACCESS_CHALLENGE:
+ /* we can't deal with this (for now) ! */
+ log_Printf(LogPHASE, "Radius: CHALLENGE received (can't handle yet)\n");
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ case -1:
+ log_Printf(LogPHASE, "radius: %s\n", rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+
+ default:
+ log_Printf(LogERROR, "rad_send_request: Failed %d: %s\n",
+ got, rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ /* So we've been accepted ! Let's see what we've got in our reply :-I */
+ r->ip.s_addr = r->mask.s_addr = INADDR_NONE;
+ r->mtu = 0;
+ r->vj = 0;
+ while ((got = rad_get_attr(r->cx.rad, &data, &len)) > 0) {
+ switch (got) {
+ case RAD_FRAMED_IP_ADDRESS:
+ r->ip = rad_cvt_addr(data);
+ log_Printf(LogPHASE, " IP %s\n", inet_ntoa(r->ip));
+ 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));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ log_Printf(LogPHASE, " Route: %s\n", nuke);
+ bundle = r->cx.auth->physical->dl->bundle;
+ dest.ipaddr.s_addr = dest.mask.s_addr = INADDR_ANY;
+ dest.width = 0;
+ argc = command_Interpret(nuke, strlen(nuke), argv);
+ if (argc < 2)
+ log_Printf(LogWARN, "radius: %s: Invalid route\n",
+ argc == 1 ? argv[0] : "\"\"");
+ else if ((strcasecmp(argv[0], "default") != 0 &&
+ !ParseAddr(&bundle->ncp.ipcp, argv[0], &dest.ipaddr,
+ &dest.mask, &dest.width)) ||
+ !ParseAddr(&bundle->ncp.ipcp, argv[1], &gw, NULL, NULL))
+ log_Printf(LogWARN, "radius: %s %s: Invalid route\n",
+ argv[0], argv[1]);
+ else {
+ if (dest.width == 32 && strchr(argv[0], '/') == NULL)
+ /* No mask specified - use the natural mask */
+ dest.mask = addr2mask(dest.ipaddr);
+ addrs = 0;
+
+ if (!strncasecmp(argv[0], "HISADDR", 7))
+ addrs = ROUTE_DSTHISADDR;
+ else if (!strncasecmp(argv[0], "MYADDR", 6))
+ addrs = ROUTE_DSTMYADDR;
+
+ if (gw.s_addr == INADDR_ANY) {
+ addrs |= ROUTE_GWHISADDR;
+ gw = bundle->ncp.ipcp.peer_ip;
+ } else if (strcasecmp(argv[1], "HISADDR") == 0)
+ addrs |= ROUTE_GWHISADDR;
+
+ route_Add(&r->routes, addrs, dest.ipaddr, dest.mask, gw);
+ }
+ free(nuke);
+ break;
+ }
+ }
+
+ if (got == -1) {
+ log_Printf(LogERROR, "rad_get_attr: %s (failing!)\n",
+ rad_strerror(r->cx.rad));
+ auth_Failure(r->cx.auth);
+ rad_close(r->cx.rad);
+ } 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 descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ radius_Continue(descriptor2radius(d), 1);
+}
+
+/*
+ * Behave as a struct descriptor (descriptor.h)
+ */
+static int
+radius_UpdateSet(struct descriptor *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 descriptor (descriptor.h)
+ */
+static int
+radius_IsSet(struct descriptor *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 descriptor (descriptor.h)
+ */
+static int
+radius_Write(struct descriptor *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->valid = 0;
+ r->cx.fd = -1;
+ *r->cfg.file = '\0';;
+ 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;
+ memset(&r->cx.timer, '\0', sizeof r->cx.timer);
+}
+
+/*
+ * Forget everything and go back to initialised state.
+ */
+void
+radius_Destroy(struct radius *r)
+{
+ r->valid = 0;
+ timer_Stop(&r->cx.timer);
+ route_DeleteAll(&r->routes);
+ if (r->cx.fd != -1) {
+ r->cx.fd = -1;
+ rad_close(r->cx.rad);
+ }
+}
+
+/*
+ * Start an authentication request to the RADIUS server.
+ */
+void
+radius_Authenticate(struct radius *r, struct authinfo *authp, const char *name,
+ const char *key, const char *challenge)
+{
+ struct timeval tv;
+ int got;
+
+ 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;
+
+ radius_Destroy(r);
+
+ if ((r->cx.rad = rad_open()) == NULL) {
+ log_Printf(LogERROR, "rad_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_ACCESS_REQUEST) != 0) {
+ log_Printf(LogERROR, "rad_create_request: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ 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;
+ }
+
+ if (challenge != NULL) {
+ /* We're talking CHAP */
+ if (rad_put_string(r->cx.rad, RAD_CHAP_PASSWORD, key) != 0 ||
+ rad_put_string(r->cx.rad, RAD_CHAP_CHALLENGE, challenge) != 0) {
+ log_Printf(LogERROR, "CHAP: rad_put_string: %s\n",
+ rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+ } else if (rad_put_string(r->cx.rad, RAD_USER_PASSWORD, key) != 0) {
+ /* We're talking PAP */
+ log_Printf(LogERROR, "PAP: rad_put_string: %s\n", rad_strerror(r->cx.rad));
+ rad_close(r->cx.rad);
+ return;
+ }
+
+ 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";
+ r->cx.timer.arg = r;
+ r->cx.auth = authp;
+ 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");
+ if (r->routes)
+ route_ShowSticky(p, r->routes, " Routes", 16);
+ } 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..37cf7b8
--- /dev/null
+++ b/usr.sbin/ppp/radius.h
@@ -0,0 +1,58 @@
+/*
+ * 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.
+ *
+ * $Id: radius.h,v 1.1 1999/01/28 01:56:34 brian Exp $
+ */
+
+struct radius {
+ struct descriptor desc; /* We're a sort of (selectable) descriptor */
+ 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 */
+ struct sticky_route *routes; /* FRAMED Routes */
+ struct {
+ char file[MAXPATHLEN]; /* Radius config file */
+ } cfg;
+};
+
+#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 void radius_Authenticate(struct radius *, struct authinfo *,
+ const char *, const char *, const char *);
diff --git a/usr.sbin/ppp/route.c b/usr.sbin/ppp/route.c
new file mode 100644
index 0000000..5b366d2
--- /dev/null
+++ b/usr.sbin/ppp/route.c
@@ -0,0 +1,580 @@
+/*
+ * PPP Routing related Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: route.c,v 1.54 1998/10/22 02:32:50 brian Exp $
+ *
+ */
+
+#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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/sysctl.h>
+#include <termios.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 "ipcp.h"
+#include "filter.h"
+#include "descriptor.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#include "bundle.h"
+#include "route.h"
+#include "prompt.h"
+#include "iface.h"
+
+static void
+p_sockaddr(struct prompt *prompt, struct sockaddr *phost,
+ struct sockaddr *pmask, int width)
+{
+ char buf[29];
+ struct sockaddr_in *ihost = (struct sockaddr_in *)phost;
+ struct sockaddr_in *mask = (struct sockaddr_in *)pmask;
+ struct sockaddr_dl *dl = (struct sockaddr_dl *)phost;
+
+ switch (phost->sa_family) {
+ case AF_INET:
+ if (!phost)
+ buf[0] = '\0';
+ else if (ihost->sin_addr.s_addr == INADDR_ANY)
+ strcpy(buf, "default");
+ else if (!mask)
+ strcpy(buf, inet_ntoa(ihost->sin_addr));
+ else {
+ u_int32_t msk = ntohl(mask->sin_addr.s_addr);
+ u_int32_t tst;
+ int bits;
+ int len;
+ struct sockaddr_in net;
+
+ for (tst = 1, bits=32; tst; tst <<= 1, bits--)
+ if (msk & tst)
+ break;
+
+ for (tst <<= 1; tst; tst <<= 1)
+ if (!(msk & tst))
+ break;
+
+ net.sin_addr.s_addr = ihost->sin_addr.s_addr & mask->sin_addr.s_addr;
+ strcpy(buf, inet_ntoa(net.sin_addr));
+ for (len = strlen(buf); len > 3; buf[len -= 2] = '\0')
+ if (strcmp(buf + len - 2, ".0"))
+ break;
+
+ if (tst) /* non-contiguous :-( */
+ sprintf(buf + strlen(buf),"&0x%08lx", (u_long)msk);
+ else
+ sprintf(buf + strlen(buf), "/%d", bits);
+ }
+ break;
+
+ 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);
+}
+
+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 nifs, debug_done; /* Figure out how many once, and debug once */
+
+ if (!nifs) {
+ int mib[6], have, had;
+ size_t needed;
+ char *buf, *ptr, *end;
+ struct sockaddr_dl *dl;
+ struct if_msghdr *ifm;
+
+ 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 "???";
+ }
+ if ((buf = malloc(needed)) == NULL)
+ return "???";
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0) {
+ free(buf);
+ return "???";
+ }
+ end = buf + needed;
+
+ have = 0;
+ for (ptr = buf; ptr < end; ptr += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)ptr;
+ 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));
+ nifs = 0;
+ if (ifs)
+ free(ifs);
+ return "???";
+ }
+ 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 (nifs < ifm->ifm_index)
+ 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 < 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 > nifs || ifs[idx-1] == NULL)
+ return "???";
+
+ return ifs[idx-1];
+}
+
+int
+route_Show(struct cmdargs const *arg)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa_dst, *sa_gw, *sa_mask;
+ char *sp, *ep, *cp, *wp;
+ 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;
+ wp = (char *)(rtm+1);
+
+ if (rtm->rtm_addrs & RTA_DST) {
+ sa_dst = (struct sockaddr *)wp;
+ wp += sa_dst->sa_len;
+ } else
+ sa_dst = NULL;
+
+ if (rtm->rtm_addrs & RTA_GATEWAY) {
+ sa_gw = (struct sockaddr *)wp;
+ wp += sa_gw->sa_len;
+ } else
+ sa_gw = NULL;
+
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sa_mask = (struct sockaddr *)wp;
+ wp += sa_mask->sa_len;
+ } else
+ sa_mask = NULL;
+
+ p_sockaddr(arg->prompt, sa_dst, sa_mask, 20);
+ p_sockaddr(arg->prompt, sa_gw, NULL, 20);
+
+ p_flags(arg->prompt, rtm->rtm_flags, 6);
+ prompt_Printf(arg->prompt, " %s\n", Index2Nam(rtm->rtm_index));
+ }
+ 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;
+ struct in_addr sa_dst, sa_none;
+ int pass;
+ size_t needed;
+ char *sp, *cp, *ep;
+ int mib[6];
+
+ log_Printf(LogDEBUG, "route_IfDelete (%d)\n", bundle->iface->index);
+ sa_none.s_addr = INADDR_ANY;
+
+ 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 necessary 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;
+ sa = (struct sockaddr *) (rtm + 1);
+ log_Printf(LogDEBUG, "route_IfDelete: addrs: %x, Netif: %d (%s),"
+ " flags: %x, dst: %s ?\n", rtm->rtm_addrs, rtm->rtm_index,
+ Index2Nam(rtm->rtm_index), rtm->rtm_flags,
+ inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
+ if (rtm->rtm_addrs & RTA_DST && rtm->rtm_addrs & RTA_GATEWAY &&
+ rtm->rtm_index == bundle->iface->index &&
+ (all || (rtm->rtm_flags & RTF_GATEWAY))) {
+ sa_dst.s_addr = ((struct sockaddr_in *)sa)->sin_addr.s_addr;
+ sa = (struct sockaddr *)((char *)sa + sa->sa_len);
+ if (sa->sa_family == AF_INET || sa->sa_family == AF_LINK) {
+ if ((pass == 0 && (rtm->rtm_flags & RTF_WASCLONED)) ||
+ (pass == 1 && !(rtm->rtm_flags & RTF_WASCLONED))) {
+ log_Printf(LogDEBUG, "route_IfDelete: Remove it (pass %d)\n", pass);
+ bundle_SetRoute(bundle, RTM_DELETE, sa_dst, sa_none, sa_none, 0, 0);
+ } else
+ log_Printf(LogDEBUG, "route_IfDelete: Skip it (pass %d)\n", pass);
+ } else
+ log_Printf(LogDEBUG,
+ "route_IfDelete: Can't remove routes of %d family !\n",
+ sa->sa_family);
+ }
+ }
+ }
+ free(sp);
+}
+
+int
+GetIfIndex(char *name)
+{
+ int idx;
+ const char *got;
+
+ idx = 1;
+ while (strcmp(got = Index2Nam(idx), "???"))
+ if (!strcmp(got, name))
+ return idx;
+ else
+ idx++;
+ return -1;
+}
+
+void
+route_Change(struct bundle *bundle, struct sticky_route *r,
+ struct in_addr me, struct in_addr peer)
+{
+ struct in_addr none, del;
+
+ none.s_addr = INADDR_ANY;
+ for (; r; r = r->next) {
+ if ((r->type & ROUTE_DSTMYADDR) && r->dst.s_addr != me.s_addr) {
+ del.s_addr = r->dst.s_addr & r->mask.s_addr;
+ bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1, 0);
+ r->dst = me;
+ if (r->type & ROUTE_GWHISADDR)
+ r->gw = peer;
+ } else if ((r->type & ROUTE_DSTHISADDR) && r->dst.s_addr != peer.s_addr) {
+ del.s_addr = r->dst.s_addr & r->mask.s_addr;
+ bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1, 0);
+ r->dst = peer;
+ if (r->type & ROUTE_GWHISADDR)
+ r->gw = peer;
+ } else if ((r->type & ROUTE_GWHISADDR) && r->gw.s_addr != peer.s_addr)
+ r->gw = peer;
+ bundle_SetRoute(bundle, RTM_ADD, r->dst, r->gw, r->mask, 1, 0);
+ }
+}
+
+void
+route_Clean(struct bundle *bundle, struct sticky_route *r)
+{
+ struct in_addr none, del;
+
+ none.s_addr = INADDR_ANY;
+ for (; r; r = r->next) {
+ del.s_addr = r->dst.s_addr & r->mask.s_addr;
+ bundle_SetRoute(bundle, RTM_DELETE, del, none, none, 1, 0);
+ }
+}
+
+void
+route_Add(struct sticky_route **rp, int type, struct in_addr dst,
+ struct in_addr mask, struct in_addr gw)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ r = NULL;
+ while (*rp) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && (*rp)->dst.s_addr == dst.s_addr)) {
+ /* 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;
+ r->dst = dst;
+ r->mask = mask;
+ r->gw = gw;
+ *rp = r;
+}
+
+void
+route_Delete(struct sticky_route **rp, int type, struct in_addr dst)
+{
+ struct sticky_route *r;
+ int dsttype = type & ROUTE_DSTANY;
+
+ for (; *rp; rp = &(*rp)->next) {
+ if ((dsttype && dsttype == ((*rp)->type & ROUTE_DSTANY)) ||
+ (!dsttype && dst.s_addr == ((*rp)->dst.s_addr & (*rp)->mask.s_addr))) {
+ 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 def;
+ 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) {
+ def = r->dst.s_addr == INADDR_ANY && r->mask.s_addr == INADDR_ANY;
+
+ prompt_Printf(p, "%*sadd ", tlen ? 0 : indent, "");
+ tlen = 0;
+ if (r->type & ROUTE_DSTMYADDR)
+ prompt_Printf(p, "MYADDR");
+ else if (r->type & ROUTE_DSTHISADDR)
+ prompt_Printf(p, "HISADDR");
+ else if (!def)
+ prompt_Printf(p, "%s", inet_ntoa(r->dst));
+
+ if (def)
+ prompt_Printf(p, "default ");
+ else
+ prompt_Printf(p, " %s ", inet_ntoa(r->mask));
+
+ if (r->type & ROUTE_GWHISADDR)
+ prompt_Printf(p, "HISADDR\n");
+ else
+ prompt_Printf(p, "%s\n", inet_ntoa(r->gw));
+ }
+}
diff --git a/usr.sbin/ppp/route.h b/usr.sbin/ppp/route.h
new file mode 100644
index 0000000..590a4e2
--- /dev/null
+++ b/usr.sbin/ppp/route.h
@@ -0,0 +1,54 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: route.h,v 1.11 1998/05/21 21:48:12 brian Exp $
+ *
+ */
+
+struct bundle;
+struct cmdargs;
+
+#define ROUTE_STATIC 0
+#define ROUTE_DSTMYADDR 1
+#define ROUTE_DSTHISADDR 2
+#define ROUTE_DSTANY 3
+#define ROUTE_GWHISADDR 4 /* May be ORd with DST_MYADDR */
+
+struct sticky_route {
+ int type; /* ROUTE_* value (not _STATIC) */
+ struct sticky_route *next; /* next in list */
+
+ struct in_addr dst;
+ struct in_addr mask;
+ struct in_addr gw;
+};
+
+extern int GetIfIndex(char *);
+extern int route_Show(struct cmdargs const *);
+extern void route_IfDelete(struct bundle *, int);
+extern const char *Index2Nam(int);
+extern void route_Change(struct bundle *, struct sticky_route *,
+ struct in_addr, struct in_addr);
+extern void route_Add(struct sticky_route **, int, struct in_addr,
+ struct in_addr, struct in_addr);
+extern void route_Delete(struct sticky_route **, int, struct in_addr);
+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);
diff --git a/usr.sbin/ppp/server.c b/usr.sbin/ppp/server.c
new file mode 100644
index 0000000..bb5b96a
--- /dev/null
+++ b/usr.sbin/ppp/server.c
@@ -0,0 +1,291 @@
+/*-
+ * 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.
+ *
+ * $Id: server.c,v 1.24 1999/03/07 11:54:43 brian Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+
+#include <errno.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 "id.h"
+#include "prompt.h"
+
+static int
+server_UpdateSet(struct descriptor *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 descriptor *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;
+}
+
+#define IN_SIZE sizeof(struct sockaddr_in)
+#define UN_SIZE sizeof(struct sockaddr_in)
+#define ADDRSZ (IN_SIZE > UN_SIZE ? IN_SIZE : UN_SIZE)
+
+static void
+server_Read(struct descriptor *d, struct bundle *bundle, const fd_set *fdset)
+{
+ struct server *s = descriptor2server(d);
+ char hisaddr[ADDRSZ];
+ struct sockaddr *sa = (struct sockaddr *)hisaddr;
+ struct sockaddr_in *in = (struct sockaddr_in *)hisaddr;
+ int ssize = ADDRSZ, wfd;
+ struct prompt *p;
+
+ 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
+ wfd = -1;
+
+ if (wfd >= 0)
+ switch (sa->sa_family) {
+ case AF_LOCAL:
+ log_Printf(LogPHASE, "Connected to local client.\n");
+ break;
+
+ case AF_INET:
+ if (ntohs(in->sin_port) < 1024) {
+ log_Printf(LogALERT, "Rejected client connection from %s:%u"
+ "(invalid port number) !\n",
+ inet_ntoa(in->sin_addr), ntohs(in->sin_port));
+ close(wfd);
+ wfd = -1;
+ break;
+ }
+ log_Printf(LogPHASE, "Connected to client from %s:%u\n",
+ inet_ntoa(in->sin_addr), in->sin_port);
+ break;
+
+ 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->rm, sizeof p->src.from - 1);
+ p->src.from[sizeof p->src.from - 1] = '\0';
+ break;
+ case AF_INET:
+ p->src.type = "tcp";
+ snprintf(p->src.from, sizeof p->src.from, "%s:%u",
+ inet_ntoa(in->sin_addr), in->sin_port);
+ break;
+ }
+ 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 descriptor *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
+};
+
+int
+server_LocalOpen(struct bundle *bundle, const char *name, mode_t mask)
+{
+ int s;
+
+ if (server.rm && !strcmp(server.rm, name)) {
+ if (chmod(server.rm, mask))
+ log_Printf(LogERROR, "Local: chmod: %s\n", strerror(errno));
+ return 0;
+ }
+
+ memset(&server.ifsun, '\0', sizeof server.ifsun);
+ server.ifsun.sun_len = strlen(name);
+ if (server.ifsun.sun_len > sizeof server.ifsun.sun_path - 1) {
+ log_Printf(LogERROR, "Local: %s: Path too long\n", name);
+ return 2;
+ }
+ server.ifsun.sun_family = AF_LOCAL;
+ strcpy(server.ifsun.sun_path, name);
+
+ s = ID0socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "Local: socket: %s\n", strerror(errno));
+ return 3;
+ }
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (mask != (mode_t)-1)
+ mask = umask(mask);
+ if (bind(s, (struct sockaddr *)&server.ifsun, sizeof server.ifsun) < 0) {
+ if (mask != (mode_t)-1)
+ umask(mask);
+ log_Printf(LogWARN, "Local: bind: %s\n", strerror(errno));
+ close(s);
+ return 4;
+ }
+ if (mask != (mode_t)-1)
+ umask(mask);
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Local: Unable to listen to socket - BUNDLE overload?\n");
+ close(s);
+ ID0unlink(name);
+ return 5;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.rm = server.ifsun.sun_path;
+ log_Printf(LogPHASE, "Listening at local socket %s.\n", name);
+ return 0;
+}
+
+int
+server_TcpOpen(struct bundle *bundle, int port)
+{
+ struct sockaddr_in ifsin;
+ int s;
+
+ if (server.port == port)
+ return 0;
+
+ s = ID0socket(PF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ log_Printf(LogERROR, "Tcp: socket: %s\n", strerror(errno));
+ return 7;
+ }
+ memset(&ifsin, '\0', sizeof ifsin);
+ ifsin.sin_family = AF_INET;
+ ifsin.sin_addr.s_addr = INADDR_ANY;
+ ifsin.sin_port = htons(port);
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (bind(s, (struct sockaddr *)&ifsin, sizeof ifsin) < 0) {
+ log_Printf(LogWARN, "Tcp: bind: %s\n", strerror(errno));
+ close(s);
+ return 8;
+ }
+ if (listen(s, 5) != 0) {
+ log_Printf(LogERROR, "Tcp: Unable to listen to socket: %s\n",
+ strerror(errno));
+ close(s);
+ return 9;
+ }
+ server_Close(bundle);
+ server.fd = s;
+ server.port = port;
+ log_Printf(LogPHASE, "Listening at port %d.\n", port);
+ return 0;
+}
+
+int
+server_Close(struct bundle *bundle)
+{
+ if (server.fd >= 0) {
+ if (server.rm) {
+ 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)
+ ID0unlink(un.sun_path);
+ server.rm = NULL;
+ }
+ close(server.fd);
+ server.fd = -1;
+ server.port = 0;
+ /* Drop associated prompts */
+ log_DestroyPrompts(&server);
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr.sbin/ppp/server.h b/usr.sbin/ppp/server.h
new file mode 100644
index 0000000..55052ab
--- /dev/null
+++ b/usr.sbin/ppp/server.h
@@ -0,0 +1,49 @@
+/*-
+ * 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.
+ *
+ * $Id: server.h,v 1.4.2.5 1998/05/01 19:25:52 brian Exp $
+ */
+
+struct bundle;
+
+struct server {
+ struct descriptor desc;
+ int fd;
+ char passwd[50];
+
+ struct sockaddr_un ifsun; /* local socket */
+ char *rm; /* Points to local socket path */
+
+ u_short port; /* tcp socket */
+};
+
+#define descriptor2server(d) \
+ ((d)->type == SERVER_DESCRIPTOR ? (struct server *)(d) : NULL)
+
+extern struct server server;
+
+extern int server_LocalOpen(struct bundle *, const char *, mode_t);
+extern int server_TcpOpen(struct bundle *, int);
+extern int server_Close(struct bundle *);
diff --git a/usr.sbin/ppp/sig.c b/usr.sbin/ppp/sig.c
new file mode 100644
index 0000000..1a771ae
--- /dev/null
+++ b/usr.sbin/ppp/sig.c
@@ -0,0 +1,107 @@
+/*-
+ * 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.
+ *
+ * $Id: sig.c,v 1.13 1998/05/21 21:48:20 brian Exp $
+ */
+
+#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 */
+
+static void
+signal_recorder(int sig)
+{
+ caused[sig - 1]++;
+ necessary = 1;
+}
+
+
+/*
+ * Set up signal_recorder, and record handler as the function to ultimately
+ * call in handle_signal()
+*/
+
+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 */
+
+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..ed49509
--- /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.
+ *
+ * $Id: sig.h,v 1.12 1998/05/21 21:48:23 brian Exp $
+ */
+
+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..881006f
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.c
@@ -0,0 +1,590 @@
+/*
+ * 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.
+ *
+ * $Id: slcompress.c,v 1.23 1999/03/31 13:33:43 brian Exp $
+ *
+ * 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/un.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.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 "ipcp.h"
+#include "filter.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "link.h"
+#include "mp.h"
+#ifndef NORADIUS
+#include "radius.h"
+#endif
+#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->cnt < 40) {
+ log_Printf(LogDEBUG, "??? 1 ip_off = %x, cnt = %d\n",
+ ip->ip_off, m->cnt);
+ 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->cnt)
+ 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->cnt)
+ 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;
+
+ /* (fall through) */
+
+ 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->cnt -= hlen;
+ 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;
+
+ 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_ip.ip_sum = 0;
+ 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;
+ if (hlen == 0)
+ goto bad; /* We've been pointed at a not-yet-used slot ! */
+ 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;
+
+ cp -= cs->cs_hlen;
+ len += cs->cs_hlen;
+ cs->cs_ip.ip_len = htons(len);
+ memcpy(cp, &cs->cs_ip, cs->cs_hlen);
+ *bufp = cp;
+
+ /* recompute the ip header checksum */
+ {
+ u_short sum, *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);
+
+ /* Watch out for alighment problems.... */
+ sum = ~changes;
+ bp = &((struct ip *)cp)->ip_sum;
+ memcpy(bp, &sum, sizeof *bp);
+ }
+ 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..6af7905
--- /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.
+ *
+ * $Id: slcompress.h,v 1.11 1998/05/21 21:48:30 brian Exp $
+ *
+ * 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/systems.c b/usr.sbin/ppp/systems.c
new file mode 100644
index 0000000..0e26711
--- /dev/null
+++ b/usr.sbin/ppp/systems.c
@@ -0,0 +1,407 @@
+/*
+ * System configuration routines
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: systems.c,v 1.41 1999/02/02 09:35:30 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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", _PATH_PPP, 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 $.... */
+static void
+InterpretArg(char *from, char *to)
+{
+ const char *env;
+ char *ptr, *startto, *endto;
+ int len;
+
+ startto = to;
+ endto = to + LINE_LEN - 1;
+
+ while(issep(*from))
+ from++;
+ if (*from == '~') {
+ ptr = strchr(++from, '/');
+ len = ptr ? ptr - from : strlen(from);
+ if (len == 0) {
+ if ((env = getenv("HOME")) == NULL)
+ env = _PATH_PPP;
+ strncpy(to, env, endto - to);
+ } else {
+ struct passwd *pwd;
+
+ strncpy(to, from, len);
+ to[len] = '\0';
+ pwd = getpwnam(to);
+ if (pwd)
+ strncpy(to, pwd->pw_dir, endto-to);
+ else
+ strncpy(to, _PATH_PPP, endto - to);
+ endpwent();
+ }
+ *endto = '\0';
+ to += strlen(to);
+ from += len;
+ }
+
+ while (to < endto && *from != '\0') {
+ if (*from == '$') {
+ 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);
+ }
+ } else
+ *to++ = *from++;
+ }
+ while (to > startto) {
+ to--;
+ if (!issep(*to)) {
+ to++;
+ break;
+ }
+ }
+ *to = '\0';
+}
+
+#define CTRL_UNKNOWN (0)
+#define CTRL_INCLUDE (1)
+
+static int
+DecodeCtrlCommand(char *line, char *arg)
+{
+ if (!strncasecmp(line, "include", 7) && issep(line[7])) {
+ InterpretArg(line+8, arg);
+ 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;
+ char *user;
+
+ userok = 0;
+ user = getlogin();
+ if (user && *user)
+ for (f = arg->argn; f < arg->argc; f++)
+ if (!strcmp("*", arg->argv[f]) || !strcmp(user, arg->argv[f])) {
+ userok = 1;
+ break;
+ }
+
+ 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
+
+/* 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, *wp;
+ int n, len;
+ char line[LINE_LEN];
+ char filename[MAXPATHLEN];
+ int linenum;
+ int argc;
+ char *argv[MAXARGS];
+ int allowcmd;
+ int indent;
+ char arg[LINE_LEN];
+
+ if (*file == '/')
+ snprintf(filename, sizeof filename, "%s", file);
+ else
+ snprintf(filename, sizeof filename, "%s/%s", _PATH_PPP, 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)
+ return 0; /* got it */
+ break;
+ default:
+ log_Printf(LogWARN, "%s: %s: Invalid command\n", filename, cp);
+ break;
+ }
+ break;
+
+ default:
+ wp = strchr(cp, ':');
+ if (wp == NULL || wp[1] != '\0') {
+ log_Printf(LogWARN, "Bad rule in %s (line %d) - missing colon.\n",
+ filename, linenum);
+ continue;
+ }
+ *wp = '\0';
+ cp = strip(cp); /* lose any spaces between the label and the ':' */
+
+ if (strcmp(cp, name) == 0) {
+ /* We're in business */
+ if (how == SYSTEM_EXISTS)
+ 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 */
+ wp = strchr(cp, ':');
+ if ((how == SYSTEM_EXEC) && (wp == NULL || wp[1] != '\0'))
+ log_Printf(LogWARN, "Unindented command (%s line %d) - ignored\n",
+ filename, linenum);
+ break;
+ }
+
+ len = strlen(cp);
+ argc = command_Interpret(cp, len, argv);
+ allowcmd = argc > 0 && !strcasecmp(argv[0], "allow");
+ if ((!(how == SYSTEM_EXEC) && allowcmd) ||
+ ((how == SYSTEM_EXEC) && !allowcmd))
+ command_Run(bundle, argc, (char const *const *)argv, prompt,
+ name, cx);
+ }
+
+ 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;
+
+ def = !strcmp(name, "default");
+ how = ID0realuid() == 0 ? SYSTEM_EXISTS : SYSTEM_VALIDATE;
+ userok = 0;
+ modeok = 1;
+ modereq = mode;
+
+ rs = ReadSystem(NULL, "default", CONFFILE, prompt, NULL, how);
+
+ 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 _PATH_PPP "/" CONFFILE ": File not found";
+ }
+
+ 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..e85b865
--- /dev/null
+++ b/usr.sbin/ppp/systems.h
@@ -0,0 +1,37 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: systems.h,v 1.12 1998/06/15 19:05:51 brian Exp $
+ *
+ */
+
+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 int LoadCommand(struct cmdargs const *);
+extern int SaveCommand(struct cmdargs const *);
diff --git a/usr.sbin/ppp/throughput.c b/usr.sbin/ppp/throughput.c
new file mode 100644
index 0000000..6773f2e
--- /dev/null
+++ b/usr.sbin/ppp/throughput.c
@@ -0,0 +1,201 @@
+/*-
+ * 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.
+ *
+ * $Id: throughput.c,v 1.7 1998/06/12 17:45:41 brian Exp $
+ */
+
+#include <sys/types.h>
+
+#include <stdio.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 f;
+
+ t->OctetsIn = t->OctetsOut = 0;
+ for (f = 0; f < SAMPLE_PERIOD; f++)
+ t->SampleOctets[f] = 0;
+ t->OctetsPerSecond = t->BestOctetsPerSecond = t->nSample = 0;
+ t->BestOctetsPerSecondTime = time(NULL);
+ memset(&t->Timer, '\0', sizeof t->Timer);
+ t->Timer.name = "throughput";
+ t->uptime = 0;
+ t->rolling = 0;
+ throughput_stop(t);
+}
+
+void
+throughput_disp(struct pppThroughput *t, struct prompt *prompt)
+{
+ int secs_up;
+
+ secs_up = t->uptime ? time(NULL) - t->uptime : 0;
+ prompt_Printf(prompt, "Connect time: %d secs\n", secs_up);
+ if (secs_up == 0)
+ secs_up = 1;
+ prompt_Printf(prompt, "%ld octets in, %ld octets out\n",
+ t->OctetsIn, t->OctetsOut);
+ if (t->rolling) {
+ prompt_Printf(prompt, " overall %5ld bytes/sec\n",
+ (t->OctetsIn+t->OctetsOut)/secs_up);
+ prompt_Printf(prompt, " currently %5d bytes/sec\n", t->OctetsPerSecond);
+ prompt_Printf(prompt, " peak %5d bytes/sec on %s",
+ t->BestOctetsPerSecond, ctime(&t->BestOctetsPerSecondTime));
+ } else
+ prompt_Printf(prompt, "Overall %ld bytes/sec\n",
+ (t->OctetsIn+t->OctetsOut)/secs_up);
+}
+
+
+void
+throughput_log(struct pppThroughput *t, int level, const char *title)
+{
+ if (t->uptime) {
+ int secs_up;
+
+ secs_up = t->uptime ? time(NULL) - t->uptime : 0;
+ if (title)
+ log_Printf(level, "%s: Connect time: %d secs: %ld octets in, %ld octets"
+ " out\n", title, secs_up, t->OctetsIn, t->OctetsOut);
+ else
+ log_Printf(level, "Connect time: %d secs: %ld octets in, %ld octets out\n",
+ secs_up, t->OctetsIn, t->OctetsOut);
+ if (secs_up == 0)
+ secs_up = 1;
+ if (t->rolling)
+ log_Printf(level, " total %ld bytes/sec, peak %d bytes/sec on %s",
+ (t->OctetsIn+t->OctetsOut)/secs_up, t->BestOctetsPerSecond,
+ ctime(&t->BestOctetsPerSecondTime));
+ else
+ log_Printf(level, " total %ld bytes/sec\n",
+ (t->OctetsIn+t->OctetsOut)/secs_up);
+ }
+}
+
+static void
+throughput_sampler(void *v)
+{
+ struct pppThroughput *t = (struct pppThroughput *)v;
+ u_long old;
+
+ timer_Stop(&t->Timer);
+
+ old = t->SampleOctets[t->nSample];
+ t->SampleOctets[t->nSample] = t->OctetsIn + t->OctetsOut;
+ t->OctetsPerSecond = (t->SampleOctets[t->nSample] - old) / SAMPLE_PERIOD;
+ if (t->BestOctetsPerSecond < t->OctetsPerSecond) {
+ t->BestOctetsPerSecond = t->OctetsPerSecond;
+ t->BestOctetsPerSecondTime = time(NULL);
+ }
+ if (++t->nSample == SAMPLE_PERIOD)
+ t->nSample = 0;
+
+ timer_Start(&t->Timer);
+}
+
+void
+throughput_start(struct pppThroughput *t, const char *name, int rolling)
+{
+ timer_Stop(&t->Timer);
+ throughput_init(t);
+ t->rolling = rolling ? 1 : 0;
+ time(&t->uptime);
+ if (t->rolling) {
+ t->Timer.load = SECTICKS;
+ t->Timer.func = throughput_sampler;
+ t->Timer.name = name;
+ t->Timer.arg = t;
+ timer_Start(&t->Timer);
+ }
+}
+
+void
+throughput_stop(struct pppThroughput *t)
+{
+ timer_Stop(&t->Timer);
+}
+
+void
+throughput_addin(struct pppThroughput *t, int n)
+{
+ t->OctetsIn += n;
+}
+
+void
+throughput_addout(struct pppThroughput *t, int n)
+{
+ t->OctetsOut += n;
+}
+
+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 < SAMPLE_PERIOD; i++)
+ t->SampleOctets[i] = 0;
+ t->nSample = 0;
+ }
+
+ if (clear_type & THROUGHPUT_OVERALL) {
+ int secs_up;
+
+ secs_up = t->uptime ? time(NULL) - t->uptime : 1;
+ prompt_Printf(prompt, "overall cleared (was %5ld bytes/sec)\n",
+ (t->OctetsIn + t->OctetsOut)/secs_up);
+ t->OctetsIn = t->OctetsOut = 0;
+ t->uptime = time(NULL);
+ }
+
+ if (clear_type & THROUGHPUT_CURRENT) {
+ prompt_Printf(prompt, "current cleared (was %5d bytes/sec)\n",
+ t->OctetsPerSecond);
+ t->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 %5d bytes/sec on %s)\n",
+ t->BestOctetsPerSecond, time_buf);
+ t->BestOctetsPerSecond = 0;
+ t->BestOctetsPerSecondTime = time(NULL);
+ }
+}
diff --git a/usr.sbin/ppp/throughput.h b/usr.sbin/ppp/throughput.h
new file mode 100644
index 0000000..bdeac1e
--- /dev/null
+++ b/usr.sbin/ppp/throughput.h
@@ -0,0 +1,57 @@
+/*-
+ * 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.
+ *
+ * $Id: throughput.h,v 1.4 1998/06/09 18:49:10 brian Exp $
+ */
+
+#define SAMPLE_PERIOD 5
+
+#define THROUGHPUT_OVERALL 0x0001
+#define THROUGHPUT_CURRENT 0x0002
+#define THROUGHPUT_PEAK 0x0004
+#define THROUGHPUT_ALL THROUGHPUT_OVERALL | THROUGHPUT_CURRENT \
+ | THROUGHPUT_PEAK
+
+struct pppThroughput {
+ time_t uptime;
+ u_long OctetsIn;
+ u_long OctetsOut;
+ u_long SampleOctets[SAMPLE_PERIOD];
+ int OctetsPerSecond;
+ int BestOctetsPerSecond;
+ time_t BestOctetsPerSecondTime;
+ int nSample;
+ unsigned rolling : 1;
+ struct pppTimer Timer;
+};
+
+extern void throughput_init(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_stop(struct pppThroughput *);
+extern void throughput_addin(struct pppThroughput *, int);
+extern void throughput_addout(struct pppThroughput *, int);
+extern void throughput_clear(struct pppThroughput *, int, struct prompt *);
diff --git a/usr.sbin/ppp/timer.c b/usr.sbin/ppp/timer.c
new file mode 100644
index 0000000..0bf2258
--- /dev/null
+++ b/usr.sbin/ppp/timer.c
@@ -0,0 +1,244 @@
+/*
+ * PPP Timer Processing Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: timer.c,v 1.31 1998/06/27 14:18:11 brian Exp $
+ *
+ * TODO:
+ */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/time.h>
+#include <termios.h>
+
+#include "log.h"
+#include "sig.h"
+#include "timer.h"
+#include "descriptor.h"
+#include "prompt.h"
+
+static struct pppTimer *TimerList = NULL;
+
+static void StopTimerNoBlock(struct pppTimer *);
+
+static const char *
+tState2Nam(u_int state)
+{
+ static const char *StateNames[] = { "stopped", "running", "expired" };
+
+ if (state >= sizeof StateNames / sizeof StateNames[0])
+ return "unknown";
+ return StateNames[state];
+}
+
+void
+timer_Stop(struct pppTimer * tp)
+{
+ int omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+ StopTimerNoBlock(tp);
+ sigsetmask(omask);
+}
+
+void
+timer_Start(struct pppTimer * tp)
+{
+ struct pppTimer *t, *pt;
+ u_long ticks = 0;
+ int omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+
+ if (tp->state != TIMER_STOPPED)
+ StopTimerNoBlock(tp);
+
+ if (tp->load == 0) {
+ log_Printf(LogTIMER, "%s timer[%p] has 0 load!\n", tp->name, tp);
+ sigsetmask(omask);
+ return;
+ }
+ 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(0); /* Start the Timer Service */
+ }
+ if (t)
+ t->rest -= tp->rest;
+
+ sigsetmask(omask);
+}
+
+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_RUNNING) {
+ tp->next = NULL;
+ 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)
+ t->next->rest += tp->rest;
+ } else
+ log_Printf(LogERROR, "Oops, %s timer not found!!\n", tp->name);
+
+ tp->next = 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) {
+ next = exp->enext;
+ exp->enext = NULL;
+ if (exp->func)
+ (*exp->func)(exp->arg);
+ exp = next;
+ }
+ }
+}
+
+void
+timer_Show(int LogLevel, struct prompt *prompt)
+{
+ struct pppTimer *pt;
+ int rest = 0;
+
+#define SECS(val) ((val) / SECTICKS)
+#define HSECS(val) (((val) % SECTICKS) * 100 / SECTICKS)
+#define DISP \
+ "%s timer[%p]: freq = %ld.%02lds, next = %d.%02ds, 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", sys_errlist[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", sys_errlist[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..ae1f521
--- /dev/null
+++ b/usr.sbin/ppp/timer.h
@@ -0,0 +1,47 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: timer.h,v 1.7 1998/06/20 01:36:38 brian Exp $
+ *
+ * TODO:
+ */
+
+#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/tun.c b/usr.sbin/ppp/tun.c
new file mode 100644
index 0000000..d8edbd8
--- /dev/null
+++ b/usr.sbin/ppp/tun.c
@@ -0,0 +1,89 @@
+/*-
+ * 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.
+ *
+ * $Id: tun.c,v 1.10 1998/10/22 02:32:50 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h> /* For IFF_ defines */
+#include <net/if.h> /* For IFF_ defines */
+#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 <string.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+
+#include "mbuf.h"
+#include "log.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 "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 "bundle.h"
+#include "tun.h"
+
+void
+tun_configure(struct bundle *bundle, int mtu)
+{
+ struct tuninfo info;
+
+ memset(&info, '\0', sizeof info);
+ info.type = IFT_PPP;
+#ifndef NORADIUS
+ if (bundle->radius.valid && bundle->radius.mtu && bundle->radius.mtu < mtu) {
+ log_Printf(LogLCP, "Reducing MTU to radius value %lu\n",
+ bundle->radius.mtu);
+ info.mtu = bundle->radius.mtu;
+ } else
+#endif
+ info.mtu = mtu;
+
+ info.baudrate = bundle->ifSpeed;
+#ifdef __OpenBSD__
+ info.flags = IFF_UP|IFF_POINTOPOINT;
+#endif
+ if (ioctl(bundle->dev.fd, TUNSIFINFO, &info) < 0)
+ log_Printf(LogERROR, "tun_configure: ioctl(TUNSIFINFO): %s\n",
+ strerror(errno));
+}
diff --git a/usr.sbin/ppp/tun.h b/usr.sbin/ppp/tun.h
new file mode 100644
index 0000000..b66987d
--- /dev/null
+++ b/usr.sbin/ppp/tun.h
@@ -0,0 +1,46 @@
+/*-
+ * 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.
+ *
+ * $Id: tun.h,v 1.5 1998/06/27 12:03:43 brian Exp $
+ */
+
+struct tun_data {
+#ifdef __OpenBSD__
+ u_int32_t head;
+#endif
+ u_char data[MAX_MRU];
+};
+
+#ifdef __OpenBSD__
+#define tun_fill_header(f,proto) do { (f).head = htonl(proto); } while (0)
+#define tun_check_header(f,proto) ((f).head == htonl(proto))
+#else
+#define tun_fill_header(f,proto) do { } while (0)
+#define tun_check_header(f,proto) (1)
+#endif
+
+struct bundle;
+
+extern void tun_configure(struct bundle *, int);
diff --git a/usr.sbin/ppp/ua.h b/usr.sbin/ppp/ua.h
new file mode 100644
index 0000000..fb68e98
--- /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.
+ *
+ * $Id: ua.h,v 1.1 1998/09/04 18:26:00 brian Exp $
+ */
+
+#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/vjcomp.c b/usr.sbin/ppp/vjcomp.c
new file mode 100644
index 0000000..269ff95
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.c
@@ -0,0 +1,182 @@
+/*
+ * Input/Output VJ Compressed packets
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * 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 Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: vjcomp.c,v 1.26 1999/03/29 08:21:28 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "slcompress.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "defs.h"
+#include "iplist.h"
+#include "throughput.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 "bundle.h"
+#include "vjcomp.h"
+
+#define MAX_VJHEADER 16 /* Maximum size of compressed header */
+
+void
+vj_SendFrame(struct link *l, struct mbuf * bp, struct bundle *bundle)
+{
+ int type;
+ u_short proto;
+ struct ip *pip;
+ u_short cproto = bundle->ncp.ipcp.peer_compproto >> 16;
+
+ log_Printf(LogDEBUG, "vj_SendFrame: COMPPROTO = %x\n",
+ bundle->ncp.ipcp.peer_compproto);
+ bp = mbuf_Contiguous(bp);
+ pip = (struct ip *)MBUF_CTOP(bp);
+ if (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_SendFrame: type = %x\n", type);
+ switch (type) {
+ case TYPE_IP:
+ proto = PROTO_IP;
+ break;
+ case TYPE_UNCOMPRESSED_TCP:
+ proto = PROTO_VJUNCOMP;
+ break;
+ case TYPE_COMPRESSED_TCP:
+ proto = PROTO_VJCOMP;
+ break;
+ default:
+ log_Printf(LogALERT, "Unknown frame type %x\n", type);
+ mbuf_Free(bp);
+ return;
+ }
+ } else
+ proto = PROTO_IP;
+
+ if (!ccp_Compress(&l->ccp, l, PRI_NORMAL, proto, bp))
+ hdlc_Output(l, PRI_NORMAL, proto, bp);
+}
+
+static struct mbuf *
+VjUncompressTcp(struct ipcp *ipcp, struct mbuf * bp, u_char type)
+{
+ u_char *bufp;
+ int len, olen, rlen;
+ struct mbuf *nbp;
+ u_char work[MAX_HDR + MAX_VJHEADER]; /* enough to hold TCP/IP header */
+
+ bp = mbuf_Contiguous(bp);
+ olen = len = mbuf_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) {
+ mbuf_Free(bp);
+ bp = NULL;
+ }
+ return (bp);
+ }
+
+ /*
+ * Handle compressed packet. 1) Read upto MAX_VJHEADER bytes into work
+ * space. 2) Try to uncompress it. 3) Compute amount of necesary 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) {
+ mbuf_Free(bp);
+ return NULL;
+ }
+ len -= olen;
+ len += rlen;
+ nbp = mbuf_Alloc(len, MB_VJCOMP);
+ memcpy(MBUF_CTOP(nbp), bufp, len);
+ nbp->next = bp;
+ return (nbp);
+}
+
+struct mbuf *
+vj_Input(struct ipcp *ipcp, struct mbuf *bp, int proto)
+{
+ u_char type;
+
+ log_Printf(LogDEBUG, "vj_Input: proto %02x\n", proto);
+ log_DumpBp(LogDEBUG, "Raw packet info:", bp);
+
+ switch (proto) {
+ case PROTO_VJCOMP:
+ type = TYPE_COMPRESSED_TCP;
+ break;
+ case PROTO_VJUNCOMP:
+ type = TYPE_UNCOMPRESSED_TCP;
+ break;
+ default:
+ log_Printf(LogWARN, "vj_Input...???\n");
+ return (bp);
+ }
+ bp = VjUncompressTcp(ipcp, bp, type);
+ return (bp);
+}
+
+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 %s slot compression",
+ (int)((val>>8)&15)+1, val & 1 ? "with" : "without");
+ else
+ strcpy(asc, "VJ disabled");
+ return asc;
+}
diff --git a/usr.sbin/ppp/vjcomp.h b/usr.sbin/ppp/vjcomp.h
new file mode 100644
index 0000000..b33436e
--- /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.
+ *
+ * $Id: vjcomp.h,v 1.5.4.6 1998/05/01 19:26:12 brian Exp $
+ */
+
+struct mbuf;
+struct link;
+struct ipcp;
+struct bundle;
+
+extern void vj_SendFrame(struct link *, struct mbuf *, struct bundle *);
+extern struct mbuf *vj_Input(struct ipcp *, struct mbuf *, int);
+extern const char *vj2asc(u_int32_t);
diff --git a/usr.sbin/pppctl/Makefile b/usr.sbin/pppctl/Makefile
new file mode 100644
index 0000000..e278cec
--- /dev/null
+++ b/usr.sbin/pppctl/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.3 1998/08/30 23:53:00 brian Exp $
+
+PROG= pppctl
+SRCS= pppctl.c
+CFLAGS+=-Wall
+LDADD+= -ledit -ltermcap
+DPADD+= ${LIBEDIT} ${LIBTERMCAP}
+MAN8= pppctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppctl/pppctl.8 b/usr.sbin/pppctl/pppctl.8
new file mode 100644
index 0000000..4ededd5
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.8
@@ -0,0 +1,187 @@
+.\" $Id: pppctl.8,v 1.10 1998/05/13 07:57:44 phk Exp $
+.Dd 26 June 1997
+.Os FreeBSD
+.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
+.Ar [host:]Port | LocalSocket
+.Op command[;command]...
+.Sh DESCRIPTION
+This program provides command line control of the
+.Xr ppp 8
+daemon. Its primary use is to facilitate simple scripts that
+control a running daemon.
+.Pp
+.Nm Pppctl
+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: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(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
+.Pp
+.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
+.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
+.Pp
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr editline 3 ,
+.Xr editrc 5 ,
+.Xr services 5 ,
+.Xr ppp 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD 2.2.5.
diff --git a/usr.sbin/pppctl/pppctl.c b/usr.sbin/pppctl/pppctl.c
new file mode 100644
index 0000000..83ee670
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.c
@@ -0,0 +1,425 @@
+/*-
+ * 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.
+ *
+ * $Id: pppctl.c,v 1.16 1998/03/22 00:43:04 brian Exp $
+ */
+
+#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 <errno.h>
+#include <histedit.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
+static char Buffer[LINELEN], Command[LINELEN];
+
+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");
+ return 1;
+}
+
+static int TimedOut = 0;
+static void
+Timeout(int Sig)
+{
+ TimedOut = 1;
+}
+
+#define REC_PASSWD (1)
+#define REC_SHOW (2)
+#define REC_VERBOSE (4)
+
+static char *passwd;
+static char *prompt;
+
+static char *
+GetPrompt(EditLine *e)
+{
+ if (prompt == NULL)
+ prompt = "";
+ return prompt;
+}
+
+static int
+Receive(int fd, int display)
+{
+ int Result;
+ int len;
+ char *last;
+
+ 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(1, 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(1, 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;
+ }
+ 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(1, Buffer, flush);
+ strcpy(Buffer, Buffer + flush);
+ len -= flush;
+ }
+ }
+
+ return Result;
+}
+
+static int data = -1;
+static jmp_buf pppdead;
+
+static void
+check_fd(int sig)
+{
+ if (data != -1) {
+ struct timeval t;
+ fd_set f;
+ static char buf[LINELEN];
+ int len;
+
+ 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(1, buf, len);
+ else
+ longjmp(pppdead, -1);
+ }
+ }
+}
+
+static const char *
+smartgets(EditLine *e, int *count, int fd)
+{
+ const char *result;
+
+ data = fd;
+ signal(SIGALRM, check_fd);
+ ualarm(500000, 500000);
+ result = setjmp(pppdead) ? NULL : el_gets(e, count);
+ ualarm(0,0);
+ signal(SIGALRM, SIG_DFL);
+ data = -1;
+
+ return result;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct servent *s;
+ struct hostent *h;
+ struct sockaddr *sock;
+ struct sockaddr_in ifsin;
+ struct sockaddr_un ifsun;
+ int socksz, arg, fd, len, verbose, err;
+ unsigned TimeoutVal;
+ char *DoneWord = "x", *next, *start;
+ struct sigaction act, oact;
+
+ verbose = 0;
+ TimeoutVal = 2;
+
+ 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':
+ passwd = (start[1] ? start + 1 : argv[++arg]);
+ start = DoneWord;
+ break;
+
+ default:
+ return Usage();
+ }
+ }
+ else
+ break;
+
+
+ if (argc < arg + 1)
+ return Usage();
+
+ if (*argv[arg] == '/') {
+ sock = (struct sockaddr *)&ifsun;
+ socksz = sizeof ifsun;
+
+ memset(&ifsun, '\0', sizeof ifsun);
+ ifsun.sun_len = strlen(argv[arg]);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ fprintf(stderr, "%s: Path too long\n", 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) {
+ fprintf(stderr, "Cannot create local domain socket\n");
+ return 2;
+ }
+ } else {
+ char *port, *host, *colon;
+ int hlen;
+
+ colon = strchr(argv[arg], ':');
+ if (colon) {
+ port = colon + 1;
+ *colon = '\0';
+ host = argv[arg];
+ } else {
+ port = argv[arg];
+ host = "127.0.0.1";
+ }
+ sock = (struct sockaddr *)&ifsin;
+ socksz = sizeof ifsin;
+ hlen = strlen(host);
+
+ memset(&ifsin, '\0', sizeof ifsin);
+ if (strspn(host, "0123456789.") == hlen) {
+ if (!inet_aton(host, &ifsin.sin_addr)) {
+ fprintf(stderr, "Cannot translate %s\n", host);
+ return 1;
+ }
+ } else if ((h = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Cannot resolve %s\n", host);
+ return 1;
+ }
+ else
+ ifsin.sin_addr.s_addr = *(u_long *)h->h_addr_list[0];
+
+ if (colon)
+ *colon = ':';
+
+ if (strspn(port, "0123456789") == strlen(port))
+ ifsin.sin_port = htons(atoi(port));
+ else if (s = getservbyname(port, "tcp"), !s) {
+ fprintf(stderr, "%s isn't a valid port or service!\n", port);
+ return Usage();
+ }
+ else
+ ifsin.sin_port = s->s_port;
+
+ ifsin.sin_len = sizeof(ifsin);
+ ifsin.sin_family = AF_INET;
+
+ if (fd = socket(AF_INET, SOCK_STREAM, 0), fd < 0) {
+ fprintf(stderr, "Cannot create internet socket\n");
+ return 2;
+ }
+ }
+
+ 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, sock, socksz) < 0) {
+ err = errno;
+ if (TimeoutVal) {
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ }
+ if (TimedOut) {
+ fputs("Timeout: ", stderr);
+ err = 0;
+ }
+ fprintf(stderr, "Cannot connect to socket %s", argv[arg]);
+ if (err)
+ fprintf(stderr, ": %s", strerror(err));
+ fputc('\n', stderr);
+ close(fd);
+ return 3;
+ }
+
+ 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:
+ if (len == 0) {
+ EditLine *edit;
+ History *hist;
+ const char *l, *env;
+ int size;
+
+ hist = history_init();
+ if ((env = getenv("EL_SIZE"))) {
+ size = atoi(env);
+ if (size < 0)
+ size = 20;
+ } else
+ size = 20;
+ history(hist, H_EVENT, size);
+
+ edit = el_init("pppctl", stdin, stdout);
+ el_source(edit, NULL);
+ el_set(edit, EL_PROMPT, GetPrompt);
+ if ((env = getenv("EL_EDITOR"))) {
+ if (!strcmp(env, "vi"))
+ el_set(edit, EL_EDITOR, "vi");
+ else if (!strcmp(env, "emacs"))
+ el_set(edit, EL_EDITOR, "emacs");
+ }
+ el_set(edit, EL_SIGNAL, 1);
+ el_set(edit, EL_HIST, history, (const char *)hist);
+ while ((l = smartgets(edit, &len, fd))) {
+ if (len > 1)
+ history(hist, H_ENTER, l);
+ write(fd, l, len);
+ if (Receive(fd, REC_SHOW) != 0)
+ break;
+ }
+ fprintf(stderr, "Connection closed\n");
+ el_end(edit);
+ history_end(hist);
+ } 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(1, 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)
+ puts("");
+ }
+ break;
+
+ default:
+ fprintf(stderr, "ppp is not responding\n");
+ break;
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/usr.sbin/pppd/Makefile b/usr.sbin/pppd/Makefile
new file mode 100644
index 0000000..323cc0d
--- /dev/null
+++ b/usr.sbin/pppd/Makefile
@@ -0,0 +1,36 @@
+# $Id: Makefile,v 1.11 1998/09/19 22:42:11 obrien Exp $
+
+CFLAGS+= -DHAVE_PATHS_H
+
+PROG= pppd
+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
+MAN8= pppd.8
+BINMODE=4555
+
+# as per handbook policies section
+MAINTAINER= peter@freebsd.org
+
+LDADD= -lcrypt -lutil -lmd
+DPADD= ${LIBCRYPT} ${LIBUTIL} ${LIBMD}
+
+# 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
+LDADD+= -lpcap
+DPADD+= ${LIBPCAP}
+
+# MS-CHAP support. Requires the DES library.
+#CFLAGS+=-DCHAPMS
+#SRCS+= chap_ms.c
+#LDADD+= -ldes
+#DPADD+= ${LIBDES}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppd/RELNOTES b/usr.sbin/pppd/RELNOTES
new file mode 100644
index 0000000..12d9c7f
--- /dev/null
+++ b/usr.sbin/pppd/RELNOTES
@@ -0,0 +1,724 @@
+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 a 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..880a65d
--- /dev/null
+++ b/usr.sbin/pppd/auth.c
@@ -0,0 +1,1630 @@
+/*
+ * 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[] = "$Id: auth.c,v 1.22 1998/03/22 05:32:43 peter Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.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
+ || (secret[0] != 0 && (cryptpap || strcmp(passwd, secret) != 0)
+ && strcmp(crypt(passwd, secret), secret) != 0)) {
+ syslog(LOG_WARNING, "PAP authentication failure for %s", user);
+ ret = UPAP_AUTHNAK;
+ }
+ 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, "/dev/", 5) == 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));
+ (void)time(&ll.ll_time);
+ (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));
+ (void)time(&utmp.ut_time);
+ (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, "/dev/", 5) == 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..db939ba
--- /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[] = "$Id$";
+#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..2f4f05f
--- /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[] = "$Id: ccp.c,v 1.8 1998/03/22 06:57:18 peter Exp $";
+#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..c196edf
--- /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.
+ *
+ * $Id: ccp.h,v 1.6 1998/03/22 06:57:19 peter Exp $
+ */
+
+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..c7ed461
--- /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[] = "$Id: chap.c,v 1.8 1997/08/19 17:52:34 peter Exp $";
+#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..6e2cc45
--- /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.
+ *
+ * $Id$
+ */
+
+#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..3b2ed89
--- /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[] = "$Id: chap_ms.c,v 1.5 1998/06/20 18:02:09 peter Exp $";
+#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 <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..2fa7c93
--- /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.
+ *
+ * $Id: chap_ms.h,v 1.3 1997/08/19 17:52:35 peter Exp $
+ */
+
+#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..af1a8bb
--- /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[] = "$Id: demand.c,v 1.3 1997/08/19 17:52:36 peter Exp $";
+#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..624799f
--- /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[] = "$Id$";
+#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;
+ /* fall through */
+ 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 an 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 */
+ /* fall through */
+ 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 */
+ /* fall through */
+ 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..f289429
--- /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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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..2ce87a5
--- /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[] = "$Id: ipcp.c,v 1.10 1997/08/22 12:03:54 peter Exp $";
+#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..165a5a1
--- /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.
+ *
+ * $Id: ipcp.h,v 1.8 1997/08/19 17:52:38 peter Exp $
+ */
+
+/*
+ * 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..ea0e2c0
--- /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[] = "$Id: ipxcp.c,v 1.3 1997/08/19 17:52:39 peter Exp $";
+#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 a 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..139a726
--- /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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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..f4920ba
--- /dev/null
+++ b/usr.sbin/pppd/lcp.c
@@ -0,0 +1,1857 @@
+/*
+ * 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[] = "$Id: lcp.c,v 1.7 1997/08/19 17:52:40 peter Exp $";
+#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 an 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);
+
+ /*
+ * 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..42260f3
--- /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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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..7ff35ad
--- /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[] = "$Id: magic.c,v 1.6 1997/08/19 17:52:42 peter Exp $";
+#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..1344626
--- /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.
+ *
+ * $Id$
+ */
+
+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..313b2ab
--- /dev/null
+++ b/usr.sbin/pppd/main.c
@@ -0,0 +1,1716 @@
+/*
+ * 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[] = "$Id: main.c,v 1.16 1998/03/22 05:33:00 peter Exp $";
+#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);
+ 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)
+ chmod(devnam, 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 *);
+#ifndef __powerpc__
+ 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..8d24c35
--- /dev/null
+++ b/usr.sbin/pppd/options.c
@@ -0,0 +1,2569 @@
+/*
+ * 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[] = "$Id: options.c,v 1.18 1998/03/22 06:57:20 peter Exp $";
+#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 <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] = "/dev/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 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, "/dev/", 5) == 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("/dev/", cp, 5) != 0) {
+ strcpy(dev, "/dev/");
+ strncat(dev, cp, MAXPATHLEN - 5);
+ 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)) == -1 || (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 compatability */
+ 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..0525280
--- /dev/null
+++ b/usr.sbin/pppd/patchlevel.h
@@ -0,0 +1,6 @@
+/* $Id: patchlevel.h,v 1.8 1998/03/22 05:33:05 peter Exp $ */
+#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..896fed6
--- /dev/null
+++ b/usr.sbin/pppd/pathnames.h
@@ -0,0 +1,32 @@
+/*
+ * define path names
+ *
+ * $Id$
+ */
+
+#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 "/etc/ppp/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..e13f01c
--- /dev/null
+++ b/usr.sbin/pppd/pppd.8
@@ -0,0 +1,1171 @@
+.\" manual page [] for pppd 2.3
+.\" $Id: pppd.8,v 1.17 1997/10/18 01:29:18 peter Exp $
+.\" 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 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 comprssion.
+.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 a 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.
+.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..ce3a757
--- /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.
+ *
+ * $Id: pppd.h,v 1.11 1997/10/10 09:28:38 peter Exp $
+ */
+
+/*
+ * 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..41ff444
--- /dev/null
+++ b/usr.sbin/pppd/sys-bsd.c
@@ -0,0 +1,1581 @@
+/*
+ * 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[] = "$Id: sys-bsd.c,v 1.14 1998/06/20 18:02:16 peter Exp $";
+#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>
+#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 "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 any ppp interfaces
+ * (in fact we check whether we can do an ioctl on ppp0).
+ */
+int
+ppp_available()
+{
+ int s, ok;
+ struct ifreq ifr;
+ extern char *no_ppp_msg;
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ return 1; /* can't tell */
+
+ strncpy(ifr.ifr_name, "ppp0", sizeof (ifr.ifr_name));
+ ok = ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) >= 0;
+ close(s);
+
+ no_ppp_msg = "\
+This system lacks kernel support for PPP. To include PPP support\n\
+in the kernel, please follow the steps detailed in the README.bsd\n\
+file in the ppp-2.2 distribution.\n";
+ return ok;
+}
+
+/*
+ * 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;
+
+ 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;
+
+ 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.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+ u_int32_t ipaddr;
+ struct sockaddr_dl *hwaddr;
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ u_int32_t ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+ return 0;
+ }
+
+ /*
+ * 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 = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+ 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(sockfd, 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(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ continue;
+
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+ syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifr;
+ for (ifr = ifc.ifc_req; ifr < ifend; ) {
+ if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
+ && ifr->ifr_addr.sa_family == AF_LINK) {
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ BCOPY(dla, hwaddr, dla->sdl_len);
+ return 1;
+ }
+ ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ 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 ifreq *ifr, *ifend, ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ 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.
+ */
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ syslog(LOG_WARNING, "ioctl(SIOCGIFCONF): %m");
+ return mask;
+ }
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) {
+ /*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = ((struct sockaddr_in *) &ifr->ifr_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.
+ */
+ strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+ != IFF_UP)
+ continue;
+ /*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
+ }
+
+ 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..469c5cb
--- /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[] = "$Id$";
+#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..510efa3
--- /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.
+ *
+ * $Id$
+ */
+
+/*
+ * 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..899c6db
--- /dev/null
+++ b/usr.sbin/pppstats/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:12:11 peter Exp $
+
+PROG= pppstats
+SRCS= pppstats.c
+MAN8= pppstats.8
+
+#as per policies in handbook
+MAINTAINER= peter@freebsd.org
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppstats/pppstats.8 b/usr.sbin/pppstats/pppstats.8
new file mode 100644
index 0000000..3bd4dac
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.8
@@ -0,0 +1,217 @@
+.\" @(#) $Id: pppstats.8,v 1.7 1997/08/22 15:39:04 peter Exp $
+.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..b654b10
--- /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[] = "$Id: pppstats.c,v 1.11 1997/09/10 08:43:17 peter Exp $";
+#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..9c13bc0
--- /dev/null
+++ b/usr.sbin/praliases/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.2 (Berkeley) 9/21/96
+
+.PATH: ${.CURDIR}/../../contrib/sendmail/praliases
+
+PROG= praliases
+MAN8= praliases.8
+CFLAGS+=-I${.CURDIR}/../../contrib/sendmail/src -DNEWDB
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/procctl/Makefile b/usr.sbin/procctl/Makefile
new file mode 100644
index 0000000..2ccbccb
--- /dev/null
+++ b/usr.sbin/procctl/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.2 1997/12/07 02:29:28 peter Exp $
+
+PROG= procctl
+MAN8= procctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/procctl/procctl.8 b/usr.sbin/procctl/procctl.8
new file mode 100644
index 0000000..7179ceb
--- /dev/null
+++ b/usr.sbin/procctl/procctl.8
@@ -0,0 +1,34 @@
+.\" $Id: procctl.8,v 1.2 1997/12/07 02:30:43 peter Exp $
+.Dd Nov 23, 1997
+.Dt PROCCTL 1
+.Os FreeBSD
+.Sh NAME
+.Nm procctl
+.Nd clear procfs event flags
+.Sh SYNOPSIS
+.Nm procctl
+.Ar command
+.Op Ar ...
+.Sh DESCRIPTION
+.Nm Procctl
+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 options are a list of process ID's;
+.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
+command was written by
+.An Sean Eric Fagan
+for
+.Bx Free .
diff --git a/usr.sbin/procctl/procctl.c b/usr.sbin/procctl/procctl.c
new file mode 100644
index 0000000..ade673d
--- /dev/null
+++ b/usr.sbin/procctl/procctl.c
@@ -0,0 +1,81 @@
+/*
+ * Copryight 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * 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];
+
+ sprintf(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..5954d13
--- /dev/null
+++ b/usr.sbin/pstat/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pstat
+CFLAGS+=-I${.CURDIR}/../../sys
+BINGRP= kmem
+BINMODE=2555
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+MAN8= pstat.8
+LINKS= ${BINDIR}/pstat ${BINDIR}/swapinfo
+MLINKS= pstat.8 swapinfo.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
new file mode 100644
index 0000000..d4e5dc6
--- /dev/null
+++ b/usr.sbin/pstat/pstat.8
@@ -0,0 +1,390 @@
+.\" Copyright (c) 1980, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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
+.\" $Id: pstat.8,v 1.16 1998/01/17 16:58:55 bde Exp $
+.\"
+.Dd May 13, 1994
+.Dt PSTAT 8
+.Os BSD 4
+.Sh NAME
+.Nm pstat ,
+.Nm swapinfo
+.Nd display system data structures
+.Sh SYNOPSIS
+.Nm pstat
+.Op Fl Tfiknstv
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Pp
+.Nm swapinfo
+.Op Fl k
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Sh DESCRIPTION
+.Nm Pstat
+displays open file entry, swap space utilization,
+terminal state, and vnode data structures.
+If
+.Ar corefile
+is given, the information is sought there, otherwise
+in
+.Pa /dev/mem .
+The required namelist is taken from
+.Pa /kernel
+unless
+.Ar system
+is specified.
+.Pp
+If invoked as
+.Nm swapinfo
+the
+.Fl s
+option is implied, and only the
+.Fl k
+option is legal.
+.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 BLOCKSIZE
+environment variable.
+.It Fl T
+Print the number of used and free slots in the several system tables
+and 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:
+.Bl -tag -width indent
+.It R
+open for reading
+.It W
+open for writing
+.It A
+open for appending
+.It S
+shared lock present
+.It X
+exclusive lock present
+.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.
+.Pp
+If you supply the option again, as in
+.Fl ss ,
+the system will display a breakdown of the swap bitmap/radix-tree.
+.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:
+.Bl -tag -width indent
+.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 v
+Print the active vnodes. Each group of vnodes corresponding
+to a particular filesystem is preceded by a two line header. The
+first line consists of the following:
+.Pp
+.Df I
+.No *** MOUNT Em fstype from
+on
+.Em on fsflags
+.De
+.Pp
+where
+.Em fstype
+is one of
+.Em ufs , nfs , mfs , or pc ;
+.Em from
+is the filesystem is mounted from;
+.Em on
+is the directory
+the filesystem is mounted on; and
+.Em fsflags
+is a list
+of optional flags applied to the mount (see
+.Xr mount 8 ) .
+.The second line is a header for the individual fields ,
+the first part of which are fixed, and the second part are filesystem
+type specific. The headers common to all vnodes are:
+.Bl -tag -width indent
+.It ADDR
+Location of this vnode.
+.It TYP
+File type.
+.It VFLAG
+.Pp
+A list of letters representing vnode flags:
+.Bl -tag -width indent
+.It R
+\- VROOT
+.It T
+\- VTEXT
+.It S
+\- VSYSTEM
+.It t
+\- VISTTY
+.It L
+\- VXLOCK
+.It W
+\- VXWANT
+.It B
+\- VBWAIT
+.It A
+\- VALIASED
+.It V
+\- VOBJBUF
+.It a
+\- VAGE
+.It l
+\- VOLOCK
+.It w
+\- VOWANT
+.El
+.Pp
+.It USE
+The number of references to this vnode.
+.It HOLD
+The number of I/O buffers held by this vnode.
+.It FILEID
+The vnode fileid.
+In the case of
+.Em ufs
+this is the inode number.
+.It IFLAG
+Miscellaneous filesystem specific state variables encoded thus:
+.Bl -tag -width indent
+.It "For ufs:"
+.Pp
+.Bl -tag -width indent
+.It L
+locked
+.It U
+update time
+.Pq Xr fs 5
+must be corrected
+.It A
+access time must be corrected
+.It W
+wanted by another process (L flag is on)
+.It C
+changed time must be corrected
+.It S
+shared lock applied
+.It E
+exclusive lock applied
+.It Z
+someone waiting for a lock
+.It M
+contains modifications
+.It R
+has a rename in progress
+.El
+.It "For nfs:"
+.Bl -tag -width indent
+.It W
+waiting for I/O buffer flush to complete
+.It P
+I/O buffers being flushed
+.It M
+locally modified data exists
+.It E
+an earlier write failed
+.It X
+non-cacheable lease (nqnfs)
+.It O
+write lease (nqnfs)
+.It G
+lease was evicted (nqnfs)
+.El
+.El
+.It SIZ/RDEV
+Number of bytes in an ordinary file, or
+major and minor device of special file.
+.El
+.It Fl i
+Same as
+.Fl v ,
+present for backwards-compatibility.
+.El
+.Sh FILES
+.Bl -tag -width /dev/memxxx -compact
+.It Pa /kernel
+namelist
+.It Pa /dev/mem
+default source of tables
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr stat 2 ,
+.Xr fs 5 ,
+.Xr iostat 8 ,
+.Xr vmstat 8
+.Rs
+.Rt Tn UNIX Rt Implementation ,
+.Ra K. Thompson
+.Re
+.Sh BUGS
+Does not understand NFS swap servers.
+.Sh HISTORY
+The
+.Nm pstat
+command 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..a01f29f
--- /dev/null
+++ b/usr.sbin/pstat/pstat.c
@@ -0,0 +1,1096 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pstat.c 8.16 (Berkeley) 5/9/95";
+#endif
+static const char rcsid[] =
+ "$Id: pstat.c,v 1.39 1999/01/22 10:36:48 dillon Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/ucred.h>
+#define KERNEL
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <sys/namei.h>
+#include <miscfs/union/union.h>
+#undef KERNEL
+#include <sys/stat.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */
+#include <sys/tty.h>
+#include <sys/conf.h>
+#include <sys/rlist.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>
+
+struct nlist nl[] = {
+#define NLMANDATORYBEG 0
+#define V_MOUNTLIST 0
+ { "_mountlist" }, /* address of head of mount list. */
+#define V_NUMV 1
+ { "_numvnodes" },
+#define FNL_NFILE 2
+ {"_nfiles"},
+#define FNL_MAXFILE 3
+ {"_maxfiles"},
+#define NLMANDATORYEND FNL_MAXFILE /* names up to here are mandatory */
+#define SCONS NLMANDATORYEND + 1
+ { "_cons" },
+#define SPTY NLMANDATORYEND + 2
+ { "_pt_tty" },
+#define SNPTY NLMANDATORYEND + 3
+ { "_npty" },
+
+#ifdef hp300
+#define SDCA (SNPTY+1)
+ { "_dca_tty" },
+#define SNDCA (SNPTY+2)
+ { "_ndca" },
+#define SDCM (SNPTY+3)
+ { "_dcm_tty" },
+#define SNDCM (SNPTY+4)
+ { "_ndcm" },
+#define SDCL (SNPTY+5)
+ { "_dcl_tty" },
+#define SNDCL (SNPTY+6)
+ { "_ndcl" },
+#define SITE (SNPTY+7)
+ { "_ite_tty" },
+#define SNITE (SNPTY+8)
+ { "_nite" },
+#endif
+
+#ifdef mips
+#define SDC (SNPTY+1)
+ { "_dc_tty" },
+#define SNDC (SNPTY+2)
+ { "_dc_cnt" },
+#endif
+
+#ifdef __FreeBSD__
+#define SCCONS (SNPTY+1)
+ { "_sccons" },
+#define NSCCONS (SNPTY+2)
+ { "_nsccons" },
+#define SIO (SNPTY+3)
+ { "_sio_tty" },
+#define NSIO (SNPTY+4)
+ { "_nsio_tty" },
+#define RC (SNPTY+5)
+ { "_rc_tty" },
+#define NRC (SNPTY+6)
+ { "_nrc_tty" },
+#define CY (SNPTY+7)
+ { "_cy_tty" },
+#define NCY (SNPTY+8)
+ { "_ncy_tty" },
+#define SI (SNPTY+9)
+ { "_si_tty" },
+#define NSI (SNPTY+10)
+ { "_si_Nports" },
+#endif
+ { "" }
+};
+
+int usenumflag;
+int totalflag;
+int swapflag;
+char *nlistf = NULL;
+char *memf = NULL;
+kvm_t *kd;
+
+char *usagestr;
+
+struct {
+ int m_flag;
+ const char *m_name;
+} mnt_flags[] = {
+ { MNT_RDONLY, "rdonly" },
+ { MNT_SYNCHRONOUS, "sync" },
+ { MNT_NOEXEC, "noexec" },
+ { MNT_NOSUID, "nosuid" },
+ { MNT_NODEV, "nodev" },
+ { MNT_UNION, "union" },
+ { MNT_ASYNC, "async" },
+ { MNT_NOATIME, "noatime" },
+ { MNT_EXRDONLY, "exrdonly" },
+ { MNT_EXPORTED, "exported" },
+ { MNT_DEFEXPORTED, "defexported" },
+ { MNT_EXPORTANON, "exportanon" },
+ { MNT_EXKERB, "exkerb" },
+ { MNT_LOCAL, "local" },
+ { MNT_QUOTA, "quota" },
+ { MNT_ROOTFS, "rootfs" },
+ { MNT_USER, "user" },
+ { MNT_UPDATE, "update" },
+ { MNT_DELEXPORT },
+ { MNT_UPDATE, "update" },
+ { MNT_DELEXPORT, "delexport" },
+ { MNT_RELOAD, "reload" },
+ { MNT_FORCE, "force" },
+#if 0
+ { MNT_UNMOUNT, "unmount" },
+ { MNT_MWAIT, "mwait" },
+ { MNT_WANTRDWR, "wantrdwr" },
+#endif
+ { 0 }
+};
+
+
+#define SVAR(var) __STRING(var) /* to force expansion */
+#define KGET(idx, var) \
+ KGET1(idx, &var, sizeof(var), SVAR(var))
+#define KGET1(idx, p, s, msg) \
+ KGET2(nl[idx].n_value, p, s, msg)
+#define KGET2(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd))
+#define KGETN(idx, var) \
+ KGET1N(idx, &var, sizeof(var), SVAR(var))
+#define KGET1N(idx, p, s, msg) \
+ KGET2N(nl[idx].n_value, p, s, msg)
+#define KGET2N(addr, p, s, msg) \
+ ((kvm_read(kd, (u_long)(addr), p, s) == s) ? 1 : 0)
+#define KGETRET(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return (0); \
+ }
+
+void filemode __P((void));
+int getfiles __P((char **, int *));
+struct mount *
+ getmnt __P((struct mount *));
+struct e_vnode *
+ kinfo_vnodes __P((int *));
+struct e_vnode *
+ loadvnodes __P((int *));
+void mount_print __P((struct mount *));
+void nfs_header __P((void));
+int nfs_print __P((struct vnode *));
+void swapmode __P((void));
+void ttymode __P((void));
+void ttyprt __P((struct tty *, int));
+void ttytype __P((struct tty *, char *, int, int, int));
+void ufs_header __P((void));
+int ufs_print __P((struct vnode *));
+void union_header __P((void));
+int union_print __P((struct vnode *));
+static void usage __P((void));
+void vnode_header __P((void));
+void vnode_print __P((struct vnode *, struct vnode *));
+void vnodemode __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, i, quit, ret;
+ int fileflag, ttyflag, vnodeflag;
+ char buf[_POSIX2_LINE_MAX],*opts;
+
+ fileflag = swapflag = ttyflag = vnodeflag = 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:fiknstv";
+ usagestr = "pstat [-Tfknstv] [-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;
+ case 'v':
+ case 'i': /* Backward compatibility. */
+ fprintf(stderr, "vnode mode not supported\n");
+ exit(1);
+#if 0
+ vnodeflag = 1;
+ break;
+#endif
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ (void)setgid(getgid());
+
+ if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == 0)
+ errx(1, "kvm_openfiles: %s", buf);
+ if ((ret = kvm_nlist(kd, nl)) != 0) {
+ if (ret == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ for (i = NLMANDATORYBEG, quit = 0; i <= NLMANDATORYEND; i++)
+ if (!nl[i].n_value) {
+ quit = 1;
+ warnx("undefined symbol: %s", nl[i].n_name);
+ }
+ if (quit)
+ exit(1);
+ }
+ if (!(fileflag | vnodeflag | ttyflag | swapflag | totalflag))
+ usage();
+ if (fileflag || totalflag)
+ filemode();
+ if (vnodeflag)
+ vnodemode();
+ if (ttyflag)
+ ttymode();
+ if (swapflag || totalflag)
+ swapmode();
+ exit (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s\n", usagestr);
+ exit (1);
+}
+
+struct e_vnode {
+ struct vnode *avnode;
+ struct vnode vnode;
+};
+
+void
+vnodemode()
+{
+ struct e_vnode *e_vnodebase, *endvnode, *evp;
+ struct vnode *vp;
+ struct mount *maddr, *mp;
+ int numvnodes;
+
+ e_vnodebase = loadvnodes(&numvnodes);
+ if (totalflag) {
+ (void)printf("%7d vnodes\n", numvnodes);
+ return;
+ }
+ endvnode = e_vnodebase + numvnodes;
+ (void)printf("%d active vnodes\n", numvnodes);
+
+
+#define ST mp->mnt_stat
+ maddr = NULL;
+ for (evp = e_vnodebase; evp < endvnode; evp++) {
+ vp = &evp->vnode;
+ if (vp->v_mount != maddr) {
+ /*
+ * New filesystem
+ */
+ if ((mp = getmnt(vp->v_mount)) == NULL)
+ continue;
+ maddr = vp->v_mount;
+ mount_print(mp);
+ vnode_header();
+ if (!strcmp(ST.f_fstypename, "ufs") ||
+ !strcmp(ST.f_fstypename, "mfs"))
+ ufs_header();
+ else if (!strcmp(ST.f_fstypename, "nfs"))
+ nfs_header();
+ else if (!strcmp(ST.f_fstypename, "union"))
+ union_header();
+ (void)printf("\n");
+ }
+ vnode_print(evp->avnode, vp);
+ if (!strcmp(ST.f_fstypename, "ufs") ||
+ !strcmp(ST.f_fstypename, "mfs"))
+ ufs_print(vp);
+ else if (!strcmp(ST.f_fstypename, "nfs"))
+ nfs_print(vp);
+ else if (!strcmp(ST.f_fstypename, "union"))
+ union_print(vp);
+ (void)printf("\n");
+ }
+ free(e_vnodebase);
+}
+
+void
+vnode_header()
+{
+ (void)printf("ADDR TYP VFLAG USE HOLD");
+}
+
+void
+vnode_print(avnode, vp)
+ struct vnode *avnode;
+ struct vnode *vp;
+{
+ char *type, flags[16];
+ char *fp = flags;
+ int flag;
+
+ /*
+ * set type
+ */
+ switch (vp->v_type) {
+ case VNON:
+ type = "non"; break;
+ case VREG:
+ type = "reg"; break;
+ case VDIR:
+ type = "dir"; break;
+ case VBLK:
+ type = "blk"; break;
+ case VCHR:
+ type = "chr"; break;
+ case VLNK:
+ type = "lnk"; break;
+ case VSOCK:
+ type = "soc"; break;
+ case VFIFO:
+ type = "fif"; break;
+ case VBAD:
+ type = "bad"; break;
+ default:
+ type = "unk"; break;
+ }
+ /*
+ * gather flags
+ */
+ flag = vp->v_flag;
+ if (flag & VROOT)
+ *fp++ = 'R';
+ if (flag & VTEXT)
+ *fp++ = 'T';
+ if (flag & VSYSTEM)
+ *fp++ = 'S';
+ if (flag & VISTTY)
+ *fp++ = 't';
+ if (flag & VXLOCK)
+ *fp++ = 'L';
+ if (flag & VXWANT)
+ *fp++ = 'W';
+ if (flag & VBWAIT)
+ *fp++ = 'B';
+ if (flag & VALIASED)
+ *fp++ = 'A';
+ if (flag & VOBJBUF)
+ *fp++ = 'V';
+ if (flag & VAGE)
+ *fp++ = 'a';
+ if (flag & VOLOCK)
+ *fp++ = 'l';
+ if (flag & VOWANT)
+ *fp++ = 'w';
+ if (flag == 0)
+ *fp++ = '-';
+ *fp = '\0';
+ (void)printf("%8lx %s %5s %4d %4d",
+ (u_long)(void *)avnode, type, flags, vp->v_usecount, vp->v_holdcnt);
+}
+
+void
+ufs_header()
+{
+ (void)printf(" FILEID IFLAG RDEV|SZ");
+}
+
+int
+ufs_print(vp)
+ struct vnode *vp;
+{
+ int flag;
+ struct inode inode, *ip = &inode;
+ char flagbuf[16], *flags = flagbuf;
+ char *name;
+ mode_t type;
+
+ KGETRET(VTOI(vp), &inode, sizeof(struct inode), "vnode's inode");
+ flag = ip->i_flag;
+ if (flag & IN_RENAME)
+ *flags++ = 'R';
+ if (flag & IN_UPDATE)
+ *flags++ = 'U';
+ if (flag & IN_ACCESS)
+ *flags++ = 'A';
+ if (flag & IN_CHANGE)
+ *flags++ = 'C';
+ if (flag & IN_MODIFIED)
+ *flags++ = 'M';
+ if (flag & IN_SHLOCK)
+ *flags++ = 'S';
+ if (flag & IN_EXLOCK)
+ *flags++ = 'E';
+ if (flag == 0)
+ *flags++ = '-';
+ *flags = '\0';
+
+ (void)printf(" %6d %5s", ip->i_number, flagbuf);
+ type = ip->i_mode & S_IFMT;
+ if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
+ if (usenumflag || ((name = devname(ip->i_rdev, type)) == NULL))
+ (void)printf(" %2d,%-2d",
+ major(ip->i_rdev), minor(ip->i_rdev));
+ else
+ (void)printf(" %7s", name);
+ else
+ (void)printf(" %7qd", ip->i_size);
+ return (0);
+}
+
+void
+nfs_header()
+{
+ (void)printf(" FILEID NFLAG RDEV|SZ");
+}
+
+int
+nfs_print(vp)
+ struct vnode *vp;
+{
+ struct nfsnode nfsnode, *np = &nfsnode;
+ char flagbuf[16], *flags = flagbuf;
+ int flag;
+ char *name;
+ mode_t type;
+
+ KGETRET(VTONFS(vp), &nfsnode, sizeof(nfsnode), "vnode's nfsnode");
+ flag = np->n_flag;
+ if (flag & NFLUSHWANT)
+ *flags++ = 'W';
+ if (flag & NFLUSHINPROG)
+ *flags++ = 'P';
+ if (flag & NMODIFIED)
+ *flags++ = 'M';
+ if (flag & NWRITEERR)
+ *flags++ = 'E';
+ if (flag & NQNFSNONCACHE)
+ *flags++ = 'X';
+ if (flag & NQNFSWRITE)
+ *flags++ = 'O';
+ if (flag & NQNFSEVICTED)
+ *flags++ = 'G';
+ if (flag == 0)
+ *flags++ = '-';
+ *flags = '\0';
+
+#define VT np->n_vattr
+ (void)printf(" %6ld %5s", VT.va_fileid, flagbuf);
+ type = VT.va_mode & S_IFMT;
+ if (S_ISCHR(VT.va_mode) || S_ISBLK(VT.va_mode))
+ if (usenumflag || ((name = devname(VT.va_rdev, type)) == NULL))
+ (void)printf(" %2d,%-2d",
+ major(VT.va_rdev), minor(VT.va_rdev));
+ else
+ (void)printf(" %7s", name);
+ else
+ (void)printf(" %7qd", np->n_size);
+ return (0);
+}
+
+void
+union_header()
+{
+ (void)printf(" UPPER LOWER");
+}
+
+int
+union_print(vp)
+ struct vnode *vp;
+{
+ struct union_node unode, *up = &unode;
+
+ KGETRET(VTOUNION(vp), &unode, sizeof(unode), "vnode's unode");
+
+ (void)printf(" %8lx %8lx", (u_long)(void *)up->un_uppervp,
+ (u_long)(void *)up->un_lowervp);
+ return (0);
+}
+
+/*
+ * Given a pointer to a mount structure in kernel space,
+ * read it in and return a usable pointer to it.
+ */
+struct mount *
+getmnt(maddr)
+ struct mount *maddr;
+{
+ static struct mtab {
+ struct mtab *next;
+ struct mount *maddr;
+ struct mount mount;
+ } *mhead = NULL;
+ struct mtab *mt;
+
+ for (mt = mhead; mt != NULL; mt = mt->next)
+ if (maddr == mt->maddr)
+ return (&mt->mount);
+ if ((mt = malloc(sizeof(struct mtab))) == NULL)
+ errx(1, "malloc");
+ KGETRET(maddr, &mt->mount, sizeof(struct mount), "mount table");
+ mt->maddr = maddr;
+ mt->next = mhead;
+ mhead = mt;
+ return (&mt->mount);
+}
+
+void
+mount_print(mp)
+ struct mount *mp;
+{
+ int flags;
+ const char *type;
+
+#define ST mp->mnt_stat
+ (void)printf("*** MOUNT %s %s on %s", ST.f_fstypename,
+ ST.f_mntfromname, ST.f_mntonname);
+ if ((flags = mp->mnt_flag)) {
+ int i;
+ const char *sep = " (";
+
+ for (i = 0; mnt_flags[i].m_flag; i++) {
+ if (flags & mnt_flags[i].m_flag) {
+ (void)printf("%s%s", sep, mnt_flags[i].m_name);
+ flags &= ~mnt_flags[i].m_flag;
+ sep = ",";
+ }
+ }
+ if (flags)
+ (void)printf("%sunknown_flags:%x", sep, flags);
+ (void)printf(")");
+ }
+ (void)printf("\n");
+#undef ST
+}
+
+struct e_vnode *
+loadvnodes(avnodes)
+ int *avnodes;
+{
+ int mib[2];
+ size_t copysize;
+ struct e_vnode *vnodebase;
+
+ if (memf != NULL) {
+ /*
+ * do it by hand
+ */
+ return (kinfo_vnodes(avnodes));
+ }
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VNODE;
+ if (sysctl(mib, 2, NULL, &copysize, NULL, 0) == -1)
+ err(1, "sysctl: KERN_VNODE");
+ if ((vnodebase = malloc(copysize)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 2, vnodebase, &copysize, NULL, 0) == -1)
+ err(1, "sysctl: KERN_VNODE");
+ if (copysize % sizeof(struct e_vnode))
+ errx(1, "vnode size mismatch");
+ *avnodes = copysize / sizeof(struct e_vnode);
+
+ return (vnodebase);
+}
+
+/*
+ * simulate what a running kernel does in in kinfo_vnode
+ */
+struct e_vnode *
+kinfo_vnodes(avnodes)
+ int *avnodes;
+{
+ struct mntlist mountlist;
+ struct mount *mp, mount, *mp_next;
+ struct vnode *vp, vnode, *vp_next;
+ char *vbuf, *evbuf, *bp;
+ int num, numvnodes;
+
+#define VPTRSZ sizeof(struct vnode *)
+#define VNODESZ sizeof(struct vnode)
+
+ KGET(V_NUMV, numvnodes);
+ if ((vbuf = malloc((numvnodes + 20) * (VPTRSZ + VNODESZ))) == NULL)
+ errx(1, "malloc");
+ bp = vbuf;
+ evbuf = vbuf + (numvnodes + 20) * (VPTRSZ + VNODESZ);
+ KGET(V_MOUNTLIST, mountlist);
+ for (num = 0, mp = mountlist.cqh_first; ; mp = mp_next) {
+ KGET2(mp, &mount, sizeof(mount), "mount entry");
+ mp_next = mount.mnt_list.cqe_next;
+ for (vp = mount.mnt_vnodelist.lh_first;
+ vp != NULL; vp = vp_next) {
+ KGET2(vp, &vnode, sizeof(vnode), "vnode");
+ vp_next = vnode.v_mntvnodes.le_next;
+ if ((bp + VPTRSZ + VNODESZ) > evbuf)
+ /* XXX - should realloc */
+ errx(1, "no more room for vnodes");
+ memmove(bp, &vp, VPTRSZ);
+ bp += VPTRSZ;
+ memmove(bp, &vnode, VNODESZ);
+ bp += VNODESZ;
+ num++;
+ }
+ if (mp == mountlist.cqh_last)
+ break;
+ }
+ *avnodes = num;
+ return ((struct e_vnode *)vbuf);
+}
+
+char hdr[] =
+" LINE RAW CAN OUT IHIWT ILOWT OHWT LWT COL STATE SESS PGID DISC\n";
+int ttyspace = 128;
+
+void
+ttymode()
+{
+ struct tty *tty;
+
+ if ((tty = malloc(ttyspace * sizeof(*tty))) == NULL)
+ errx(1, "malloc");
+#if !defined(hp300) && !defined(mips)
+ if (nl[SCONS].n_type != 0) {
+ (void)printf("1 console\n");
+ KGET(SCONS, *tty);
+ (void)printf(hdr);
+ ttyprt(&tty[0], 0);
+ }
+#endif
+#ifdef vax
+ if (nl[SNQD].n_type != 0)
+ qdss();
+ if (nl[SNDZ].n_type != 0)
+ ttytype(tty, "dz", SDZ, SNDZ, 0);
+ if (nl[SNDH].n_type != 0)
+ ttytype(tty, "dh", SDH, SNDH, 0);
+ if (nl[SNDMF].n_type != 0)
+ ttytype(tty, "dmf", SDMF, SNDMF, 0);
+ if (nl[SNDHU].n_type != 0)
+ ttytype(tty, "dhu", SDHU, SNDHU, 0);
+ if (nl[SNDMZ].n_type != 0)
+ ttytype(tty, "dmz", SDMZ, SNDMZ, 0);
+#endif
+#ifdef tahoe
+ if (nl[SNVX].n_type != 0)
+ ttytype(tty, "vx", SVX, SNVX, 0);
+ if (nl[SNMP].n_type != 0)
+ ttytype(tty, "mp", SMP, SNMP, 0);
+#endif
+#ifdef hp300
+ if (nl[SNITE].n_type != 0)
+ ttytype(tty, "ite", SITE, SNITE, 0);
+ if (nl[SNDCA].n_type != 0)
+ ttytype(tty, "dca", SDCA, SNDCA, 0);
+ if (nl[SNDCM].n_type != 0)
+ ttytype(tty, "dcm", SDCM, SNDCM, 0);
+ if (nl[SNDCL].n_type != 0)
+ ttytype(tty, "dcl", SDCL, SNDCL, 0);
+#endif
+#ifdef mips
+ if (nl[SNDC].n_type != 0)
+ ttytype(tty, "dc", SDC, SNDC, 0);
+#endif
+#ifdef __FreeBSD__
+ if (nl[NSCCONS].n_type != 0)
+ ttytype(tty, "vty", SCCONS, NSCCONS, 0);
+ if (nl[NSIO].n_type != 0)
+ ttytype(tty, "sio", SIO, NSIO, 0);
+ if (nl[NRC].n_type != 0)
+ ttytype(tty, "rc", RC, NRC, 0);
+ if (nl[NCY].n_type != 0)
+ ttytype(tty, "cy", CY, NCY, 0);
+ if (nl[NSI].n_type != 0)
+ ttytype(tty, "si", SI, NSI, 1);
+#endif
+ if (nl[SNPTY].n_type != 0)
+ ttytype(tty, "pty", SPTY, SNPTY, 0);
+}
+
+void
+ttytype(tty, name, type, number, indir)
+ struct tty *tty;
+ char *name;
+ int type, number, indir;
+{
+ struct tty *tp;
+ int ntty;
+ struct tty **ttyaddr;
+
+ if (tty == NULL)
+ return;
+ KGET(number, ntty);
+ (void)printf("%d %s %s\n", ntty, name, (ntty == 1) ? "line" : "lines");
+ if (ntty > ttyspace) {
+ ttyspace = ntty;
+ if ((tty = realloc(tty, ttyspace * sizeof(*tty))) == 0)
+ errx(1, "realloc");
+ }
+ if (indir) {
+ KGET(type, ttyaddr);
+ KGET2(ttyaddr, tty, ntty * sizeof(struct tty), "tty structs");
+ } else {
+ KGET1(type, tty, ntty * sizeof(struct tty), "tty structs");
+ }
+ (void)printf(hdr);
+ for (tp = tty; tp < &tty[ntty]; tp++)
+ ttyprt(tp, tp - tty);
+}
+
+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'},
+};
+
+void
+ttyprt(tp, line)
+ struct tty *tp;
+ int line;
+{
+ int i, j;
+ pid_t pgid;
+ char *name, state[20];
+
+ if (usenumflag || tp->t_dev == 0 ||
+ (name = devname(tp->t_dev, S_IFCHR)) == NULL)
+ (void)printf("%7d ", line);
+ else
+ (void)printf("%7s ", name);
+ (void)printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc);
+ (void)printf("%3d %5d %5d %4d %3d %7d ", tp->t_outq.c_cc,
+ tp->t_ihiwat, tp->t_ilowat, tp->t_ohiwat, tp->t_olowat,
+ tp->t_column);
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (tp->t_state&ttystates[i].flag)
+ state[j++] = ttystates[i].val;
+ if (j == 0)
+ state[j++] = '-';
+ state[j] = '\0';
+ (void)printf("%-6s %8lx", state, (u_long)(void *)tp->t_session);
+ pgid = 0;
+ if (tp->t_pgrp != NULL)
+ KGET2(&tp->t_pgrp->pg_id, &pgid, sizeof(pid_t), "pgid");
+ (void)printf("%6d ", pgid);
+ switch (tp->t_line) {
+ case TTYDISC:
+ (void)printf("term\n");
+ break;
+ case NTTYDISC:
+ (void)printf("ntty\n");
+ break;
+ case TABLDISC:
+ (void)printf("tab\n");
+ break;
+ case SLIPDISC:
+ (void)printf("slip\n");
+ break;
+ case PPPDISC:
+ (void)printf("ppp\n");
+ break;
+ default:
+ (void)printf("%d\n", tp->t_line);
+ break;
+ }
+}
+
+void
+filemode()
+{
+ struct file *fp;
+ struct file *addr;
+ char *buf, flagbuf[16], *fbp;
+ int len, maxfile, nfile;
+ static char *dtypes[] = { "???", "inode", "socket" };
+
+ KGET(FNL_MAXFILE, maxfile);
+ if (totalflag) {
+ KGET(FNL_NFILE, nfile);
+ (void)printf("%3d/%3d files\n", nfile, maxfile);
+ return;
+ }
+ if (getfiles(&buf, &len) == -1)
+ return;
+ /*
+ * Getfiles returns in malloc'd memory a pointer to the first file
+ * structure, and then an array of file structs (whose addresses are
+ * derivable from the previous entry).
+ */
+ addr = ((struct filelist *)buf)->lh_first;
+ fp = (struct file *)(buf + sizeof(struct filelist));
+ nfile = (len - sizeof(struct filelist)) / sizeof(struct file);
+
+ (void)printf("%d/%d open files\n", nfile, maxfile);
+ (void)printf(" LOC TYPE FLG CNT MSG DATA OFFSET\n");
+ for (; (char *)fp < buf + len; addr = fp->f_list.le_next, fp++) {
+ if ((unsigned)fp->f_type > DTYPE_SOCKET)
+ continue;
+ (void)printf("%8lx ", (u_long)(void *)addr);
+ (void)printf("%-8.8s", dtypes[fp->f_type]);
+ fbp = flagbuf;
+ if (fp->f_flag & FREAD)
+ *fbp++ = 'R';
+ if (fp->f_flag & FWRITE)
+ *fbp++ = 'W';
+ if (fp->f_flag & FAPPEND)
+ *fbp++ = 'A';
+#ifdef FSHLOCK /* currently gone */
+ if (fp->f_flag & FSHLOCK)
+ *fbp++ = 'S';
+ if (fp->f_flag & FEXLOCK)
+ *fbp++ = 'X';
+#endif
+ if (fp->f_flag & FASYNC)
+ *fbp++ = 'I';
+ *fbp = '\0';
+ (void)printf("%6s %3d", flagbuf, fp->f_count);
+ (void)printf(" %3d", fp->f_msgcount);
+ (void)printf(" %8lx", (u_long)(void *)fp->f_data);
+ if (fp->f_offset < 0)
+ (void)printf(" %qx\n", fp->f_offset);
+ else
+ (void)printf(" %qd\n", fp->f_offset);
+ }
+ free(buf);
+}
+
+int
+getfiles(abuf, alen)
+ char **abuf;
+ int *alen;
+{
+ size_t len;
+ int mib[2];
+ char *buf;
+
+ /*
+ * XXX
+ * Add emulation of KINFO_FILE here.
+ */
+ if (memf != 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>.
+ */
+void
+swapmode(void)
+{
+ struct kvm_swap kswap[16];
+ int i;
+ int n;
+ int pagesize = getpagesize();
+ const char *header;
+ int hlen;
+ long blocksize;
+
+ n = kvm_getswapinfo(
+ kd,
+ kswap,
+ sizeof(kswap)/sizeof(kswap[0]),
+ ((swapflag > 1) ? SWIF_DUMP_TREE : 0) | SWIF_DEV_PREFIX
+ );
+
+#define CONVERT(v) ((int)((quad_t)(v) * pagesize / blocksize))
+
+ header = getbsize(&hlen, &blocksize);
+ if (totalflag == 0) {
+ (void)printf("%-11s %*s %8s %8s %8s %s\n",
+ "Device", hlen, header,
+ "Used", "Avail", "Capacity", "Type");
+
+ for (i = 0; i < n; ++i) {
+ (void)printf(
+ "%-11s %*d ",
+ kswap[i].ksw_devname,
+ hlen,
+ CONVERT(kswap[i].ksw_total)
+ );
+ (void)printf(
+ "%8d %8d %5.0f%% %s\n",
+ CONVERT(kswap[i].ksw_used),
+ CONVERT(kswap[i].ksw_total - kswap[i].ksw_used),
+ (double)kswap[i].ksw_used * 100.0 /
+ (double)kswap[i].ksw_total,
+ (kswap[i].ksw_flags & SW_SEQUENTIAL) ?
+ "Sequential" : "Interleaved"
+ );
+ }
+ }
+
+ if (totalflag) {
+ blocksize = 1024 * 1024;
+
+ (void)printf(
+ "%dM/%dM swap space\n",
+ CONVERT(kswap[n].ksw_used),
+ CONVERT(kswap[n].ksw_total)
+ );
+ } else if (n > 1) {
+ (void)printf(
+ "%-11s %*d %8d %8d %5.0f%%\n",
+ "Total",
+ hlen,
+ CONVERT(kswap[n].ksw_total),
+ CONVERT(kswap[n].ksw_used),
+ CONVERT(kswap[n].ksw_total - kswap[n].ksw_used),
+ (double)kswap[n].ksw_used * 100.0 /
+ (double)kswap[n].ksw_total
+ );
+ }
+}
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
new file mode 100644
index 0000000..7b630dd
--- /dev/null
+++ b/usr.sbin/pw/Makefile
@@ -0,0 +1,18 @@
+# $Id: Makefile,v 1.6 1998/09/19 22:42:12 obrien Exp $
+
+PROG= pw
+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
+
+MAN5= pw.conf.5
+MAN8= pw.8
+
+#RND= -DUSE_MD5RAND
+CFLAGS+= -W -Wall $(CDB) $(RND)
+LDADD= -lcrypt
+DPADD= ${LIBCRYPT}
+
+BINMODE=0555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pw/README b/usr.sbin/pw/README
new file mode 100644
index 0000000..3888127
--- /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 memberhip 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>
+
+$Id: README,v 1.3 1997/02/22 16:12:18 peter Exp $
+
diff --git a/usr.sbin/pw/bitmap.c b/usr.sbin/pw/bitmap.c
new file mode 100644
index 0000000..bb55c5b
--- /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[] =
+ "$Id$";
+#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..a2ef01a
--- /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.
+ *
+ * $Id$
+ */
+
+#ifndef _BITMAP_H_
+#define _BITMAP_H_
+
+#include <sys/cdefs.h>
+
+struct bitmap
+{
+ int size;
+ unsigned char *map;
+};
+
+__BEGIN_DECLS
+struct bitmap bm_alloc __P((int size));
+void bm_dealloc __P((struct bitmap * bm));
+void bm_setbit __P((struct bitmap * bm, int pos));
+void bm_clrbit __P((struct bitmap * bm, int pos));
+int bm_isset __P((struct bitmap * bm, int pos));
+int bm_firstunset __P((struct bitmap * bm));
+int bm_lastset __P((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..048a87b
--- /dev/null
+++ b/usr.sbin/pw/cpdir.c
@@ -0,0 +1,116 @@
+/*-
+ * 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[] =
+ "$Id$";
+#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;
+
+ sprintf(src, "%s/%s", skel, p);
+ if (stat(src, &st) == 0) {
+ if (strncmp(p, "dot.", 4) == 0) /* Conversion */
+ p += 3;
+ sprintf(dst, "%s/%s", dir, p);
+ 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);
+ /*
+ * Note: don't propogate 'special' attributes
+ */
+ } 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);
+ 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..649a398
--- /dev/null
+++ b/usr.sbin/pw/edgroup.c
@@ -0,0 +1,230 @@
+/*-
+ * 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[] =
+ "$Id: edgroup.c,v 1.6 1998/07/16 17:18:22 nate Exp $";
+#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, 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 | O_EXLOCK, 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. Unfortunately, it will lose preservation
+ * of the inode.
+ */
+ 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..d76259b
--- /dev/null
+++ b/usr.sbin/pw/fileupd.c
@@ -0,0 +1,197 @@
+/*-
+ * 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[] =
+ "$Id: fileupd.c,v 1.6 1998/07/16 17:18:24 nate Exp $";
+#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)
+ errno = EINVAL;
+ else {
+ int infd = open(filename, O_RDWR | O_CREAT, fmode);
+
+ if (infd != -1) {
+ FILE *infp = fdopen(infd, "r+");
+
+ if (infp == NULL)
+ close(infd);
+ else {
+ int outfd;
+ char file[MAXPATHLEN];
+
+ strcpy(file, filename);
+ strcat(file, ".new");
+ outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
+ if (outfd != -1) {
+ FILE *outfp = fdopen(outfd, "w+");
+
+ if (outfp == NULL)
+ 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)
+ errno = (updmode == UPD_CREATE) ? EEXIST : ENOENT;
+ 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
+ */
+ rc = fflush(outfp) != EOF;
+ if (rc) {
+
+ /*
+ * 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
+ * Unfortunately, it will lose the inode
+ * and hence the lock.
+ */
+ if (fflush(infp) == EOF || ferror(infp)) {
+ rc = errno; /* Preserve errno for return */
+ 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..e36c192
--- /dev/null
+++ b/usr.sbin/pw/grupd.c
@@ -0,0 +1,170 @@
+/*-
+ * 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[] =
+ "$Id: grupd.c,v 1.5 1997/10/10 06:23:32 charnier Exp $";
+#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 {
+ if ((l = fileupdate(getgrpath(_GROUP), 0644, grbuf, pfx, l, mode)) != 0)
+ l = grdb(NULL) == 0;
+ }
+ 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..5bdd3c9
--- /dev/null
+++ b/usr.sbin/pw/psdate.c
@@ -0,0 +1,299 @@
+/*-
+ * 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[] =
+ "$Id: psdate.c,v 1.4 1997/10/10 06:23:32 charnier Exp $";
+#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(*s)) {
+ i = atoi(s);
+ while (isdigit(*s))
+ ++s;
+ *str = s;
+ }
+ return i;
+}
+
+static int
+numerics(char const * str)
+{
+ int rc = isdigit(*str);
+
+ if (rc)
+ while (isdigit(*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(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(**str))
+ ++(*str);
+ /* And any following whitespace */
+ while (**str && (**str == ',' || isspace(**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(*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(*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);
+ str = strncpy(tmp, str, sizeof tmp - 1);
+ tmp[sizeof tmp - 1] = '\0';
+ 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(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;
+
+ strncpy(timestr, str, l);
+ timestr[l] = '\0';
+ strncpy(datestr, q + 1, sizeof datestr);
+ datestr[sizeof datestr - 1] = '\0';
+ 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;
+
+ strncpy(timestr, q + 1, sizeof timestr);
+ timestr[sizeof timestr - 1] = '\0';
+ strncpy(datestr, tmp, l);
+ datestr[l] = '\0';
+ } 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..f0f1522
--- /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.
+ *
+ * $Id$
+ */
+
+#ifndef _PSDATE_H_
+#define _PSDATE_H_
+
+#include <time.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+time_t parse_date __P((time_t dt, char const * str));
+void print_date __P((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..5de8237
--- /dev/null
+++ b/usr.sbin/pw/pw.8
@@ -0,0 +1,887 @@
+.\" 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.
+.\"
+.\" $Id: pw.8,v 1.15 1998/09/18 04:45:43 jkoshy Exp $
+.\"
+.Dd December 9, 1996
+.Dt PW 8
+.Os
+.Sh NAME
+.Nm pw
+.Nd create, remove, modify & display system users and groups
+.Sh SYNOPSIS
+.Nm pw
+.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 s Ar shell
+.Op Fl o
+.Op Fl L Ar class
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.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,max
+.Op Fl i Ar min,max
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl y Ar path
+.Nm pw
+.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 pw
+.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
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.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 a
+.Nm pw
+.Op Fl V Ar etcdir
+.Ar usernext
+.Op Fl C Ar config
+.Op Fl q
+.Nm pw
+.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
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.Op Fl V Ar etcdir
+.Ar groupdel
+.Op group|gid
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl Y
+.Nm pw
+.Op Fl V Ar etcdir
+.Ar groupmod
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl F
+.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
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.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 pw
+.Op Fl V Ar etcdir
+.Ar groupnext
+.Op Fl C Ar config
+.Op Fl q
+.Sh DESCRIPTION
+.Nm Pw
+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. NIS users and groups must be
+maintained on the NIS server.
+.Nm Pw
+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 NIS database files.
+If separate passwd and group files are being used by NIS, then use the
+.Fl y Ar path
+option to specify the location of the NIS passwd database so that
+.Nm
+will concurrently update it with the system password
+databases.
+.El
+.Pp
+.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
+.Ql 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
+.Ql Fl n
+or
+.Ql 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
+.Ql 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
+Sets 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
+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 filesystem.
+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
+.Ql 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
+.Ql Fl b
+option (see below), bearing the name of the new account.
+This can be overridden by the
+.Ql 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
+.Ql Fl d
+or
+.Ql 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 L Ar class
+Set the
+.Em class
+field in the user's passwd record.
+This field is not currently used, but will be used in the future to specify a
+.Em termcap
+entry like tag. See
+.Xr passwd 5
+for details.
+.It Fl h Ar fd
+This option provides a special interface by which interactive scripts can
+set an account password using
+.Nm pw .
+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
+.Ql 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 pw .
+.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.
+.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
+.Ql 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
+.Ql 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
+.Ql Fl D
+option, you must not use either
+.Ql Fl n Ar name
+or
+.Ql Fl u Ar uid
+or an error will result.
+Use of
+.Ql 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
+.Ql 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
+.Ql 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
+.Ql 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 Fl u Ar min,max , Fl i Ar min,max
+These options set the minimum and maximum user and group ids allocated for new accounts
+and groups created by
+.Nm pw .
+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
+.Ql 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 NIS if you are not sharing
+the information from
+.Pa /etc/master.passwd
+directly with NIS.
+You should only set this option for NIS servers.
+.El
+.Pp
+The
+.Ar userdel
+command has only three valid options. The
+.Ql Fl n Ar name
+and
+.Ql 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.
+.Nm Pw
+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
+.Ql Fl P
+option is used, then
+.Nm
+outputs the account details in a more human readable form.
+The
+.Ql Fl a
+option lists all users currently on file.
+.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 pw .
+.Pp
+.Sh GROUP OPTIONS
+The
+.Ql Fl C
+and
+.Ql 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
+.Ql 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
+.Ql 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
+.Ql Fl g Ar gid
+replacing
+.Ql Fl u Ar uid
+to specify the group id.
+.Pp
+The command
+.Ar groupnext
+returns the next available group id on standard output.
+.Sh DIAGNOSTICS
+.Nm Pw
+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
+.Pp
+.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
+.Nm Pw
+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.
+.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
+.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
+.Nm Pw
+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..f71cdf7
--- /dev/null
+++ b/usr.sbin/pw/pw.c
@@ -0,0 +1,427 @@
+/*-
+ * 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[] =
+ "$Id: pw.c,v 1.14 1999/02/23 11:01:50 davidn Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <sys/wait.h>
+#include "pw.h"
+
+const char *Modes[] = {"add", "del", "mod", "show", "next", NULL};
+const char *Which[] = {"user", "group", NULL};
+static const char *Combo1[] = {
+ "useradd", "userdel", "usermod", "usershow", "usernext",
+ "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
+ NULL};
+static const char *Combo2[] = {
+ "adduser", "deluser", "moduser", "showuser", "nextuser",
+ "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:Db:NPy:Y",
+ "V:C:qn:u:rY",
+ "V:C:qn:u:c:d:e:p:g:G:ml:k:s:w:L:h:FNPY",
+ "V:C:qn:u:FPa7",
+ "V:C:q"
+ },
+ { /* grp */
+ "V:C:qn:g:h:M:pNPY",
+ "V:C:qn:g:Y",
+ "V:C:qn:g:l: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);
+
+ /*
+ * 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 ((tmp = getindex(Modes, argv[1])) != -1)
+ mode = tmp;
+ else if ((tmp = getindex(Which, argv[1])) != -1)
+ which = tmp;
+ else if ((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)
+ 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, NULL);
+ 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("/dev/null", "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", 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: pw [user|group] [add|del|mod|show|next] [ help | switches/values ]\n");
+ else if (mode == -1)
+ fprintf(stderr, "usage: 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-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-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"
+ },
+ {
+ "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"
+ }
+ };
+
+ fprintf(stderr, help[which][mode]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+struct carg *
+getarg(struct cargs * _args, int ch)
+{
+ struct carg *c = _args->lh_first;
+
+ while (c != NULL && c->ch != ch)
+ c = c->list.le_next;
+ 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..06bccb7
--- /dev/null
+++ b/usr.sbin/pw/pw.conf.5
@@ -0,0 +1,301 @@
+.\" 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.
+.\"
+.\" $Id: pw.conf.5,v 1.7 1997/10/10 06:23:34 charnier Exp $
+.\"
+.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
+.Aq Pa /etc/pw.conf
+contains configuration data for the
+.Xr pw 8
+program.
+The
+.Xr pw 8
+program 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
+.Aq 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 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.
+.Xr Pw 8
+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 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.
+.Xr Pw 8 '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
+.Ar shellpath
+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
+.Ar extragroups
+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 determines 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.
+.Pp
+.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..16bea61
--- /dev/null
+++ b/usr.sbin/pw/pw.h
@@ -0,0 +1,129 @@
+/*-
+ * 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.
+ *
+ * $Id: pw.h,v 1.7 1997/10/10 06:23:35 charnier Exp $
+ */
+
+#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_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,...);
+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..dc1bd61
--- /dev/null
+++ b/usr.sbin/pw/pw_conf.c
@@ -0,0 +1,495 @@
+/*-
+ * 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[] =
+ "$Id: pw_conf.c,v 1.7 1997/10/10 06:23:36 charnier Exp $";
+#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"
+};
+
+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 */
+};
+
+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# Space 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 + 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 + 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..220a9b1
--- /dev/null
+++ b/usr.sbin/pw/pw_group.c
@@ -0,0 +1,334 @@
+/*-
+ * 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[] =
+ "$Id: pw_group.c,v 1.7 1997/10/10 06:23:36 charnier Exp $";
+#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)
+{
+ 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
+ };
+
+ /*
+ * 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(*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;
+
+ if (delgrent(grp) == -1)
+ err(EX_IOERR, "error updating group file");
+ 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) {
+ if (strcmp(arg->val, "-") == 0)
+ grp->gr_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ 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);
+ 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(*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 && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) {
+ 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 (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) 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..e11e5ee
--- /dev/null
+++ b/usr.sbin/pw/pw_log.c
@@ -0,0 +1,67 @@
+/*-
+ * 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[] =
+ "$Id$";
+#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";
+ strftime(nfmt, sizeof nfmt, "%d-%b-%Y %R ", 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..3b041a8
--- /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[] =
+ "$Id: pw_nis.c,v 1.4 1997/10/10 06:23:38 charnier Exp $";
+#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..0eb9802
--- /dev/null
+++ b/usr.sbin/pw/pw_user.c
@@ -0,0 +1,1126 @@
+/*-
+ * 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[] =
+ "$Id: pw_user.c,v 1.27 1999/02/23 07:15:10 davidn Exp $";
+#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>
+#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 randinit;
+
+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 rmskey(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
+ * -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 r, r1;
+ char *p = NULL;
+ 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];
+
+ static struct passwd fakeuser =
+ {
+ NULL,
+ "*",
+ -1,
+ -1,
+ 0,
+ "",
+ "User &",
+ "/bin/sh",
+ 0,
+ 0
+ };
+
+ /*
+ * 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 */
+ }
+ p = strncpy(dbuf, cnf->home, sizeof dbuf);
+ dbuf[MAXPATHLEN-1] = '\0';
+ 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) {
+ p = arg->val;
+ if ((grp = GETGRNAM(p)) == NULL) {
+ if (!isdigit(*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(*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 (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;
+ }
+ if ((arg = getarg(args, 'w')) != NULL)
+ cnf->default_password = boolean_val(arg->val, cnf->default_password);
+
+ 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) {
+ 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));
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ 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 skey record from /etc/skeykeys
+ */
+
+ rmskey(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);
+ strncpy(home, pwd->pw_dir, sizeof home);
+ home[sizeof home - 1] = '\0';
+
+ if (!delpwent(pwd))
+ err(EX_IOERR, "error updating passwd file");
+
+ if (cnf->nispasswd && *cnf->nispasswd=='/' && !delnispwent(cnf->nispasswd, a_name->val))
+ warn("WARNING: NIS passwd update");
+
+ 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);
+ }
+ if ((arg = getarg(args, 'u')) != NULL && isdigit(*arg->val)) {
+ pwd->pw_uid = (uid_t) atol(arg->val);
+ 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 */
+ pwd->pw_gid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid;
+
+ if ((arg = getarg(args, 'p')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
+ pwd->pw_change = 0;
+ 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);
+ pwd->pw_change = expire;
+ }
+ }
+ if ((arg = getarg(args, 'e')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
+ pwd->pw_expire = 0;
+ 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);
+ pwd->pw_expire = expire;
+ }
+ }
+ if ((arg = getarg(args, 's')) != NULL)
+ pwd->pw_shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
+
+ if (getarg(args, 'L'))
+ pwd->pw_class = cnf->default_class;
+
+ if ((arg = getarg(args, 'd')) != NULL) {
+ 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)
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+
+ } else {
+ 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_passwd = pw_password(cnf, args, pwd->pw_name);
+ 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);
+
+ 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)
+ pwd->pw_gecos = pw_checkname((u_char *)arg->val, 1);
+
+ if ((arg = getarg(args, 'h')) != NULL) {
+ if (strcmp(arg->val, "-") == 0)
+ pwd->pw_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+
+ 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 user %s:", (mode == M_UPDATE) ? "New p" : "P", 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("-h file descriptor");
+ 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);
+ pwd->pw_passwd = pw_pwcrypt(line);
+ }
+ }
+
+ /*
+ * Special case: -N only displays & exits
+ */
+ if (getarg(args, 'N') != NULL)
+ return print_user(pwd,
+ getarg(args, 'P') != NULL,
+ getarg(args, '7') != NULL);
+
+ r = r1 = 1;
+ if (mode == M_ADD) {
+ r = addpwent(pwd);
+ if (r && cnf->nispasswd && *cnf->nispasswd=='/')
+ r1 = addnispwent(cnf->nispasswd, pwd);
+ } else if (mode == M_UPDATE) {
+ r = chgpwent(a_name->val, pwd);
+ if (r && cnf->nispasswd && *cnf->nispasswd=='/')
+ r1 = chgnispwent(cnf->nispasswd, a_name->val, pwd);
+ }
+
+ if (!r) {
+ warn("password update");
+ return EX_IOERR;
+ } else if (!r1) {
+ warn("WARNING: NIS password update");
+ /* Keep on trucking */
+ }
+
+ /*
+ * Ok, user is created or changed - now edit group file
+ */
+
+ if (mode == M_ADD || getarg(args, 'G') != NULL)
+ editgroups(pwd->pw_name, cnf->groups);
+
+ /* pwd may have been invalidated */
+ if ((pwd = GETPWNAM(a_name->val)) == NULL)
+ 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(%d):%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) {
+ FILE *fp;
+
+ 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);
+
+ /*
+ * Send mail to the new user as well, if we are asked to
+ */
+ if (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);
+ }
+ }
+ }
+ /*
+ * Finally, 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);
+ }
+ 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(*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 = grpargs.lh_first;
+ while (a_gid != NULL) {
+ struct carg *t = a_gid->list.le_next;
+ 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
+ */
+ strncpy(paths, path, sizeof paths);
+ paths[sizeof paths - 1] = '\0';
+ 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
+ */
+ if (!randinit) {
+ randinit = 1;
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((unsigned long) (time(NULL) ^ getpid()));
+#endif
+ }
+ for (i = 0; i < 8; i++)
+ salt[i] = chars[random() % 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-n));
+ }
+ 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 = random();
+ /* 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 */
+ if (!randinit) {
+ randinit = 1;
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((unsigned long) (time(NULL) ^ getpid()));
+#endif
+ }
+ l = (random() % 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, 'N') == NULL) {
+ if (isatty(1))
+ 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 */
+ strncpy(pwbuf, user, sizeof pwbuf);
+ pwbuf[sizeof pwbuf - 1] = '\0';
+ 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) {
+ strncpy(uname, p, sizeof uname);
+ uname[sizeof uname - 1] = '\0';
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strncpy(office, p, sizeof office);
+ office[sizeof office - 1] = '\0';
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strncpy(wphone, p, sizeof wphone);
+ wphone[sizeof wphone - 1] = '\0';
+ if ((p = strtok(NULL, "")) != NULL) {
+ strncpy(hphone, p, sizeof hphone);
+ hphone[sizeof hphone - 1] = '\0';
+ }
+ }
+ }
+ }
+ /*
+ * 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(*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)9 && (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\n", j ? "\n" : "");
+ }
+ return EXIT_SUCCESS;
+}
+
+char *
+pw_checkname(u_char *name, int gecos)
+{
+ int l = 0;
+ char const *notch = gecos ? ":!@" : " ,\t:+&#%$^()!@~*?<>=|\\/\"";
+
+ while (name[l]) {
+ if (strchr(notch, name[l]) != NULL || name[l] < ' ' || name[l] == 127 ||
+ (!gecos && l==0 && name[l] == '-') || /* leading '-' */
+ (!gecos && name[l] & 0x80)) /* 8-bit */
+ errx(EX_DATAERR, (name[l] >= ' ' && name[l] < 127)
+ ? "invalid character `%c' in field"
+ : "invalid character 0x%02x in field",
+ name[l]);
+ ++l;
+ }
+ if (!gecos && l > LOGNAMESIZE)
+ errx(EX_DATAERR, "name too long `%s'", name);
+ 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
+rmskey(char const * name)
+{
+ static const char etcskey[] = "/etc/skeykeys";
+ FILE *fp = fopen(etcskey, "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..fca5324
--- /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[] =
+ "$Id$";
+#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];
+
+ strncpy(pwtmp, getpwpath(_MASTERPASSWD), sizeof pwtmp);
+ pwtmp[sizeof pwtmp - 1] = '\0';
+
+ 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;
+ }
+}
+
+int
+vsetgrent(void)
+{
+ vendgrent();
+ return 0;
+}
+
+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);
+ strncpy(grtmp, getgrpath(_GROUP), MAXPATHLEN);
+ grtmp[MAXPATHLEN - 1] = '\0';
+
+ 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..8e5fb32
--- /dev/null
+++ b/usr.sbin/pw/pwupd.c
@@ -0,0 +1,194 @@
+/*-
+ * 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[] =
+ "$Id: pwupd.c,v 1.7 1998/02/11 23:31:24 wosch Exp $";
+#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
+
+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 = -1;
+ else if (pid == 0) { /* Child */
+ execv(args[0], args);
+ _exit(1);
+ } else { /* Parent */
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errno = EIO; /* set SOMETHING */
+ }
+ return i;
+}
+
+int
+fmtpwentry(char *buf, struct passwd * pwd, int type)
+{
+ int l;
+ char *pw;
+
+ pw = (pwd->pw_passwd == NULL || !*pwd->pw_passwd) ? "" : (type == PWF_MASTER) ? 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[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_PASSWD);
+ if ((rc = fileupdate(getpwpath(_PASSWD), 0644, pwbuf, pfx, l, mode)) != 0) {
+
+ /*
+ * Then the master.passwd file
+ */
+ if (pwd != NULL)
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ if ((rc = fileupdate(getpwpath(_MASTERPASSWD), 0644, pwbuf, pfx, l, mode)) != 0)
+ rc = pwdb(NULL) == 0;
+ }
+ }
+ 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..6695973
--- /dev/null
+++ b/usr.sbin/pw/pwupd.h
@@ -0,0 +1,156 @@
+/*-
+ * 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.
+ *
+ * $Id: pwupd.h,v 1.4 1997/02/22 16:12:31 peter Exp $
+ */
+
+#ifndef _PWUPD_H_
+#define _PWUPD_H_
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/cdefs.h>
+
+enum updtype
+{
+ UPD_DELETE = -1,
+ UPD_CREATE = 0,
+ UPD_REPLACE = 1
+};
+
+__BEGIN_DECLS
+int fileupdate __P((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, ...);
+ int (*_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 __P((struct passwd * pwd));
+int delpwent __P((struct passwd * pwd));
+int chgpwent __P((char const * login, struct passwd * pwd));
+int fmtpwent __P((char *buf, struct passwd * pwd));
+int fmtpwentry __P((char *buf, struct passwd * pwd, int type));
+
+int setpwdir __P((const char * dir));
+char * getpwpath __P((char const * file));
+int pwdb __P((char *arg, ...));
+
+int addgrent __P((struct group * grp));
+int delgrent __P((struct group * grp));
+int chggrent __P((char const * name, struct group * grp));
+int fmtgrent __P((char **buf, int * buflen, struct group * grp));
+int fmtgrentry __P((char **buf, int * buflen, struct group * grp, int type));
+int editgroups __P((char *name, char **groups));
+
+int setgrdir __P((const char * dir));
+char * getgrpath __P((const char *file));
+int grdb __P((char *arg, ...));
+
+void vsetpwent __P((void));
+void vendpwent __P((void));
+struct passwd * vgetpwent __P((void));
+struct passwd * vgetpwuid __P((uid_t uid));
+struct passwd * vgetpwnam __P((const char * nam));
+struct passwd * vgetpwent __P((void));
+int vpwdb __P((char *arg, ...));
+
+int vsetgrent __P((void));
+void vendgrent __P((void));
+struct group * vgetgrent __P((void));
+struct group * vgetgrgid __P((gid_t gid));
+struct group * vgetgrnam __P((const char * nam));
+struct group * vgetgrent __P((void));
+int vgrdb __P((char *arg, ...));
+int vsetgrent __P((void));
+void vendgrent __P((void));
+
+void copymkdir __P((char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid));
+void rm_r __P((char const * dir, uid_t uid));
+int extendline __P((char **buf, int *buflen, int needed));
+int extendarray __P((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..f671486
--- /dev/null
+++ b/usr.sbin/pw/rm_r.c
@@ -0,0 +1,74 @@
+/*-
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.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..25d001e
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pwd_mkdb
+SRCS= pw_scan.c pwd_mkdb.c
+MAN8= pwd_mkdb.8
+CFLAGS+= -DPASSWD_IGNORE_COMMENTS
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pwd_mkdb/pw_scan.c b/usr.sbin/pwd_mkdb/pw_scan.c
new file mode 100644
index 0000000..d474786
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pw_scan.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1990, 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
+#if 0
+static char sccsid[] = "@(#)pw_scan.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This module is used to "verify" password entries by chpass(1) and
+ * pwd_mkdb(8).
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pw_scan.h"
+
+int
+pw_scan(bp, pw)
+ char *bp;
+ struct passwd *pw;
+{
+ long id;
+ int root;
+ char *p, *sh;
+
+ pw->pw_fields = 0;
+ if (!(pw->pw_name = strsep(&bp, ":"))) /* login */
+ goto fmt;
+ root = !strcmp(pw->pw_name, "root");
+ if(pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0'))
+ pw->pw_fields |= _PWF_NAME;
+
+ if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
+ goto fmt;
+ if(pw->pw_passwd[0]) pw->pw_fields |= _PWF_PASSWD;
+
+ if (!(p = strsep(&bp, ":"))) /* uid */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_UID;
+ id = atol(p);
+ if (root && id) {
+ warnx("root uid should be 0");
+ return (0);
+ }
+ if (id > USHRT_MAX) {
+ warnx("%s > max uid value (%d)", p, USHRT_MAX);
+ /*return (0);*/ /* THIS SHOULD NOT BE FATAL! */
+ }
+ pw->pw_uid = id;
+
+ if (!(p = strsep(&bp, ":"))) /* gid */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_GID;
+ id = atol(p);
+ if (id > USHRT_MAX) {
+ warnx("%s > max gid value (%d)", p, USHRT_MAX);
+ /* return (0); This should not be fatal! */
+ }
+ pw->pw_gid = id;
+
+ pw->pw_class = strsep(&bp, ":"); /* class */
+ if(pw->pw_class[0]) pw->pw_fields |= _PWF_CLASS;
+
+ if (!(p = strsep(&bp, ":"))) /* change */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_CHANGE;
+ pw->pw_change = atol(p);
+
+ if (!(p = strsep(&bp, ":"))) /* expire */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_EXPIRE;
+ pw->pw_expire = atol(p);
+
+ if (!(pw->pw_gecos = strsep(&bp, ":"))) /* gecos */
+ goto fmt;
+ if(pw->pw_gecos[0]) pw->pw_fields |= _PWF_GECOS;
+
+ if (!(pw->pw_dir = strsep(&bp, ":"))) /* directory */
+ goto fmt;
+ if(pw->pw_dir[0]) pw->pw_fields |= _PWF_DIR;
+
+ if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
+ goto fmt;
+
+ p = pw->pw_shell;
+ if (root && *p) /* empty == /bin/sh */
+ for (setusershell();;) {
+ if (!(sh = getusershell())) {
+ warnx("warning, unknown root shell");
+ break;
+ }
+ if (!strcmp(p, sh))
+ break;
+ }
+ if(p[0]) pw->pw_fields |= _PWF_SHELL;
+
+ if ((p = strsep(&bp, ":"))) { /* too many */
+fmt: warnx("corrupted entry");
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.sbin/pwd_mkdb/pw_scan.h b/usr.sbin/pwd_mkdb/pw_scan.h
new file mode 100644
index 0000000..d1d4bc1
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pw_scan.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pw_scan.h 8.1 (Berkeley) 4/1/94
+ */
+
+extern int pw_scan __P((char *, struct passwd *));
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8
new file mode 100644
index 0000000..7beecd7
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8
@@ -0,0 +1,157 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt PWD_MKDB 8
+.Os
+.Sh NAME
+.Nm pwd_mkdb
+.Nd "generate the password databases"
+.Sh SYNOPSIS
+.Nm pwd_mkdb
+.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
+.Nm Pwd_mkdb
+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 Pwd_mkdb
+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 pwd_mkdb
+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
+.Nm Pwd_mkdb
+exits zero on success, non-zero on failure.
+.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 pwd_mkdb ,
+.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 pwd_mkdb ,
+.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..bb0398e
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -0,0 +1,619 @@
+/*-
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
+#endif
+static const char rcsid[] =
+ "$Id: pwd_mkdb.c,v 1.29 1998/12/13 01:53:50 dillon Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.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)
+
+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 __P((void));
+void error __P((char *));
+void cp __P((char *, char *, mode_t mode));
+void mv __P((char *, char *));
+int scan __P((FILE *, struct passwd *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB *dp, *sdp, *pw_db;
+ DBT data, sdata, key;
+ FILE *fp, *oldfp;
+ sigset_t set;
+ int ch, cnt, ypcnt, len, makeold, tfd, yp_enabled = 0;
+ char *p, *t;
+ 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) {
+ (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);
+ buf[0] = _PW_KEYBYNAME;
+ 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_KEYBYUID;
+ memmove(buf + 1, p, sizeof(int));
+ key.data = (u_char *)buf;
+ key.size = sizeof(int) + 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.)
+ */
+ 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;
+#define COMPACT(e) t = e; while ((*p++ = *t++));
+#ifdef PASSWD_IGNORE_COMMENTS
+ if(is_comment)
+ --cnt;
+#endif
+ if (!is_comment &&
+ (!username || (strcmp(username, pwd.pw_name) == 0))) {
+ /* Create insecure data. */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
+ memmove(p, &pwd.pw_uid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_change, sizeof(time_t));
+ p += sizeof(time_t);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pwd.pw_expire, sizeof(time_t));
+ p += sizeof(time_t);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ data.size = p - buf;
+
+ /* Create secure data. */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ memmove(p, &pwd.pw_uid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_change, sizeof(time_t));
+ p += sizeof(time_t);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pwd.pw_expire, sizeof(time_t));
+ p += sizeof(time_t);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = _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] = _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] = _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] = _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] = _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] = _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] = _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), "%d", pwd.pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%d", 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;
+ tbuf[0] = _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'))) {
+ warnx("line too long");
+ goto fmt;
+
+ }
+ *p = '\0';
+
+#ifdef PASSWD_IGNORE_COMMENTS
+ /*
+ * 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;
+#endif
+
+ if (!pw_scan(line, pw)) {
+ 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)
+ 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..4a25625
--- /dev/null
+++ b/usr.sbin/quot/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+PROG= quot
+MAN8= quot.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quot/quot.8 b/usr.sbin/quot/quot.8
new file mode 100644
index 0000000..924e721
--- /dev/null
+++ b/usr.sbin/quot/quot.8
@@ -0,0 +1,103 @@
+.\" 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.
+.\"
+.\" $Id: quot.8,v 1.8 1997/10/10 06:31:06 charnier Exp $
+.\"
+.Dd February 8, 1994
+.Dt QUOT 8
+.Os BSD 4
+.Sh NAME
+.Nm quot
+.Nd display disk space occupied by each user
+.Sh SYNOPSIS
+.Nm quot
+.Op Fl acfhknv
+.Op Ar filesystem ...
+.Sh DESCRIPTION
+.Nm Quot
+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 filesystems.
+.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 doesn't give the correct results (it doesn't
+account for the holes in files), this option isn't any faster
+and thus is discouraged.
+.It Fl k
+By default, all sizes are reported in 512-byte block counts.
+The
+.Fl k
+options causes the numbers to be reported in kilobyte 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
+.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
+.Gl k
+option is not specified, the block counts will be displayed in units of that
+size block.
+.El
+.Sh BUGS
+ncheck does not exist in FreeBSD.. :-)
+.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..547fd8f
--- /dev/null
+++ b/usr.sbin/quot/quot.c
@@ -0,0 +1,583 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: quot.c,v 1.8 1998/01/17 16:45:03 bde Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <errno.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)();
+static long blocksize;
+static char *header;
+static int headerlen;
+
+/*
+ * 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) (sizeof(struct dinode) * INOCNT(fs))
+
+static struct dinode *
+get_inode(fd,super,ino)
+ struct fs *super;
+ ino_t ino;
+{
+ static struct dinode *ip;
+ static ino_t last;
+
+ if (fd < 0) { /* flush cache */
+ if (ip) {
+ free(ip);
+ ip = 0;
+ }
+ return 0;
+ }
+
+ if (!ip || ino < last || ino >= last + INOCNT(super)) {
+ if (!ip
+ && !(ip = (struct dinode *)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,ip,INOSZ(super)) != INOSZ(super))
+ err(1, "read inodes");
+ }
+
+ return ip + ino % INOCNT(super);
+}
+
+#ifdef COMPAT
+#define actualblocks(super,ip) ((ip)->di_blocks/2)
+#else
+#define actualblocks(super,ip) ((ip)->di_blocks)
+#endif
+
+static int virtualblocks(super,ip)
+ struct fs *super;
+ struct dinode *ip;
+{
+ register off_t nblk, sz;
+
+ sz = ip->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(ip)
+ struct dinode *ip;
+{
+#ifdef COMPAT
+ return (ip->di_mode&IFMT) == 0;
+#else /* COMPAT */
+
+ switch (ip->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 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 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 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(u1,u2)
+ struct user *u1, *u2;
+{
+ 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 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)
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct dinode *ip;
+ daddr_t sz, ksz;
+ struct fsizes *fp, **fsp;
+ register i;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+#ifdef COMPAT
+ if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
+ errx(1, "alloc fsize structure");
+#endif /* COMPAT */
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+#ifdef COMPAT
+ && ((ip->di_mode&IFMT) == IFREG
+ || (ip->di_mode&IFMT) == IFDIR)
+#else /* COMPAT */
+ && !isfree(ip)
+#endif /* COMPAT */
+ ) {
+ sz = estimate ? virtualblocks(super,ip) :
+ actualblocks(super,ip);
+#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, "alloc 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("%d\t%d\t%d\n",fp->fsz_first + i,
+ fp->fsz_count[i],
+ SIZE(sz += fp->fsz_sz[i]));
+ }
+ }
+}
+
+static void
+douser(fd,super,name)
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct user *usr, *usrs;
+ struct dinode *ip;
+ register n;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+ && !isfree(ip))
+ uses(ip->di_uid,
+ estimate ? virtualblocks(super,ip) :
+ actualblocks(super,ip),
+ ip->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%5d",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)
+ struct fs *super;
+ char *name;
+{
+ int c;
+ ino_t inode, inode1;
+ ino_t maxino;
+ struct dinode *ip;
+
+ 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("%d",&inode) == 1) {
+ if (inode < 0 || inode > maxino) {
+ warnx("illegal inode %d",inode);
+ return;
+ }
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+ && !isfree(ip)) {
+ printf("%s\t",user(ip->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);
+}
+
+static char superblock[SBSIZE];
+
+void
+quot(name,mp)
+ char *name, *mp;
+{
+ int fd;
+
+ get_inode(-1); /* flush cache */
+ inituser();
+ initfsizes();
+ if ((fd = open(name,0)) < 0
+ || lseek(fd,SBOFF,0) != SBOFF
+ || read(fd,superblock,SBSIZE) != SBSIZE) {
+ warn("%s", name);
+ close(fd);
+ return;
+ }
+ if (((struct fs *)superblock)->fs_magic != FS_MAGIC) {
+ warnx("%s: not a BSD filesystem",name);
+ close(fd);
+ return;
+ }
+ printf("%s:",name);
+ if (mp)
+ printf(" (%s)",mp);
+ putchar('\n');
+ (*func)(fd,superblock,name);
+ close(fd);
+}
+
+int
+main(argc,argv)
+ char **argv;
+{
+ char all = 0;
+ struct statfs *mp;
+ 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,"/dev/r%s",nm + 1);
+ nm = dev;
+ } else
+ nm = mp->f_mntfromname;
+ quot(nm,mp->f_mntonname);
+ }
+ }
+ }
+ while (--argc >= 0)
+ quot(*argv++,0);
+ return 0;
+}
diff --git a/usr.sbin/quotaon/Makefile b/usr.sbin/quotaon/Makefile
new file mode 100644
index 0000000..be3001e
--- /dev/null
+++ b/usr.sbin/quotaon/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= quotaon
+MAN8= quotaon.8
+MLINKS= quotaon.8 quotaoff.8
+LINKS= ${BINDIR}/quotaon ${BINDIR}/quotaoff
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quotaon/quotaon.8 b/usr.sbin/quotaon/quotaon.8
new file mode 100644
index 0000000..7094de5
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.8
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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
+.\"
+.Dd December 11, 1993
+.Dt QUOTAON 8
+.Os BSD 4.2
+.Sh NAME
+.Nm quotaon ,
+.Nm quotaoff
+.Nd turn filesystem quotas on and off
+.Sh SYNOPSIS
+.Nm quotaon
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotaon
+.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
+.Nm Quotaon
+announces to the system that disk quotas should be enabled on one or more
+filesystems.
+.Nm Quotaoff
+announces to the system that the specified
+filesystems should have any disk quotas
+diskquotas turned off.
+The filesystems specified must have entries in
+.Pa /etc/fstab
+and be mounted.
+.Nm Quotaon
+expects each filesystem 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 filesystem names,
+.Nm quotaon Ns / Ns Nm quotaoff
+will enable/disable all the filesystems 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 quotaon
+and
+.Nm quotaoff
+to print a message for each filesystem 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 filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+filesystem 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
+command 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..61b8394
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.c
@@ -0,0 +1,265 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)quotaon.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * 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 <string.h>
+#include <unistd.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+int aflag; /* all file systems */
+int gflag; /* operate on group quotas */
+int uflag; /* operate on user quotas */
+int vflag; /* verbose */
+
+int hasquota __P((struct fstab *, int, char **));
+int oneof __P((char *, char *[], int));
+int quotaonoff __P((struct fstab *fs, int, int, char *));
+int readonly __P((struct fstab *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct fstab *fs;
+ char ch, *qfnp, *whoami;
+ long argnum, done = 0;
+ int 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 file system 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..bc7e7f5
--- /dev/null
+++ b/usr.sbin/rarpd/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+PROG= rarpd
+MAN8= rarpd.8
+SRCS= rarpd.c
+
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rarpd/rarpd.8 b/usr.sbin/rarpd/rarpd.8
new file mode 100644
index 0000000..4422521
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.8
@@ -0,0 +1,117 @@
+.\" @(#) $Id$ (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 July 19, 1993
+.Dt RARPD 8
+.Os
+.Sh NAME
+.Nm rarpd
+.Nd reverse ARP daemon
+.Sh SYNOPSIS
+.Nm rarpd
+.Op Fl afsv
+.Op Ar interface
+.Sh DESCRIPTION
+.Nm Rarpd
+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.
+
+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).
+
+In normal operation,
+.Nm
+forks a copy of itself and runs in
+the background. Anomalies and errors are reported via
+.Xr syslog 3 .
+
+.Sh OPTIONS
+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 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 v
+Enable verbose sysloging.
+.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
+.Pp
+RFC 903: Finlayson, R.; Mann, T.; Mogul, J.C.; Theimer, M. Reverse Address
+Resolution Protocol. 1984 June; 4 p.
+.Sh AUTHORS
+.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
+.Nm Rarpd
+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 reccommended to
+configure
+.Pa /etc/host.conf
+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..ccc7eb1
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.c
@@ -0,0 +1,1011 @@
+/*
+ * 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.
+ */
+
+#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 */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: rarpd.c,v 1.19 1998/04/02 13:20:15 roberto Exp $";
+#endif /* not lint */
+
+/*
+ * rarpd - Reverse ARP Daemon
+ *
+ * Usage: rarpd -a [ -fsv ] [ hostname ]
+ * rarpd [ -fsv ] 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/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 <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined(SUNOS4) || defined(__FreeBSD__) /* XXX */
+#define HAVE_DIRENT_H
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+
+/* Cast a struct sockaddr to a structaddr_in */
+#define SATOSIN(sa) ((struct sockaddr_in *)(sa))
+
+#ifndef TFTP_DIR
+#define TFTP_DIR "/tftpboot"
+#endif
+
+#if BSD >= 199200
+#define ARPSECS (20 * 60) /* as per code in netinet/if_ether.c */
+#define REVARP_REQUEST ARPOP_REVREQUEST
+#define REVARP_REPLY ARPOP_REVREPLY
+#endif
+
+#ifndef ETHERTYPE_REVARP
+#define ETHERTYPE_REVARP 0x8035
+#define REVARP_REQUEST 3
+#define REVARP_REPLY 4
+#endif
+
+/*
+ * Map field names in ether_arp struct. What a pain in the neck.
+ */
+#ifdef SUNOS3
+#undef arp_sha
+#undef arp_spa
+#undef arp_tha
+#undef arp_tpa
+#define arp_sha arp_xsha
+#define arp_spa arp_xspa
+#define arp_tha arp_xtha
+#define arp_tpa arp_xtpa
+#endif
+
+#ifndef __GNUC__
+#define inline
+#endif
+
+/*
+ * The structure for each interface.
+ */
+struct if_info {
+ struct if_info *ii_next;
+ int ii_fd; /* BPF file descriptor */
+ u_long ii_ipaddr; /* IP address of this interface */
+ u_long ii_netmask; /* subnet or net mask */
+ u_char ii_eaddr[6]; /* Ethernet address of this interface */
+ char ii_ifname[sizeof(((struct ifreq *)0)->ifr_name) + 1];
+};
+
+/*
+ * 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 */
+int s; /* inet datagram socket */
+char *tftp_dir = TFTP_DIR; /* tftp directory */
+
+#ifndef __P
+#define __P(protos) ()
+#endif
+
+#if BSD < 199200
+extern char *malloc();
+extern void exit();
+#endif
+extern int ether_ntohost();
+
+void init __P((char *));
+void init_one __P((struct ifreq *, char *));
+char *intoa __P((u_long));
+u_long ipaddrtonetmask __P((u_long));
+char *eatoa __P((u_char *));
+int rarp_bootable __P((u_long));
+void rarp_loop __P((void));
+int rarp_open __P((char *));
+void rarp_process __P((struct if_info *, u_char *, u_int));
+void rarp_reply __P((struct if_info *, struct ether_header *, u_long, u_int));
+void update_arptab __P((u_char *, u_long));
+static void usage __P((void));
+
+static u_char zero[6];
+
+int sflag = 0; /* ignore /tftpboot */
+
+void
+main(argc, argv)
+ 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 syslogs.
+ */
+ openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "afsv")) != -1) {
+ switch (op) {
+ case 'a':
+ ++aflag;
+ break;
+
+ case 'f':
+ ++fflag;
+ break;
+
+ case 's':
+ ++sflag;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ ifname = argv[optind++];
+ hostname = ifname ? argv[optind] : NULL;
+ if ((aflag && ifname) || (!aflag && ifname == NULL))
+ usage();
+
+ if (aflag)
+ init(NULL);
+ else
+ init(ifname);
+
+ if (!fflag) {
+ if (daemon(0,0)) {
+ syslog(LOG_ERR, "cannot fork");
+ exit(1);
+ }
+ }
+ rarp_loop();
+}
+
+/*
+ * Add to the interface list.
+ */
+void
+init_one(ifrp, target)
+ register struct ifreq *ifrp;
+ register char *target;
+{
+ register struct if_info *ii;
+ register struct sockaddr_dl *ll;
+ int family;
+ struct ifreq ifr;
+
+ family = ifrp->ifr_addr.sa_family;
+ switch (family) {
+
+ case AF_INET:
+#if BSD >= 199100
+ case AF_LINK:
+#endif
+ (void)strncpy(ifr.ifr_name, ifrp->ifr_name,
+ sizeof(ifrp->ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ syslog(LOG_ERR,
+ "SIOCGIFFLAGS: %.*s: %m",
+ sizeof(ifrp->ifr_name), ifrp->ifr_name);
+ exit(1);
+ }
+ if ((ifr.ifr_flags & IFF_UP) == 0 ||
+ (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0)
+ return;
+ break;
+
+
+ default:
+ return;
+ }
+
+ /* Don't bother going any further if not the target interface */
+ if (target != NULL &&
+ strncmp(ifrp->ifr_name, target, sizeof(ifrp->ifr_name)) != 0)
+ return;
+
+ /* Look for interface in list */
+ for (ii = iflist; ii != NULL; ii = ii->ii_next)
+ if (strncmp(ifrp->ifr_name, ii->ii_ifname,
+ sizeof(ifrp->ifr_name)) == 0)
+ break;
+
+ /* Allocate a new one if not found */
+ if (ii == NULL) {
+ ii = (struct if_info *)malloc(sizeof(*ii));
+ if (ii == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+ bzero(ii, sizeof(*ii));
+ ii->ii_fd = -1;
+ (void)strncpy(ii->ii_ifname, ifrp->ifr_name,
+ sizeof(ifrp->ifr_name));
+ ii->ii_ifname[sizeof(ii->ii_ifname) - 1] = '\0';
+ ii->ii_next = iflist;
+ iflist = ii;
+ }
+
+ switch (family) {
+
+ case AF_INET:
+ if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "ipaddr SIOCGIFADDR: %s: %m",
+ ii->ii_ifname);
+ exit(1);
+ }
+ ii->ii_ipaddr = SATOSIN(&ifr.ifr_addr)->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "SIOCGIFNETMASK: %m");
+ exit(1);
+ }
+ ii->ii_netmask = SATOSIN(&ifr.ifr_addr)->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);
+#if BSD < 199100
+ /* Use BPF descriptor to get ethernet address. */
+ if (ioctl(ii->ii_fd, SIOCGIFADDR, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "eaddr SIOCGIFADDR: %s: %m",
+ ii->ii_ifname);
+ exit(1);
+ }
+ bcopy(&ifr.ifr_addr.sa_data[0], ii->ii_eaddr, 6);
+#endif
+ }
+ break;
+
+#if BSD >= 199100
+ case AF_LINK:
+ ll = (struct sockaddr_dl *)&ifrp->ifr_addr;
+ if (ll->sdl_type == IFT_ETHER)
+ bcopy(LLADDR(ll), ii->ii_eaddr, 6);
+ break;
+#endif
+ }
+}
+/*
+ * Initialize all "candidate" interfaces that are in the system
+ * configuration list. A "candidate" is up, not loopback and not
+ * point to point.
+ */
+void
+init(target)
+ char *target;
+{
+ register int n;
+ register struct ifreq *ifrp, *ifend;
+ register struct if_info *ii, *nii, *lii;
+ struct ifconf ifc;
+ struct ifreq ibuf[16];
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ ifc.ifc_len = sizeof ibuf;
+ ifc.ifc_buf = (caddr_t)ibuf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ (u_int)ifc.ifc_len < sizeof(struct ifreq)) {
+ syslog(LOG_ERR, "SIOCGIFCONF: %m");
+ exit(1);
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+ while (ifrp < ifend) {
+ init_one(ifrp, target);
+
+#if BSD >= 199100
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+ ifrp = (struct ifreq *)((char *)ifrp + n);
+#else
+ ++ifrp;
+#endif
+ }
+
+ /* 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)
+ syslog(LOG_DEBUG, "%s %s 0x%08x %s",
+ ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)),
+ ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr));
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: rarpd [-afsv] [interface]\n");
+ exit(1);
+}
+
+static int
+bpf_open()
+{
+ 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 < 0 && errno == EBUSY);
+
+ if (fd < 0) {
+ syslog(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.
+ */
+int
+rarp_open(device)
+ 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) < 0) {
+ syslog(LOG_ERR, "BIOCIMMEDIATE: %m");
+ exit(1);
+ }
+ (void)strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ syslog(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) < 0) {
+ syslog(LOG_ERR, "BIOCGDLT: %m");
+ exit(1);
+ }
+ if (dlt != DLT_EN10MB) {
+ syslog(LOG_ERR, "%s is not an ethernet", device);
+ exit(1);
+ }
+ /*
+ * Set filter program.
+ */
+ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) < 0) {
+ syslog(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(p, len)
+ 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)) {
+ syslog(LOG_ERR, "truncated request, got %d, expected %d",
+ len, 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) {
+ syslog(LOG_DEBUG, "request fails sanity check");
+ return 0;
+ }
+ if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
+ syslog(LOG_DEBUG, "ether/arp sender address mismatch");
+ return 0;
+ }
+ if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
+ syslog(LOG_DEBUG, "ether/arp target address mismatch");
+ return 0;
+ }
+ return 1;
+}
+
+#ifndef FD_SETSIZE
+#define FD_SET(n, fdp) ((fdp)->fds_bits[0] |= (1 << (n)))
+#define FD_ISSET(n, fdp) ((fdp)->fds_bits[0] & (1 << (n)))
+#define FD_ZERO(fdp) ((fdp)->fds_bits[0] = 0)
+#endif
+
+/*
+ * Loop indefinitely listening for RARP requests on the
+ * interfaces in 'iflist'.
+ */
+void
+rarp_loop()
+{
+ u_char *buf, *bp, *ep;
+ int cc, fd;
+ fd_set fds, listeners;
+ int bufsize, maxfd = 0;
+ struct if_info *ii;
+
+ if (iflist == NULL) {
+ syslog(LOG_ERR, "no interfaces");
+ exit(1);
+ }
+ if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) < 0) {
+ syslog(LOG_ERR, "BIOCGBLEN: %m");
+ exit(1);
+ }
+ buf = (u_char *)malloc((unsigned)bufsize);
+ if (buf == NULL) {
+ syslog(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) < 0) {
+ /* Don't choke when we get ptraced */
+ if (errno == EINTR)
+ continue;
+ syslog(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 < 0 && errno == EINTR)
+ goto again;
+#if defined(SUNOS3) || defined(SUNOS4)
+ /*
+ * Due to a SunOS bug, after 2^31 bytes, the
+ * file offset overflows and read fails with
+ * EINVAL. The lseek() to 0 will fix things.
+ */
+ if (cc < 0) {
+ if (errno == EINVAL &&
+ (long)(tell(fd) + bufsize) < 0) {
+ (void)lseek(fd, 0, 0);
+ goto again;
+ }
+ syslog(LOG_ERR, "read: %m");
+ exit(1);
+ }
+#endif
+
+ /* Loop through the packet(s) */
+#define bhp ((struct bpf_hdr *)bp)
+ bp = buf;
+ ep = bp + cc;
+ while (bp < ep) {
+ register 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.
+ */
+int
+rarp_bootable(addr)
+ u_long addr;
+{
+#ifdef HAVE_DIRENT_H
+ register struct dirent *dent;
+#else
+ register struct direct *dent;
+#endif
+ register DIR *d;
+ char ipname[9];
+ static DIR *dd = NULL;
+
+ (void)sprintf(ipname, "%08X", (unsigned int )ntohl(addr));
+
+ /*
+ * If directory is already open, rewind it. Otherwise, open it.
+ */
+ if ((d = dd) != NULL)
+ rewinddir(d);
+ else {
+ if (chdir(tftp_dir) == -1) {
+ syslog(LOG_ERR, "chdir: %s: %m", tftp_dir);
+ exit(1);
+ }
+ d = opendir(".");
+ if (d == NULL) {
+ syslog(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.
+ */
+u_long
+choose_ipaddr(alist, net, netmask)
+ u_long **alist;
+ u_long net;
+ u_long 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.
+ */
+void
+rarp_process(ii, pkt, len)
+ struct if_info *ii;
+ u_char *pkt;
+ u_int len;
+{
+ struct ether_header *ep;
+ struct hostent *hp;
+ u_long target_ipaddr;
+ char ename[256];
+
+ ep = (struct ether_header *)pkt;
+ /* should this be arp_tha? */
+ if (ether_ntohost(ename, &ep->ether_shost) != 0) {
+ syslog(LOG_ERR, "cannot map %s to name",
+ eatoa(ep->ether_shost));
+ return;
+ }
+
+ if ((hp = gethostbyname(ename)) == NULL) {
+ syslog(LOG_ERR, "cannot map %s to IP address", ename);
+ return;
+ }
+
+ /*
+ * Choose correct address from list.
+ */
+ if (hp->h_addrtype != AF_INET) {
+ syslog(LOG_ERR, "cannot handle non IP addresses for %s",
+ ename);
+ return;
+ }
+ target_ipaddr = choose_ipaddr((u_long **)hp->h_addr_list,
+ ii->ii_ipaddr & ii->ii_netmask,
+ ii->ii_netmask);
+ if (target_ipaddr == 0) {
+ syslog(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)
+ syslog(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).
+ */
+#if BSD >= 199200
+static struct sockaddr_inarp sin_inarp = {
+ sizeof(struct sockaddr_inarp), AF_INET
+};
+static struct sockaddr_dl sin_dl = {
+ sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6
+};
+static struct {
+ struct rt_msghdr rthdr;
+ char rtspace[512];
+} rtmsg;
+
+void
+update_arptab(ep, ipaddr)
+ u_char *ep;
+ u_long ipaddr;
+{
+ register int cc;
+ register struct sockaddr_inarp *ar, *ar2;
+ register struct sockaddr_dl *ll, *ll2;
+ register struct rt_msghdr *rt;
+ register int xtype, xindex;
+ static pid_t pid;
+ int r;
+ static seq;
+
+ r = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (r < 0) {
+ syslog(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) < 0 && errno != ESRCH) {
+ syslog(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 < 0) {
+ syslog(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).
+ */
+ syslog(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) < 0 && errno != EEXIST) {
+ syslog(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 < 0) {
+ syslog(LOG_ERR, "rtmsg add read: %m");
+ return;
+ }
+}
+#else
+void
+update_arptab(ep, ipaddr)
+ u_char *ep;
+ u_long ipaddr;
+{
+ struct arpreq request;
+ struct sockaddr_in *sin;
+
+ request.arp_flags = 0;
+ sin = (struct sockaddr_in *)&request.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = ipaddr;
+ request.arp_ha.sa_family = AF_UNSPEC;
+ bcopy((char *)ep, (char *)request.arp_ha.sa_data, 6);
+
+ if (ioctl(s, SIOCSARP, (caddr_t)&request) < 0)
+ syslog(LOG_ERR, "SIOCSARP: %m");
+}
+#endif
+
+/*
+ * 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.
+ */
+void
+rarp_reply(ii, ep, ipaddr, len)
+ struct if_info *ii;
+ struct ether_header *ep;
+ u_long ipaddr;
+ u_int len;
+{
+ 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)
+ syslog(LOG_ERR, "write: only %d of %d bytes written", n, len);
+ if (verbose)
+ syslog(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.
+ */
+u_long
+ipaddrtonetmask(addr)
+ u_long 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);
+ syslog(LOG_DEBUG, "unknown IP address class: %08X", addr);
+ return htonl(0xffffffff);
+}
+
+/*
+ * A faster replacement for inet_ntoa().
+ */
+char *
+intoa(addr)
+ u_long addr;
+{
+ register char *cp;
+ register u_int byte;
+ register 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;
+}
+
+char *
+eatoa(ea)
+ register 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);
+}
diff --git a/usr.sbin/repquota/Makefile b/usr.sbin/repquota/Makefile
new file mode 100644
index 0000000..eea7351
--- /dev/null
+++ b/usr.sbin/repquota/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= repquota
+MAN8= repquota.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/repquota/repquota.8 b/usr.sbin/repquota/repquota.8
new file mode 100644
index 0000000..220f652
--- /dev/null
+++ b/usr.sbin/repquota/repquota.8
@@ -0,0 +1,103 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt REPQUOTA 8
+.Os BSD 4.2
+.Sh NAME
+.Nm repquota
+.Nd summarize quotas for a file system
+.Sh SYNOPSIS
+.Nm repquota
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm repquota
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Repquota
+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 filesystems 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 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 filesystem 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 filesystem root with user quotas
+.It Pa quota.group
+at the filesystem 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
+command 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..617a3a2
--- /dev/null
+++ b/usr.sbin/repquota/repquota.c
@@ -0,0 +1,396 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)repquota.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: repquota.c,v 1.5 1997/10/13 11:05:07 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Quota report
+ */
+#include <sys/param.h>
+#include <sys/stat.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 <unistd.h>
+#include <utmp.h>
+
+#define max(a,b) ((a) >= (b) ? (a) : (b))
+
+char *qfname = QUOTAFILENAME;
+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();
+struct fileusage *addid();
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+int vflag; /* verbose */
+int aflag; /* all file systems */
+
+int hasquota __P((struct fstab *, int, char **));
+int oneof __P((char *, char *[], int));
+int repquota __P((struct fstab *, int, char *));
+char *timeprt __P((time_t));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct fstab *fs;
+ register struct passwd *pw;
+ register struct group *gr;
+ int gflag = 0, uflag = 0, errs = 0;
+ long i, argnum, done = 0;
+ char ch, *qfnp;
+
+ while ((ch = getopt(argc, argv, "aguv")) != -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) {
+ if (aflag)
+ gflag++;
+ uflag++;
+ }
+ if (gflag) {
+ setgrent();
+ while ((gr = getgrent()) != 0)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag) {
+ 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] [-u] -a",
+ " repquota [-v] [-g] [-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%7s",
+ 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)(dbtob(fup->fu_dqblk.dqb_curblocks) / 1024),
+ (u_long)(dbtob(fup->fu_dqblk.dqb_bsoftlimit) / 1024),
+ (u_long)(dbtob(fup->fu_dqblk.dqb_bhardlimit) / 1024),
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_btime) : "");
+ printf(" %6lu%6lu%6lu%7s\n",
+ fup->fu_dqblk.dqb_curinodes,
+ fup->fu_dqblk.dqb_isoftlimit,
+ 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)
+ return ("none");
+ seconds -= now;
+ minutes = (seconds + 30) / 60;
+ hours = (minutes + 30) / 60;
+ if (hours >= 36) {
+ sprintf(buf, "%lddays", (hours + 12) / 24);
+ return (buf);
+ }
+ if (minutes >= 60) {
+ sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60);
+ return (buf);
+ }
+ sprintf(buf, "%2ld", minutes);
+ return (buf);
+}
diff --git a/usr.sbin/rmt/Makefile b/usr.sbin/rmt/Makefile
new file mode 100644
index 0000000..166d458
--- /dev/null
+++ b/usr.sbin/rmt/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rmt
+MAN8= rmt.8
+
+# 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..8f9d0df
--- /dev/null
+++ b/usr.sbin/rmt/rmt.8
@@ -0,0 +1,218 @@
+.\" 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
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm rmt
+.Sh DESCRIPTION
+.Nm Rmt
+is a program used by the remote dump and restore programs
+in manipulating a magnetic tape drive through an interprocess
+communication connection.
+.Nm Rmt
+is normally started up with an
+.Xr rexec 3
+or
+.Xr rcmd 3
+call.
+.Pp
+The
+.Nm
+program 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 -filled -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 -filled -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
+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.
+.It Xo Sy C Ar device No \en
+.Xc
+Close the currently open device. The
+.Ar device
+specified is ignored.
+.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
+call.
+.Sm off
+.It Sy W Ar count No \en
+.Sm on
+Write data onto the open device.
+.Nm Rmt
+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.
+.Nm Rmt
+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
+call. The return value is the
+.Ar count
+parameter when the operation is successful.
+.ne 1i
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl
+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
+.Sm on
+.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
+command 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..436d6fb
--- /dev/null
+++ b/usr.sbin/rmt/rmt.c
@@ -0,0 +1,259 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: rmt.c,v 1.1 1998/05/15 12:09:06 hans Exp hans $";
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mtio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sgtty.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 __P((char *, int));
+void error __P((int));
+void getstring __P((char *));
+
+int
+main(argc, argv)
+ 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(0, &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)atol(count), 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(0, &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(1, resp, strlen(resp));
+ (void)write(1, 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);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(1, resp, strlen(resp));
+ (void)write(1, (char *)&mtget, sizeof (mtget));
+ 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(1, 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(0, cp+i, 1) != 1)
+ exit(0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf(record, size)
+ char *record;
+ int size;
+{
+
+ if (size <= maxrecsize)
+ return (record);
+ if (record != 0)
+ free(record);
+ record = malloc(size);
+ if (record == 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 (record);
+}
+
+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(1, resp, strlen(resp));
+}
diff --git a/usr.sbin/rndcontrol/Makefile b/usr.sbin/rndcontrol/Makefile
new file mode 100644
index 0000000..2cda5a8
--- /dev/null
+++ b/usr.sbin/rndcontrol/Makefile
@@ -0,0 +1,9 @@
+# $id$
+
+PROG= rndcontrol
+CFLAGS+= -Wall
+MAN4= random.4
+MAN8= rndcontrol.8
+MLINKS+= random.4 urandom.4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rndcontrol/random.4 b/usr.sbin/rndcontrol/random.4
new file mode 100644
index 0000000..b563e56
--- /dev/null
+++ b/usr.sbin/rndcontrol/random.4
@@ -0,0 +1,187 @@
+.\"
+.\" random.c -- A strong random number generator
+.\"
+.\" Version 0.92, last modified 21-Sep-95
+.\"
+.\" Copyright Theodore Ts'o, 1994, 1995. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce 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.
+.\"
+.\" ALTERNATIVELY, this product may be distributed under the terms of
+.\" the GNU Public License, in which case the provisions of the GPL are
+.\" required INSTEAD OF the above restrictions. (This clause is
+.\" necessary due to a potential bad interaction between the GPL and
+.\" the restrictions contained in a BSD-style copyright.)
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: random.4,v 1.5 1997/02/22 16:12:46 peter Exp $
+.\"
+.Dd October 21, 1995
+.Dt RANDOM 4 i386
+.Os
+.Sh NAME
+.Nm random ,
+.Nm urandom
+.Nd random number devices
+.Sh DESCRIPTION
+This device gathers environmental noise from device drivers, etc.,
+and returns good random numbers, suitable for cryptographic use.
+Besides the obvious cryptographic uses, these numbers are also good
+for seeding TCP sequence numbers, and other places where it is
+desirable to have numbers which are not only random, but hard to
+predict by an attacker.
+.Ss Theory of operation
+Computers are very predictable devices. Hence it is extremely hard
+to produce truly random numbers on a computer \(em as opposed to
+pseudo-random numbers, which can easily generated by using a
+algorithm. Unfortunately, it is very easy for attackers to guess
+the sequence of pseudo-random number generators, and for some
+applications this is not acceptable. So instead, we must try to
+gather "environmental noise" from the computer's environment, which
+must be hard for outside attackers to observe, and use that to
+generate random numbers. In a Unix environment, this is best done
+from inside the kernel.
+.Pp
+Sources of randomness from the environment include inter-keyboard
+timings, inter-interrupt timings from some interrupts, and other
+events which are both (a) non-deterministic and (b) hard for an
+outside observer to measure. Randomness from these sources are
+added to an "entropy pool", which is periodically mixed using the
+MD5 compression function in CBC mode. As random bytes are mixed
+into the entropy pool, the routines keep an
+.Em estimate
+of how many bits of randomness have been stored into the random number
+generator's internal state.
+.Pp
+When random bytes are desired, they are obtained by taking the MD5
+hash of a counter plus the contents of the "entropy pool". The
+reason for the MD5 hash is so that we can avoid exposing the
+internal state of random number generator. Although the MD5 hash
+does protect the pool, each random byte which is generated from
+the pool reveals some information which was derived from the
+internal state, and thus increases the amount of information an
+outside attacker has available to try to make some guesses about
+the random number generator's internal state. For this reason,
+the routine decreases its internal estimate of how many bits of
+"true randomness" are contained in the entropy pool as it outputs
+random numbers.
+.Pp
+If this estimate goes to zero, the routine can still generate random
+numbers; however it may now be possible for an attacker to analyze
+the output of the random number generator, and the MD5 algorithm,
+and thus have some success in guessing the output of the routine.
+Phil Karn (who devised this mechanism of using MD5 plus a counter
+to extract random numbers from an entropy pool) calls this
+"practical randomness", since in the worse case this is equivalent
+to hashing MD5 with a counter and an undisclosed secret. If MD5 is
+a strong cryptographic hash, this should be fairly resistant to attack.
+.Ss Exported interfaces \(em output
+There are three exported interfaces; the first is one designed to
+be used from within the kernel:
+.Pp
+.Bl -tag -width Pa -compact
+.It Pa void get_random_bytes(void *buf, int nbytes);
+.El
+.Pp
+This interface will return the requested number of random bytes,
+and place it in the requested buffer.
+.Pp
+The two other interfaces are two character devices
+.Pa /dev/random
+and
+.Pa /dev/urandom .
+The
+.Pa /dev/random
+device is suitable for use when very high quality randomness is desired
+(e.g. for key generation), as it will only return a maximum
+of the number of bits of randomness (as estimated by the random number
+generator) contained in the entropy pool.
+.Pp
+The
+.Pa /dev/urandom
+device does not have this limit, and will return as many bytes as are
+requested. As more and more random bytes are requested without giving
+time for the entropy pool to recharge, this will result in lower quality
+random numbers. For many applications, however, this is acceptable.
+.Ss Exported interfaces \(em input
+The two current exported interfaces for gathering environmental
+noise from the devices are:
+.Pp
+.Bl -tag -width Pa -compact
+.It Pa void add_keyboard_randomness(unsigned char scancode);
+.It Pa void add_interrupt_randomness(int irq);
+.El
+.Pp
+The first function uses the inter-keypress timing, as well as the
+scancode as random inputs into the "entropy pool".
+.Pp
+The second function uses the inter-interrupt timing as random
+inputs to the entropy pool. Note that not all interrupts are good
+sources of randomness! For example, the timer interrupts is not a
+good choice, because the periodicity of the interrupts is too
+regular, and hence predictable to an attacker. Disk interrupts are
+a better measure, since the timing of the disk interrupts are more
+unpredictable. The routines try to estimate how many bits of
+randomness a particular interrupt channel offers, by keeping track
+of the first and second order deltas in the interrupt timings.
+.Sh ACKNOWLEDGEMENTS
+The original core code was written by
+.An Theodore Ts'o ,
+and was intended
+for the Linux platform. This was ported to
+.Bx Free
+by
+.An Mark Murray ,
+who also wrote the
+.Xr rndcontrol 8
+utility.
+.Pp
+Ideas for constructing this random number generator were derived
+from the Pretty Good Privacy's random number generator, and from
+private discussions with Phil Karn. This design has been further
+modified by myself, so any flaws are solely my responsibility, and
+should not be attributed to the authors of PGP or to Phil.
+.Pp
+The code for MD5 transform was taken from Colin Plumb's
+implementation, which has been placed in the public domain. The
+MD5 cryptographic checksum was devised by Ronald Rivest, and is
+documented in RFC 1321, "The MD5 Message Digest Algorithm".
+.Pp
+Further background information on this topic may be obtained from
+RFC 1750, "Randomness Recommendations for Security", by Donald
+Eastlake, Steve Crocker, and Jeff Schiller.
+.Sh "SEE ALSO"
+.Xr rndcontrol 8
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /dev/random
+.It Pa /dev/urandom
+.El
+.Sh HISTORY
+The
+.Pa random ,
+.Pa urandom
+files appeared in
+.Fx 2.1.5 .
diff --git a/usr.sbin/rndcontrol/rndcontrol.8 b/usr.sbin/rndcontrol/rndcontrol.8
new file mode 100644
index 0000000..1dc7b48
--- /dev/null
+++ b/usr.sbin/rndcontrol/rndcontrol.8
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1995
+.\" Mark Murray. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mark Murray
+.\" and Theodore Ts'o
+.\" 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 MARK MURRAY AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL MARK MURRAY OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (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: rndcontrol.8,v 1.7 1997/10/13 11:08:45 charnier Exp $
+.\"
+.Dd October 20, 1995
+.Dt RNDCONTROL 8
+.Os FreeBSD 2
+.Sh NAME
+.Nm rndcontrol
+.Nd a utility for manipulating the /dev/random device
+.Sh SYNOPSIS
+.Nm rndcontrol
+.Op Fl q
+.Op Fl s Ar irq_no
+.Op Fl c Ar irq_no
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set which interrupts are used to help randomise
+the ``pool of entropy'' maintained by the kernel. The
+.Pa /dev/random
+and
+.Pa /dev/urandom
+devices are the user interface to this source of randomness.
+Any changes take effect immediately.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl q
+Turn off all output except errors.
+.It Fl s Ar n
+Allow IRQ
+.Ar n
+to be used as a source of randomness. This option may be repeated for
+more than one IRQ.
+.It Fl c Ar n
+Stop IRQ
+.Ar n
+from being used as a source of randomness. This option may be repeated for
+more than one IRQ.
+.El
+.Pp
+The default is to have no IRQ's being used.
+.Pp
+.Sh FILES
+.Bl -tag -width /dev/urandom -compact
+.It Pa /dev/random
+secure random device
+.It Pa /dev/urandom
+random device
+.El
+.Sh BUGS
+Sure to be some.
+.Sh "SEE ALSO"
+.Xr random 4
+.Sh AUTHORS
+.An Theodore Ts'o
+wrote the core code.
+.An Mark Murray
+ported this code to
+.Bx Free
+and wrote the support routines and constructed the man pages.
+.Sh HISTORY
+.Nm Rndcontrol
+first appeared in
+.Fx 2.1.5 .
diff --git a/usr.sbin/rndcontrol/rndcontrol.c b/usr.sbin/rndcontrol/rndcontrol.c
new file mode 100644
index 0000000..581bb0e
--- /dev/null
+++ b/usr.sbin/rndcontrol/rndcontrol.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1995
+ * Mark Murray. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Murray
+ * and Theodore Ts'o
+ * 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 MARK MURRAY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL MARK MURRAY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <machine/random.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rndcontrol [-q] [-s irq_no] [-c irq_no]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int verbose, ch, fd, result, i;
+ u_int16_t irq;
+
+ verbose = 1;
+
+ fd = open("/dev/random", O_RDONLY, 0);
+ if (fd == -1) {
+ warn("/dev/random");
+ return (1);
+ }
+ else {
+ while ((ch = getopt(argc, argv, "qs:c:")) != -1)
+ switch (ch) {
+ case 'q':
+ verbose = 0;
+ break;
+ case 's':
+ irq = (u_int16_t)atoi(optarg);
+ if (verbose)
+ printf("%s: setting irq %d\n", argv[0], irq);
+ result = ioctl(fd, MEM_SETIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ break;
+ case 'c':
+ irq = (u_int16_t)atoi(optarg);
+ if (verbose)
+ printf("%s: clearing irq %d\n", argv[0], irq);
+ result = ioctl(fd, MEM_CLEARIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ if (verbose) {
+ result = ioctl(fd, MEM_RETURNIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ printf("%s: interrupts in use:", argv[0]);
+ for (i = 0; i < 16; i++)
+ if (irq & (1 << i))
+ printf(" %d", i);
+ printf("\n");
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc) {
+ fprintf(stderr, "%s: unknown argument(s): ", argv[-optind]);
+ for (i = 0; i < argc; i++)
+ fprintf(stderr, " %s", argv[i]);
+ fprintf(stderr, "\n");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile
new file mode 100644
index 0000000..053f0a8
--- /dev/null
+++ b/usr.sbin/rpc.lockd/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.6 1998/03/06 14:30:45 bde Exp $
+
+PROG = rpc.lockd
+SRCS = nlm_prot_svc.c nlm_prot.h lockd.c procs.c
+MAN8 = rpc.lockd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CFLAGS+= -I.
+
+CLEANFILES= nlm_prot_svc.c nlm_prot.h
+
+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: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.lockd/handles.c b/usr.sbin/rpc.lockd/handles.c
new file mode 100644
index 0000000..8be7a29
--- /dev/null
+++ b/usr.sbin/rpc.lockd/handles.c
@@ -0,0 +1,61 @@
+/*
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "nlm_prot.h"
+
+/* ------------------------------------------------------------------------- */
+/*
+
+
+* need to find all fds in use by a host and free them when host crashes
+ (need not be efficient)
+
+* need to find fd corresponding to <inode, device>
+
+*/
+
+typedef struct fdinfo
+{
+ int fd; /* The file descriptor itself */
+ int ref_count; /* Count of hosts using the fd - fd is */
+ /* closed when this reaches zero */
+ ino_t inode_no; /* The inode number of this file. */
+ dev_t device; /* device on which the file lives. */
+ struct fdinfo *next; /* Chain of FdInfo structures */
+ struct fdinfo *prev;
+} FdInfo;
diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c
new file mode 100644
index 0000000..44a5e21
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -0,0 +1,107 @@
+/*
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+/* 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 procs.c */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include "lockd.h"
+
+void nlm_prog_1 __P((struct svc_req *, SVCXPRT *));
+void nlm_prog_3 __P((struct svc_req *, SVCXPRT *));
+static void usage __P((void));
+
+int debug_level = 0; /* Zero means no debugging syslog() calls */
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+
+ if (argc > 1)
+ {
+ if (strncmp(argv[1], "-d", 2))
+ usage();
+ if (argc > 2) debug_level = atoi(argv[2]);
+ else debug_level = atoi(argv[1] + 2);
+ /* Ensure at least some debug if -d with no specified level */
+ if (!debug_level) debug_level = 1;
+ }
+
+ (void)pmap_unset(NLM_PROG, NLM_VERS);
+ (void)pmap_unset(NLM_PROG, NLM_VERSX);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, udp)");
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, tcp)");
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, tcp)");
+
+ /* 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, "fork");
+ openlog("rpc.lockd", 0, LOG_DAEMON);
+ if (debug_level) syslog(LOG_INFO, "Starting, debug level %d", debug_level);
+ else syslog(LOG_INFO, "Starting");
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.lockd [-d [debuglevel]]\n");
+ exit(1);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h
new file mode 100644
index 0000000..39586bf
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.h
@@ -0,0 +1,44 @@
+/*
+ * 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 <stdio.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */
+#include "nlm_prot.h" /* The protocol we are implementing */
+
+
+/* global variables ------------------------------------------------------- */
+extern int debug_level;
diff --git a/usr.sbin/rpc.lockd/procs.c b/usr.sbin/rpc.lockd/procs.c
new file mode 100644
index 0000000..84ead97
--- /dev/null
+++ b/usr.sbin/rpc.lockd/procs.c
@@ -0,0 +1,592 @@
+/*
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <string.h>
+#include "lockd.h"
+
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+
+#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
+#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
+
+
+/* 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(char *fun_name, struct svc_req *req)
+{
+ struct sockaddr_in *addr;
+ struct hostent *host;
+ char hostname_buf[40];
+
+ addr = svc_getcaller(req->rq_xprt);
+ host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
+ if (host)
+ {
+ strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
+ hostname_buf[sizeof(hostname_buf) -1] = '\0';
+ }
+ else /* No hostname available - print raw address */
+ {
+ strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
+ }
+
+ syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
+}
+
+
+/* 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 in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
+static int clnt_cache_next_to_use = 0;
+
+static CLIENT *get_client(struct sockaddr_in *host_addr)
+{
+ CLIENT *client;
+ int sock_no;
+ struct timeval retry_time, time_now;
+ int i;
+
+ 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 && !memcmp(&clnt_cache_addr[i], &host_addr->sin_addr,
+ sizeof(struct in_addr)))
+ {
+ /* Found it! */
+ if (debug_level > 3) syslog(LOG_DEBUG, "Found CLIENT* in cache");
+ return (client);
+ }
+ }
+
+ /* 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;
+ }
+
+ /* Create the new client handle */
+
+ sock_no = RPC_ANYSOCK;
+ retry_time.tv_sec = 5;
+ retry_time.tv_usec = 0;
+ host_addr->sin_port = 0; /* Force consultation with portmapper */
+ client = clntudp_create(host_addr, NLM_PROG, NLM_VERS, retry_time, &sock_no);
+ if (!client)
+ {
+ syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
+ syslog(LOG_ERR, "Unable to return result to %s",
+ inet_ntoa(host_addr->sin_addr));
+ return NULL;
+ }
+
+ /* Success - update the cache entry */
+ clnt_cache_ptr[clnt_cache_next_to_use] = client;
+ clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
+ 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, &retry_time);
+
+ if (debug_level > 3) syslog(LOG_DEBUG, "Created CLIENT* for %s",
+ inet_ntoa(host_addr->sin_addr));
+ 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
+*/
+
+static void transmit_result(int opcode, nlm_res *result, struct svc_req *req)
+{
+ static char dummy;
+ struct sockaddr_in *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+
+ addr = svc_getcaller(req->rq_xprt);
+ if ((cli = get_client(addr)))
+ {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2) syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ 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(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ static nlm_testres res;
+ if (debug_level) log_from_addr("nlm_test", rqstp);
+
+ /* 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;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *nlm_test_msg_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ nlm_testres res;
+ static char dummy;
+ struct sockaddr_in *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+
+ if (debug_level) log_from_addr("nlm_test_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+
+ /* nlm_test has different result type to the other operations, so */
+ /* can't use transmit_result() in this case */
+ addr = svc_getcaller(rqstp->rq_xprt);
+ if ((cli = get_client(addr)))
+ {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres, &res, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2) syslog(LOG_DEBUG, "clnt_call returns %d\n", 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(nlm_lockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ 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 = nlm_granted;
+ return (&res);
+}
+
+void *nlm_lock_msg_1_svc(nlm_lockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+
+ if (debug_level) log_from_addr("nlm_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_LOCK_RES, &res, rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ Purpose: Cancel a blocked lock request
+ Returns: granted or denied
+ Notes:
+*/
+
+nlm_res *nlm_cancel_1_svc(nlm_cancargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ 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 = nlm_denied;
+ return (&res);
+}
+
+void *nlm_cancel_msg_1_svc(nlm_cancargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ 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 = nlm_denied;
+ transmit_result(NLM_CANCEL_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ Purpose: Release an existing lock
+ Returns: Always granted, unless during grace period
+ Notes: "no such lock" error condition is ignored, as the
+ protocol uses unreliable UDP datagrams, and may well
+ re-try an unlock that has already succeeded.
+*/
+
+nlm_res *nlm_unlock_1_svc(nlm_unlockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_unlock", rqstp);
+
+ res.stat.stat= nlm_granted;
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *nlm_unlock_msg_1_svc(nlm_unlockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_unlock_msg", rqstp);
+
+ res.stat.stat = nlm_granted;
+ res.cookie = arg->cookie;
+
+ transmit_result(NLM_UNLOCK_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ Client-side pseudo-RPCs for results. Note that for the client there
+ are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ version returns the results in the RPC result, and so the client
+ does not normally receive incoming RPCs.
+
+ The exception to this is nlm_granted(), which is genuinely an RPC
+ call from the server to the client - a 'call-back' in normal procedure
+ call terms.
+*/
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ Purpose: Receive notification that formerly blocked lock now granted
+ Returns: always success ('granted')
+ Notes:
+*/
+
+nlm_res *nlm_granted_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_granted", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *nlm_granted_msg_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ nlm_res res;
+ if (debug_level) log_from_addr("nlm_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_GRANTED_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ Purpose: Accept result from earlier nlm_test_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_test_res_1_svc(nlm_testres *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_test_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ Purpose: Accept result from earlier nlm_lock_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_lock_res_1_svc(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_lock_res", rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ Purpose: Accept result from earlier nlm_cancel_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_cancel_res_1_svc(nlm_res *arg, 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(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ Purpose: Accept result from earlier nlm_granted_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_granted_res_1_svc(nlm_res *arg, 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(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(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(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(nlm_notify *arg, struct svc_req *rqstp)
+{
+ static char dummy;
+
+ if (debug_level) log_from_addr("nlm_free_all", rqstp);
+ return (&dummy);
+}
+
+
diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8
new file mode 100644
index 0000000..4fe66b6
--- /dev/null
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -0,0 +1,95 @@
+.\" -*- 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.
+.\"
+.\"
+.Dd September 24, 1995
+.Dt RPC.LOCKD 8
+.Os
+.Sh NAME
+.Nm rpc.lockd
+.Nd NFS file locking daemon
+.Sh SYNOPSIS
+.Nm rpc.lockd
+.Op Fl d Op Ar debug_level
+.Sh DESCRIPTION
+.Nm Rpc.lockd
+is a daemon which provides file- and record-locking services in an NFS
+environment.
+.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. If debug_level is not specified,
+level 1 is assumed, giving one log line per protocol operation. Higher
+debug levels can be specified, causing display of operation arguments
+and internal operations of the daemon.
+.El
+.Pp
+Error conditions are logged to syslog, irrespective of the debug level,
+using log level LOG_ERR and facility LOG_DAEMON.
+.Pp
+The
+.Nm
+daemon 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/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 8 ,
+.Xr rpc.statd 8
+.Sh BUGS
+The current implementation provides only the server side of the protocol
+(ie. clients running other OS types can establish locks on a
+.Bx Free
+fileserver,
+but there is currently no means for a
+.Bx Free
+client to establish locks).
+.Pp
+Versions 1, 2 and 3 of the protocol are supported. However, only versions
+2 (Unix systems) and 3 (PC-NFS clients) seem to be in common use - the version
+1 support has not been tested due to the lack of version 1 clients against
+which to test.
+.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.lockd/test.c b/usr.sbin/rpc.lockd/test.c
new file mode 100644
index 0000000..01142a4
--- /dev/null
+++ b/usr.sbin/rpc.lockd/test.c
@@ -0,0 +1,366 @@
+#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";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
+
+/* 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)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+
+ 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..d0d5d7b
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.7 1998/03/06 07:00:25 bde Exp $
+
+PROG = rpc.statd
+SRCS = file.c sm_inter_svc.c sm_inter.h statd.c procs.c
+MAN8 = rpc.statd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CFLAGS+= -I.
+
+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..b8fc5cc
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,350 @@
+/*
+ * 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 <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 "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(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++;
+ }
+}
+
+/* xdr_stat_chge ----------------------------------------------------------- */
+/*
+ Purpose: XDR-encode structure of type stat_chge
+ Returns: TRUE if successful
+ Notes: This function is missing from librpcsvc, because the
+ sm_inter.x distributed by Sun omits the SM_NOTIFY
+ procedure used between co-operating statd's
+*/
+
+bool_t xdr_stat_chge(XDR *xdrs, stat_chge *objp)
+{
+ if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
+ {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->state))
+ {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+/* 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, xdr_stat_chge, &arg, 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..a33abd40
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,355 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 <netdb.h> /* for gethostbyname() */
+
+#include "statd.h"
+
+/* 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;
+
+ if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
+
+ if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
+ else
+ {
+ syslog(LOG_ERR, "invalid hostname to sm_stat: %s", 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 gethostbyname())
+*/
+
+struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ HostInfo *hp;
+ MonList *lp;
+
+ 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_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 (!gethostbyname(arg->mon_id.mon_name))
+ {
+ syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
+ }
+ else 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)
+{
+ 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_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)
+{
+ 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, struct svc_req *req)
+{
+ 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)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ static char dummy;
+ 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;
+ }
+ lp = hp->monList;
+ if (!lp) return (FALSE); /* 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;
+ }
+ 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, xdr_status, &tx_arg, 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..9226b4f
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,104 @@
+.\" -*- 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.
+.\"
+.\"
+.Dd September 19, 1995
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm rpc.statd
+.Op Fl d
+.Sh DESCRIPTION
+.Nm Rpc.statd
+is a daemon which co-operates with rpc.statd 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
+daemon 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.
+
+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.
+
+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..0c08892
--- /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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/* 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 <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.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();
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+ struct sigaction sa;
+
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "-d"))
+ usage();
+ debug = 1;
+ }
+
+ (void)pmap_unset(SM_PROG, SM_VERS);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP))
+ errx(1, "unable to register (SM_PROG, SM_VERS, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP))
+ errx(1, "unable to register (SM_PROG, SM_VERS, tcp)");
+ 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, int code, struct sigcontext *scp)
+{
+ 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..7b4e659
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,123 @@
+/*
+ * 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 "sm_inter.h"
+
+/* These pieces are missing from the distributed sm_inter.x, which */
+/* omits the SM_NOTIFY procedure used between cooperating rpc.statd's */
+
+#define SM_NOTIFY ((u_long)6)
+extern void *sm_notify_1();
+
+struct stat_chge
+{
+ char *mon_name;
+ int state;
+};
+typedef struct stat_chge stat_chge;
+bool_t xdr_stat_chge();
+
+/* ------------------------------------------------------------------------- */
+/*
+ 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(char * /*filename*/);
+extern void notify_hosts(void);
+extern void sync_file(void);
diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c
new file mode 100644
index 0000000..6a6a2ff
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,144 @@
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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.yppasswdd/Makefile b/usr.sbin/rpc.yppasswdd/Makefile
new file mode 100644
index 0000000..b4738c6
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/Makefile
@@ -0,0 +1,64 @@
+# $Id: Makefile,v 1.9 1998/04/26 16:30:09 bde Exp $
+
+PROG= rpc.yppasswdd
+SRCS= pw_copy.c pw_util.c 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
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv ${.CURDIR}/../../usr.bin/chpass \
+ ${.CURDIR}/../../libexec/ypxfr ${RPCDIR}
+
+MAN8= rpc.yppasswdd.8
+
+CFLAGS+= -I${.CURDIR}/../../usr.sbin/vipw -I${.CURDIR}/../../usr.sbin/ypserv \
+ -I${.CURDIR}/../../libexec/ypxfr -I${.CURDIR}/../../usr.bin/chpass \
+ -I${.CURDIR} -I.
+
+DPADD= ${LIBRPCSVC} ${LIBCRYPT}
+LDADD= -lrpcsvc -lcrypt
+
+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} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/yppwupdate \
+ ${DESTDIR}/usr/libexec/yppwupdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.yppasswdd/pw_copy.c b/usr.sbin/rpc.yppasswdd/pw_copy.c
new file mode 100644
index 0000000..653f5e8
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/pw_copy.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1990, 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
+#if 0
+static char sccsid[] = "@(#)pw_copy.c 8.4 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This module is used to copy the master password file, replacing a single
+ * record, by chpass(1) and passwd(1).
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pw_util.h>
+#include "yppasswdd_extern.h"
+
+int
+pw_copy(ffd, tfd, pw)
+ int ffd, tfd;
+ struct passwd *pw;
+{
+ FILE *from, *to;
+ int done;
+ char *p, buf[8192];
+ char uidstr[20];
+ char gidstr[20];
+ char chgstr[20];
+ char expstr[20];
+
+ snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%d", pw->pw_gid);
+ snprintf(chgstr, sizeof(chgstr), "%ld", pw->pw_change);
+ snprintf(expstr, sizeof(expstr), "%ld", pw->pw_expire);
+
+ if (!(from = fdopen(ffd, "r"))) {
+ pw_error(passfile, 1, 1);
+ return(-1);
+ }
+ if (!(to = fdopen(tfd, "w"))) {
+ pw_error(tempname, 1, 1);
+ return(-1);
+ }
+ for (done = 0; fgets(buf, sizeof(buf), from);) {
+ if (!strchr(buf, '\n')) {
+ yp_error("%s: line too long", passfile);
+ pw_error(NULL, 0, 1);
+ goto err;
+ }
+ if (done) {
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ if (!(p = strchr(buf, ':'))) {
+ yp_error("%s: corrupted entry", passfile);
+ pw_error(NULL, 0, 1);
+ goto err;
+ }
+ *p = '\0';
+ if (strcmp(buf, pw->pw_name)) {
+ *p = ':';
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ (void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ done = 1;
+ if (ferror(to))
+ goto err;
+ }
+ if (!done) {
+ if (allow_additions) {
+ (void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ } else {
+ yp_error("user \"%s\" not found in %s -- \
+NIS maps and password file possibly out of sync", pw->pw_name, passfile);
+ goto err;
+ }
+ }
+ if (ferror(to)) {
+err: pw_error(NULL, 1, 1);
+ (void)fclose(to);
+ (void)fclose(from);
+ return(-1);
+ }
+ (void)fclose(to);
+ (void)fclose(from);
+ return(0);
+}
diff --git a/usr.sbin/rpc.yppasswdd/pw_util.c b/usr.sbin/rpc.yppasswdd/pw_util.c
new file mode 100644
index 0000000..4b5de7c
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/pw_util.c
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 1990, 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
+#if 0
+static char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pw_util.h>
+#include "yppasswdd_extern.h"
+
+int pstat;
+pid_t pid;
+
+void
+pw_init()
+{
+ struct rlimit rlim;
+
+ /* 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_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTSTP, SIG_IGN);
+ (void)signal(SIGTTOU, SIG_IGN);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+}
+
+static int lockfd;
+
+int
+pw_lock()
+{
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ lockfd = open(passfile, O_RDONLY, 0);
+ if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) {
+ yp_error("%s: %s", passfile, strerror(errno));
+ return (-1);
+ }
+ if (flock(lockfd, LOCK_EX|LOCK_NB)) {
+ yp_error("%s: the password db file is busy", passfile);
+ return(-1);
+ }
+ return (lockfd);
+}
+
+int
+pw_tmp()
+{
+ static char path[MAXPATHLEN];
+ int fd;
+ char *p;
+
+ sprintf(path,"%s",passfile);
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "pw.XXXXXX");
+ if ((fd = mkstemp(path)) == -1) {
+ yp_error("%s: %s", path, strerror(errno));
+ return(-1);
+ }
+ tempname = path;
+ return (fd);
+}
+
+int
+pw_mkdb(username)
+char *username;
+{
+
+ yp_error("rebuilding the database...");
+ (void)fflush(stderr);
+ /* Temporarily turn off SIGCHLD catching */
+ install_reaper(0);
+ if (!(pid = vfork())) {
+ if(!username) {
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", tempname, NULL);
+ } else {
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-u", username,
+ tempname, NULL);
+ }
+ pw_error(_PATH_PWD_MKDB, 1, 1);
+ return(-1);
+ }
+ /* Handle this ourselves. */
+ reaper(-1);
+ /* Put the handler back. Foo. */
+ install_reaper(1);
+ if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) {
+ return (-1);
+ }
+ yp_error("done");
+ return (0);
+}
+
+void
+pw_error(name, err, eval)
+ char *name;
+ int err, eval;
+{
+ if (err && name != NULL)
+ yp_error("%s", name);
+
+ yp_error("%s: unchanged", passfile);
+ (void)unlink(tempname);
+}
diff --git a/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8 b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
new file mode 100644
index 0000000..4047417
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
@@ -0,0 +1,327 @@
+.\" 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.
+.\"
+.\" $Id: rpc.yppasswdd.8,v 1.8 1997/10/13 11:18:46 charnier Exp $
+.\"
+.Dd February 8, 1996
+.Dt RPC.YPPASSWDD 8
+.Os
+.Sh NAME
+.Nm rpc.yppasswdd
+.Nd "server for updating NIS passwords"
+.Sh SYNOPSIS
+.Nm rpc.yppasswdd
+.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
+daemon allows users to change their NIS passwords and certain
+other information using the
+.Xr yppasswd 1
+and
+.Xr ypchpass 1
+commands.
+.Nm Rpc.yppasswdd
+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
+server 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
+server 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
+.Bx Free
+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 peformed over
+the network.
+.Pp
+The
+.Nm
+daemon 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
+server 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
+.Bx Free
+.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
+seperate template files for each domain. For example, if a server
+supports three domains,
+.Pa foo ,
+.Pa bar ,
+and
+.Pa baz ,
+there should be three seperate 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 rpc.yppasswdd .
+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 sucessfully 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 rpc.yppasswdd .
+.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 4 ,
+.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..9f50671
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
@@ -0,0 +1,72 @@
+/*
+ * 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
+%#ifndef lint
+%static const char rcsid[] =
+% "$Id: yppasswd_private.x,v 1.4 1997/07/29 15:43:18 wpaul Exp $";
+%#endif /* not lint */
+#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..76bacbc
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ *
+ * $Id: yppasswdd_extern.h,v 1.2 1997/07/28 18:31:11 wpaul Exp $
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#include <paths.h>
+#include <rpc/rpc.h>
+#include <pwd.h>
+#include <err.h>
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+#include "ypxfr_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 __P(( void ));
+extern void yppasswdprog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern void master_yppasswdprog_1 __P(( struct svc_req *,
+ register SVCXPRT * ));
+extern void reaper __P(( int ));
+extern void install_reaper __P(( int ));
+extern int pw_copy __P(( int, int, struct passwd * ));
+extern int pw_lock __P(( void ));
+extern int pw_mkdb __P(( char * ));
+extern int pw_tmp __P(( void ));
+extern void pw_init __P(( void ));
+extern char *ok_shell __P (( char * ));
+extern char *passfile;
+extern char *passfile_default;
+extern char *tempname;
+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 __P((void));
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
new file mode 100644
index 0000000..307abfd
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
@@ -0,0 +1,337 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id: yppasswdd_main.c,v 1.12 1997/10/13 11:18:50 charnier Exp $";
+#endif /* not lint */
+
+#include "yppasswd.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.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 <err.h>
+#include <errno.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd_private.h"
+#include "ypxfr_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
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+char *progname = "rpc.yppasswdd";
+char *yp_dir = _PATH_YP;
+char *passfile_default = _PATH_YP "master.passwd";
+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(sig)
+ int sig;
+{
+ svc_unregister(YPPASSWDPROG, YPPASSWDVERS);
+ svc_unregister(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS);
+ unlink(sockname);
+ exit(0);
+}
+
+static void reload(sig)
+ int sig;
+{
+ load_securenets();
+}
+
+static void
+closedown(int sig)
+{
+ 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()
+{
+ 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(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+ char *mastername;
+ char myname[MAXHOSTNAMELEN + 2];
+ extern int errno;
+ 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);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+ } else {
+ if (!debug) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ }
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPPASSWDPROG, YPPASSWDVERS);
+ (void) pmap_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS);
+ unlink(sockname);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ yp_error("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswdprog_1, proto)) {
+ yp_error("unable to register (YPPASSWDPROG, YPPASSWDVERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ yp_error("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswdprog_1, proto)) {
+ yp_error("unable to register (YPPASSWDPROG, YPPASSWDVERS, tcp).");
+ exit(1);
+ }
+ }
+
+ unlink(sockname);
+ transp = svcunix_create(sock, 0, 0, sockname);
+ if (transp == NULL) {
+ yp_error("cannot create AF_LOCAL service.");
+ exit(1);
+ }
+ if (!svc_register(transp, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, master_yppasswdprog_1, 0)) {
+ yp_error("unable to register (MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, unix).");
+ exit(1);
+ }
+ /* 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);
+ }
+ /* set up resource limits and block signals */
+ pw_init();
+
+ /* except SIGCHLD, which we need to catch */
+ install_reaper(1);
+ signal(SIGTERM, (SIG_PF) terminate);
+
+ signal(SIGHUP, (SIG_PF) reload);
+
+ 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..5f57ce8
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
@@ -0,0 +1,873 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id: yppasswdd_server.c,v 1.14 1997/10/13 11:18:53 charnier Exp $";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h>
+#include <db.h>
+#include <pwd.h>
+#include <errno.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd.h"
+#include "yppasswd_private.h"
+
+char *tempname;
+
+void reaper(sig)
+ int sig;
+{
+ extern pid_t pid;
+ extern int pstat;
+ int st;
+ int saved_errno;
+
+ saved_errno = errno;
+
+ if (sig > 0) {
+ if (sig == SIGCHLD)
+ while(wait3(&st, WNOHANG, NULL) > 0) ;
+ } else {
+ pid = waitpid(pid, &pstat, 0);
+ }
+
+ errno = saved_errno;
+ return;
+}
+
+void install_reaper(on)
+ int on;
+{
+ if (on) {
+ signal(SIGCHLD, reaper);
+ } else {
+ signal(SIGCHLD, SIG_DFL);
+ }
+ return;
+}
+
+static struct passwd yp_password;
+
+static void copy_yp_pass(p, x, m)
+char *p;
+int x, m;
+{
+ register char *t, *s = p;
+ static char *buf;
+
+ yp_password.pw_fields = 0;
+
+ buf = (char *)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(arg)
+ char *arg;
+{
+ int 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(opw, npw)
+ struct passwd *opw;
+ 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(opw, npw)
+ 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 (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 (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(pw)
+ 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 == pw->pw_uid &&
+ yp_password.pw_gid == 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 int update_inplace(pw, domain)
+ 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 *maps[] = { "master.passwd.byname", "master.passwd.byuid",
+ "passwd.byname", "passwd.byuid" };
+
+ 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" };
+ char *ptr = NULL;
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
+
+ for (i = 0; i < 4; i++) {
+
+ if (i % 2) {
+ snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
+ key.data = (char *)&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,
+ ptr - (char *)data.data, 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 ne 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);
+}
+
+static char *yp_mktmpnam()
+{
+ static char path[MAXPATHLEN];
+ char *p;
+
+ sprintf(path,"%s",passfile);
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "yppwtmp.XXXXXX");
+ return(mktemp(path));
+}
+
+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 *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((char *)rqhost, (char *)&clntaddr,
+ sizeof(struct sockaddr_in)) &&
+ t_test.tv_sec > t_saved.tv_sec &&
+ t_test.tv_sec - t_saved.tv_sec < 300) {
+
+ bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
+ bzero((char *)&t_saved, sizeof(struct timeval));
+ return(NULL);
+ }
+
+ bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
+ 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;
+ }
+
+ /* Step 5: make a new password file with the updated info. */
+
+ if ((pfd = pw_lock()) < 0) {
+ return (&result);
+ }
+ if ((tfd = pw_tmp()) < 0) {
+ return (&result);
+ }
+
+ if (pw_copy(pfd, tfd, &yp_password)) {
+ yp_error("failed to created updated password file -- \
+cleaning up and bailing out");
+ unlink(tempname);
+ return(&result);
+ }
+
+ passfile_hold = yp_mktmpnam();
+ rename(passfile, passfile_hold);
+ if (strcmp(passfile, _PATH_MASTERPASSWD)) {
+ rename(tempname, passfile);
+ } else {
+ if (pw_mkdb(argp->newpw.pw_name) < 0) {
+ yp_error("pwd_mkdb failed");
+ return(&result);
+ }
+ }
+
+ 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", NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, 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):",
+ argp->newpw.pw_name,
+ argp->newpw.pw_uid);
+
+ 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);
+}
+
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+/*
+ * 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;
+ int rval = 0;
+ DBT key, data;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ struct sockaddr_in *rqhost;
+ struct cmessage *cm;
+ SVCXPRT *transp;
+
+ result = 1;
+
+ /*
+ * NO AF_INET CONNETCIONS ALLOWED!
+ */
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+ 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(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ transp = rqstp->rq_xprt;
+
+ if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
+ transp->xp_verf.oa_base == NULL ||
+ transp->xp_verf.oa_flavor != AUTH_UNIX) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ cm = (struct cmessage *)transp->xp_verf.oa_base;
+ if (cm->cmsg.cmsg_type != SCM_CREDS) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (cm->cmcred.cmcred_euid) {
+ yp_error("caller euid is %d, expecting 0 -- rejecting request",
+ cm->cmcred.cmcred_euid);
+ 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;
+ }
+
+ if ((pfd = pw_lock()) < 0) {
+ return (&result);
+ }
+ if ((tfd = pw_tmp()) < 0) {
+ return (&result);
+ }
+
+ if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw)) {
+ yp_error("failed to created updated password file -- \
+cleaning up and bailing out");
+ unlink(tempname);
+ return(&result);
+ }
+
+ passfile_hold = yp_mktmpnam();
+ rename(passfile, passfile_hold);
+ if (strcmp(passfile, _PATH_MASTERPASSWD)) {
+ rename(tempname, passfile);
+ } else {
+ if (pw_mkdb(argp->newpw.pw_name) < 0) {
+ yp_error("pwd_mkdb failed");
+ return(&result);
+ }
+ }
+
+ 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", NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, 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..acec9f0
--- /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.
+#
+# $Id$
+#
+
+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..bc5c97a
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,36 @@
+# $Id: Makefile,v 1.4 1998/03/06 14:40:42 bde Exp $
+
+PROG= rpc.ypupdated
+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
+
+.PATH: ${.CURDIR}/../ypserv ${.CURDIR}/../../libexec/ypxfr
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+NOMAN= yes
+
+CFLAGS+= -I${.CURDIR}/../ypserv -I.
+CFLAGS+= -I${.CURDIR}/../../libexec/ypxfr
+
+#CFLAGS+= -DYP
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypupdate_prot_svc.c ypupdate_prot.h
+
+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..5b11d83
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,363 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 __P(( 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.
+ */
+mapupdate(requester, mapname, op, keylen, key, datalen, data)
+ 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
+_openchild(command, fto, ffrom)
+ 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;
+ }
+#ifdef VFORK
+ switch (pid = vfork()) {
+#else
+ switch (pid = fork()) {
+#endif
+ 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, 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(path)
+ char *path;
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+#ifdef foo
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+extern char *malloc();
+#endif
+
+static int match __P(( 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(name, filename, op, keylen, key, datalen, data)
+ char *name; /* Name of the requestor */
+ char *filename;
+ u_int op;
+ u_int keylen; /* Not used */
+ char *key;
+ u_int datalen; /* Not used */
+ 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(line, name)
+ 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..304db74
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
@@ -0,0 +1,71 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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(dbp,key)
+ 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..db84c77
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/fcntl.h>
+
+#include <stdio.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(map, domain)
+ char *map;
+ char *domain;
+{
+ int pid;
+
+ switch((pid = fork())) {
+ case 0:
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, map, domain, 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(netname, map, op, keylen, keyval, datlen, datval)
+ 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..a06247f
--- /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.
+#
+# $Id: yppwupdate,v 1.3 1996/06/05 06:13:09 wpaul Exp $
+#
+
+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..28ea7ac
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
@@ -0,0 +1,29 @@
+#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 __P(( struct svc_req *, register SVCXPRT * ));
+extern int localupdate __P(( char *, char *, u_int, u_int, char *, u_int, char * ));
+extern int ypmap_update __P(( char *, char *, u_int, u_int, char *, u_int, char * ));
+extern int yp_del_record __P(( 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..854f611
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_main.c
@@ -0,0 +1,289 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "ypupdate_prot.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#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 <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, msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, 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()
+{
+#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(sig)
+ 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()
+{
+ fprintf(stderr, "rpc.ypupdatedd [-p path]\n");
+ exit(0);
+}
+
+int
+main(argc, argv)
+ 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")) != EOF) {
+ 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..4741c56
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c
@@ -0,0 +1,235 @@
+/*
+ * 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
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_des.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(svcreq)
+ 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(args, svcreq)
+ 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(args, svcreq)
+ 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(args, svcreq)
+ 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(args, svcreq)
+ 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..c9ae441
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/Makefile
@@ -0,0 +1,37 @@
+# $Id: Makefile,v 1.6 1998/03/06 14:34:47 bde Exp $
+
+PROG= rpc.ypxfrd
+SRCS= ypxfrd_svc.c ypxfrd.h ypxfrd_server.c yp_error.c \
+ yp_access.c ypxfrd_main.c
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+MAN8= rpc.ypxfrd.8
+
+CFLAGS+= -I. -DXFRBLOCKSIZE=65535
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypxfrd_svc.c ypxfrd.h
+
+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..37d15d6
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
@@ -0,0 +1,140 @@
+.\" 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.
+.\"
+.\" $Id: rpc.ypxfrd.8,v 1.6 1997/10/13 11:22:35 charnier Exp $
+.\"
+.Dd June 2, 1996
+.Dt RPC.YPXFRD 8
+.Os
+.Sh NAME
+.Nm rpc.ypxfrd
+.Nd "NIS map transfer server"
+.Sh SYNOPSIS
+.Nm rpc.ypxfrd
+.Op Fl p Ar path
+.Sh DESCRIPTION
+The
+.Nm
+daemon is used to speed up the distribtion 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
+server 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
+server 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 4 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+The
+.Bx Free
+.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
+.Bx Free
+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..3a1f154
--- /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.
+ *
+ * $Id$
+ */
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#ifndef XFRBLOCKSIZE
+#define XFRBLOCKSIZE YPXFRBLOCK
+#endif
+
+extern int forked;
+extern int children;
+extern void load_securenets __P(( void ));
+extern void yp_error __P((const char *, ...));
+extern int yp_access __P((const char *, const struct svc_req * ));
+extern int yp_validdomain __P((const char * ));
+extern char *yp_dir;
+extern void ypxfrd_freebsd_prog_1 __P(( 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..ef24d33
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
@@ -0,0 +1,304 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id: ypxfrd_main.c,v 1.6 1997/10/13 11:22:37 charnier Exp $";
+#endif /* not lint */
+
+#include "ypxfrd.h"
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#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, msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, 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()
+{
+#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(sig)
+ 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()
+{
+ fprintf(stderr, "usage: rpc.ypxfrd [-p path]\n");
+ exit(0);
+}
+
+int
+main(argc, argv)
+ 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("/dev/console", 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ i = open("/dev/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..a8fe278
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
@@ -0,0 +1,147 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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((char *)&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((char *)&buf, O_RDONLY)) == -1) {
+ result.xfr_u.xfrstat = XFR_READ_ERR;
+ return(&result);
+ }
+
+ /* Start sending the file. */
+
+ svc_sendreply(rqstp->rq_xprt, xdr_my_xfr, (char *)&result);
+
+ close(fp);
+
+ return (NULL);
+}
diff --git a/usr.sbin/rtprio/Makefile b/usr.sbin/rtprio/Makefile
new file mode 100644
index 0000000..c023e18
--- /dev/null
+++ b/usr.sbin/rtprio/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.5 (Berkeley) 5/11/90
+# $Id$
+
+BINDIR=/usr/sbin
+PROG= rtprio
+LINKS= ${BINDIR}/rtprio ${BINDIR}/idprio
+MLINKS= rtprio.1 idprio.1
+MAN1= rtprio.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..b89ab50
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.1
@@ -0,0 +1,205 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: rtprio.1,v 1.14 1998/03/12 01:29:12 jkh Exp $
+.\"
+.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
+.Ar [-]pid
+.Nm [id|rt]prio
+.Ar priority
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Ar priority
+.Ar -pid
+.Nm [id|rt]prio
+.Fl t
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Fl t
+.Ar -pid
+.Sh DESCRIPTION
+.Nm Rtprio
+is used for controlling realtime process scheduling.
+.Pp
+.Nm Idprio
+is used for controlling idletime process scheduling, and can be called
+with the same options as
+.Nm rtprio .
+.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
+.Nm Rtprio
+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 RETURN VALUE
+If
+.Nm
+execute a command, the exit value is that of the command executed.
+In all other cases,
+.Nm
+exits with 0 for 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 8
+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
+.Bx Free
+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
+.Bx Free
+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 Henrik Vestergaard Draboel Aq hvd@terry.ping.dk
+is the original author. This
+implementation in
+.Bx Free
+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..b1f4edc
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: rtprio.c,v 1.6 1998/05/19 20:52:31 dufault Exp $";
+#endif /* not lint */
+
+#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 (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/rwhod/Makefile b/usr.sbin/rwhod/Makefile
new file mode 100644
index 0000000..0f884f5
--- /dev/null
+++ b/usr.sbin/rwhod/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rwhod
+MAN8= rwhod.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8
new file mode 100644
index 0000000..8050ca9
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.8
@@ -0,0 +1,224 @@
+.\" 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
+.\"
+.Dd December 11, 1993
+.Dt RWHOD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rwhod
+.Nd system status server
+.Sh SYNOPSIS
+.Nm rwhod
+.Op Fl i
+.Op Fl l
+.Op Fl m Op Ar ttl
+.Sh DESCRIPTION
+.Nm Rwhod
+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
+.Nm Rwhod
+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
+.Fl i
+option enables insecure mode, which causes
+.Nm
+to ignore the source port on incoming packets.
+.Pp
+The
+.Fl l
+option enables listen mode, which causes
+.Nm
+to not broadcast any information.
+This allows you to monitor other machines'
+.Nm
+information, without broadcasting your own.
+.Pp
+The
+.Fl m
+option causes
+.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 than
+.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 rwhod Ns s ,
+but, if multicasting is used,
+those old
+.Nm rwhod Ns s
+won't hear the reports generated by this program.
+.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.
+.Nm Rwhod
+performs an
+.Xr nlist 3
+on
+.Pa /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
+command 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..6b6899f
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.c
@@ -0,0 +1,736 @@
+/*
+ * 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 */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: rwhod.c,v 1.8 1998/12/17 11:05:57 des Exp $";
+#endif /* not lint */
+
+#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 <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 <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 multicast_mode = NO_MULTICAST;
+int multicast_scope;
+struct sockaddr_in multicast_addr = { sizeof multicast_addr, AF_INET };
+
+/*
+ * 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 (sizeof(mywd) - sizeof(mywd.wd_we))
+
+void run_as __P((uid_t *, gid_t *));
+int configure __P((int));
+void getboottime __P((int));
+void onalrm __P((int));
+void quit __P((char *));
+void rt_xaddrs __P((caddr_t, caddr_t, struct rt_addrinfo *));
+int verify __P((char *, int));
+static void usage __P((void));
+#ifdef DEBUG
+char *interval __P((int, char *));
+void Sendto __P((int, const void *, size_t, int,
+ const struct sockaddr *, int));
+#define sendto Sendto
+#endif
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sockaddr_in from;
+ struct stat st;
+ char path[64];
+ int on = 1;
+ char *cp;
+ struct sockaddr_in sin;
+ 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
+ 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, "rwhod: udp/who: unknown service\n");
+ exit(1);
+ }
+ if (chdir(_PATH_RWHODIR) < 0) {
+ syslog(LOG_ERR, "rwhod: %s: %m\n", _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(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = sp->s_port;
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 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);
+
+ 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 from port",
+ ntohs(from.sin_port));
+ 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 %x",
+ 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((time_t *)&wd.wd_recvtime);
+ (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] [-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;
+{
+ 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(0));
+ 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;
+{
+ 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(tm.tv_sec);
+}
+
+void
+quit(msg)
+ char *msg;
+{
+ syslog(LOG_ERR, msg);
+ exit(1);
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+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;
+ ADVANCE(cp, sa);
+ }
+}
+
+/*
+ * Figure out device configuration and select
+ * networks which deserve status information.
+ */
+int
+configure(s)
+ int s;
+{
+ 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(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+ return(0);
+ }
+ ttl = multicast_scope;
+ if (setsockopt(s, 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_POINTOPOINT)) == 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 = 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..b51840a
--- /dev/null
+++ b/usr.sbin/sa/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= sa
+MAN8= sa.8
+SRCS= main.c pdb.c usrdb.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sa/extern.h b/usr.sbin/sa/extern.h
new file mode 100644
index 0000000..3ec7276
--- /dev/null
+++ b/usr.sbin/sa/extern.h
@@ -0,0 +1,100 @@
+/*
+ * 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.
+ *
+ * $Id$
+ */
+
+#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) __P((const DBT *, const DBT *));
+
+/* external functions in sa.c */
+int main __P((int, char **));
+
+/* external functions in pdb.c */
+int pacct_init __P((void));
+void pacct_destroy __P((void));
+int pacct_add __P((const struct cmdinfo *));
+int pacct_update __P((void));
+void pacct_print __P((void));
+
+/* external functions in usrdb.c */
+int usracct_init __P((void));
+void usracct_destroy __P((void));
+int usracct_add __P((const struct cmdinfo *));
+int usracct_update __P((void));
+void usracct_print __P((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 int 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..99b1d8a
--- /dev/null
+++ b/usr.sbin/sa/main.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
+ All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * sa: system accounting
+ */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int acct_load __P((char *, int));
+static u_quad_t decode_comp_t __P((comp_t));
+static int cmp_comm __P((const char *, const char *));
+static int cmp_usrsys __P((const DBT *, const DBT *));
+static int cmp_avgusrsys __P((const DBT *, const DBT *));
+static int cmp_dkio __P((const DBT *, const DBT *));
+static int cmp_avgdkio __P((const DBT *, const DBT *));
+static int cmp_cpumem __P((const DBT *, const DBT *));
+static int cmp_avgcpumem __P((const DBT *, const DBT *));
+static int cmp_calls __P((const DBT *, const DBT *));
+static void usage __P((void));
+
+int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
+int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
+int cutoff = 1;
+
+static char *dfltargv[] = { _PATH_ACCT };
+static int dfltargc = (sizeof dfltargv/sizeof(char *));
+
+/* default to comparing by sum of user + system time */
+cmpf_t sa_cmp = cmp_usrsys;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char ch;
+ int error;
+
+ 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':
+ /* seperate 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 < 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 < 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 %12quk mem %12qu io %s\n",
+ ci.ci_uid,
+ (ci.ci_utime + ci.ci_stime) / (double) AHZ,
+ ci.ci_mem, 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ 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..861cfec
--- /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.
+ *
+ * $Id$
+ */
+
+#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..8576915
--- /dev/null
+++ b/usr.sbin/sa/pdb.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.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 (%qu) -- ", cip->ci_comm, 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("%8qu ", 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("%10qutio ", cip->ci_io);
+ else
+ printf("%8.0favio ", cip->ci_io / c);
+
+ if (Kflag)
+ printf("%10quk*sec ", cip->ci_mem);
+ else
+ printf("%8.0fk ", cip->ci_mem / t);
+
+ printf(" %s\n", cip->ci_comm);
+}
diff --git a/usr.sbin/sa/sa.8 b/usr.sbin/sa/sa.8
new file mode 100644
index 0000000..e79217e
--- /dev/null
+++ b/usr.sbin/sa/sa.8
@@ -0,0 +1,238 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: sa.8,v 1.8 1997/10/15 06:41:18 charnier Exp $
+.\"
+.Dd February 25, 1994
+.Dt SA 8
+.Os
+.Sh NAME
+.Nm sa
+.Nd print system accounting statistics
+.Sh SYNOPSIS
+.Nm sa
+.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
+.Nm Sa
+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.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.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 than 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..d3a0654
--- /dev/null
+++ b/usr.sbin/sa/usrdb.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.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 %d 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 %d != expected record number %d",
+ 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 %d 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 *ui;
+ double t;
+ int rv;
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+
+ while (rv == 0) {
+ ui = (struct userinfo *) data.data;
+
+ printf("%-8s %9qu ",
+ user_from_uid(ui->ui_uid, 0), 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("%12qu%s", ui->ui_io / ui->ui_calls, "avio");
+ else
+ printf("%12qu%s", ui->ui_io, "tio");
+
+ /* t is always >= 0.0001; see above */
+ if (kflag)
+ printf("%12qu%s", ui->ui_mem / t, "k");
+ else
+ printf("%12qu%s", ui->ui_mem, "k*sec");
+
+ printf("\n");
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+ }
+}
+
+static int
+uid_compare(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..0be2955
--- /dev/null
+++ b/usr.sbin/sade/Makefile
@@ -0,0 +1,85 @@
+PROG= sysinstall
+MAN8= sysinstall.8
+
+BINDIR=/stand
+NOSHARED=YES
+
+CLEANFILES+= makedevs.c rtermcap rtermcap.tmp dumpnlist
+CLEANFILES+= keymap.tmp keymap.h
+
+.PATH: ${.CURDIR}/../disklabel ${.CURDIR}/../../usr.bin/cksum
+
+SRCS= anonFTP.c cdrom.c command.c config.c devices.c kget.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c floppy.c \
+ ftp.c globals.c index.c install.c installUpgrade.c keymap.c \
+ label.c lndir.c main.c makedevs.c media.c menus.c misc.c mouse.c \
+ msg.c network.c nfs.c options.c package.c register.c system.c \
+ tape.c tcpip.c termcap.c ufs.c user.c variable.c wizard.c \
+ keymap.h
+
+CFLAGS+= -Wall -I${.CURDIR}/../../gnu/lib/libdialog -I${.OBJDIR}
+CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -DUC_PRIVATE -DKERN_NO_SYMBOLS
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lmytinfo -lutil -ldisk -lftpio
+
+makedevs.c: Makefile rtermcap keymap.h
+ rm -f makedevs.tmp
+ echo '#include <sys/types.h>' > makedevs.tmp
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.tmp
+.if ${MACHINE_ARCH} != "alpha"
+ file2c 'u_char boot0[] = {' '};' < /boot/boot0 >> makedevs.tmp
+.endif
+ mv makedevs.tmp makedevs.c
+
+rtermcap: ${.CURDIR}/rtermcap.c
+ ${CC} -o rtermcap ${.CURDIR}/rtermcap.c -ltermcap
+
+
+KEYMAPS= be.iso br275.iso danish.iso finnish.iso fr.iso \
+ german.iso hr.iso hu.iso2.101keys it.iso icelandic.iso jp.106 \
+ norwegian.iso pl_PL.ISO_8859-2 pt.iso ru.koi8-r si.iso \
+ spanish.iso swedish.iso swissfrench.iso swissgerman.iso uk.iso \
+ us.dvorak us.iso
+
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ 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..b14f913
--- /dev/null
+++ b/usr.sbin/sade/command.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * 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", commandStack[i]->cmds[j].ptr);
+ ret = vsystem((char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n", 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("%x: Execute(%s, %s)", func, commandStack[i]->key, commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %x 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..74cd515
--- /dev/null
+++ b/usr.sbin/sade/config.c
@@ -0,0 +1,741 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: config.c,v 1.123 1999/02/14 07:35:27 jkh Exp $
+ *
+ * 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>
+
+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)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat)
+ return "msdos";
+ 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";
+ }
+ 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->private_data)
+ 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\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+
+ /* And finally, a /proc. */
+ fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
+ Mkdir("/proc");
+
+ 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 = (Boolean)variable_get(VAR_NAMESERVER);
+
+ 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;
+}
+
+void
+configRC_conf(void)
+{
+ FILE *rcSite;
+ Variable *v;
+ int write_header;
+ static int did_marker = 0;
+
+ write_header = !file_readable("/etc/rc.conf");
+ rcSite = fopen("/etc/rc.conf", "a");
+ if (!rcSite)
+ return;
+ if (write_header) {
+ fprintf(rcSite, "# This file now contains just the overrides from /etc/defaults/rc.conf\n");
+ fprintf(rcSite, "# please make all changes to this file.\n\n");
+ }
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ if (v->dirty) {
+ if (!did_marker) {
+ fprintf(rcSite, "# -- sysinstall generated deltas -- #\n");
+ did_marker = 1;
+ }
+ fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
+ v->dirty = 0;
+ }
+ }
+ fclose(rcSite);
+}
+
+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) | DITEM_RESTORE;
+}
+
+int
+configRegister(dialogMenuItem *self)
+{
+ return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
+}
+
+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 | DITEM_RESTORE;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ dialog_clear();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+configXEnvironment(dialogMenuItem *self)
+{
+ char *config, *execfile;
+ char *moused;
+
+tryagain:
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ if (file_readable("/var/run/ld.so.hints"))
+ systemExecute("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ config = variable_get(VAR_XF86_CONFIG);
+ if (!config)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ execfile = string_concat("/usr/X11R6/bin/", config);
+ if (file_executable(execfile)) {
+ dialog_clear_norefresh();
+ 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;
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ dialog_clear();
+ 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.");
+ dialog_clear();
+ systemExecute(execfile);
+ if (!file_readable("/etc/XF86Config") && !msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+}
+
+int
+configResolv(dialogMenuItem *ditem)
+{
+ FILE *fp;
+ char *cp, *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);
+ 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, "127.0.0.1\t\tlocalhost.%s localhost\n", dp);
+ else
+ fprintf(fp, "127.0.0.1\t\tlocalhost\n");
+ /* Now the host entries, if applicable */
+ if (cp && cp[0] != '0' && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\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(variable_get(VAR_GATED_PKG)) != 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);
+ }
+ }
+ return ret | DITEM_RESTORE;
+}
+
+int
+configPackages(dialogMenuItem *self)
+{
+ static PkgNode top, plist;
+ static Boolean index_initted = FALSE;
+ PkgNodePtr tmp;
+ FILE *fp;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ if (!index_initted) {
+ msgNotify("Attempting to fetch packages/INDEX file from selected media.");
+ fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
+ if (!fp) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n"
+ "This may be because the packages collection is not available at\n"
+ "on the distribution media you've chosen (most likely an FTP site\n"
+ "without the packages collection mirrored). Please verify media\n"
+ "(or path to media) and try again. If your local site does not\n"
+ "carry the packages collection, then we recommend either a CD\n"
+ "distribution or the master distribution on ftp.freebsd.org.");
+ mediaDevice->shutdown(mediaDevice);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ 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);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ fclose(fp);
+ index_sort(&top);
+ index_initted = TRUE;
+ }
+ 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) {
+ index_extract(mediaDevice, &top, &plist);
+ break;
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+}
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (variable_get(VAR_PCNFSD))
+ variable_unset(VAR_PCNFSD);
+ else {
+ ret = package_add(variable_get(VAR_PCNFSD_PKG));
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES", 0);
+ variable_set2("mountd_flags", "-n", 1);
+ }
+ }
+ return ret;
+}
+
+int
+configNFSServer(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ /* 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 '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ restorescr(w);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES", 1);
+ }
+ 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;
+}
diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c
new file mode 100644
index 0000000..bf4af72
--- /dev/null
+++ b/usr.sbin/sade/devices.c
@@ -0,0 +1,531 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: devices.c,v 1.89 1999/04/06 08:25:52 jkh Exp $
+ *
+ * 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;
+ char dev_type;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%dc", "SCSI CDROM drive", 6, 2, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "mcd%da", "Mitsumi (old model) CDROM drive", 7, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "scd%da", "Sony CDROM drive - CDU31/33A type", 16, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "matcd%da", "Matsushita CDROM ('sound blaster' type)", 17, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 19, 2, 8, 4, 'b' },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4, 'c' },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4, 'c' },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 4, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rda%d", "SCSI disk device", 13, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "wd%d", "IDE/ESDI/MFM/ST506 disk device", 0, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rwd%d", "IDE/ESDI/MFM/ST506 disk device", 3, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "fla%d", "DiskOnChip2000 Flash device", 28, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rfla%d", "DiskOnChip2000 Flash devicee", 101, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 1, 65538, 8, 4, 'b' },
+ { DEVICE_TYPE_DISK, "rwfd%d", "ATAPI floppy device", 87, 65538, 8, 4, 'c' },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' },
+ { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" },
+ { 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, "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", "WD/SMC 80xx; Novell NE1000/2000; 3Com 3C503 card" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 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, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX 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, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ze", "IBM/National Semiconductor PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "zp", "3Com Etherlink III PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16, 'c' },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { 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);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0)
+ return fd;
+ m = 0640;
+ if (dev.dev_type == 'c')
+ m |= S_IFCHR;
+ else
+ m |= S_IFBLK;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0)
+ 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);
+ 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++) {
+ Devices[i]->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) {
+ msgConfirm("ifconfig: socket");
+ goto skipif; /* Jump over network iface probing */
+ }
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ msgConfirm("ifconfig (SIOCGIFCONF)");
+ goto skipif; /* Jump over network iface probing */
+ }
+ 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)
+ continue;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo", 2))
+ continue;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ continue;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ continue;
+ }
+ /* 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);
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ msgConfirm("ifconfig: socket");
+ continue;
+ }
+ 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));
+ }
+
+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);
+ 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);
+ 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;
+ if (device_names[i].dev_type == 'c')
+ m |= S_IFCHR;
+ else
+ m |= S_IFBLK;
+ 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);
+ 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);
+ 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;
+
+ d = Open_Disk(names[i]);
+ if (!d)
+ msgFatal("Unable to open disk %s", names[i]);
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ 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 == 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;
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+}
+
+/* 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..11b6470a
--- /dev/null
+++ b/usr.sbin/sade/disks.c
@@ -0,0 +1,814 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: disks.c,v 1.108 1999/01/08 00:14:21 jkh Exp $
+ *
+ * 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 <sys/stat.h>
+#include <sys/disklabel.h>
+
+/* 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 void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ int 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 int Total;
+
+static void
+print_chunks(Disk *d)
+{
+ int row;
+ int i;
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %d/%d/%d 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 = %lu sectors",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect);
+ mvprintw(3, 0, "%10s %10s %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, chunk_info[i]->size,
+ 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 B = Bad Block Scan C = Create Slice");
+ mvprintw(17, 0, "D = Delete Slice G = Set Drive Geometry S = Set Bootable");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 48, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static u_char *
+getBootMgr(char *dname)
+{
+#ifndef __alpha__ /* only meaningful on x86 */
+ extern u_char mbr[], boot0[];
+ 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:
+ return boot0;
+
+ case 1:
+ return mbr;
+
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ return NULL;
+}
+
+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;
+}
+
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ Boolean chunking;
+ char *msg = NULL;
+ u_char *mbrContents;
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+
+ 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);
+ 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':
+#ifdef __alpha__
+ rv = 1;
+#else /* The rest is only relevant on x86 */
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ 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 'B':
+ if (chunk_info[current_chunk]->type != freebsd)
+ msg = "Can only scan for bad blocks in FreeBSD slice.";
+ else if (strncmp(d->name, "sd", 2) ||
+ strncmp(d->name, "da", 2) ||
+ !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
+ "Are you sure you want to do this on a SCSI disk?")) {
+ if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
+ chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
+ else
+ chunk_info[current_chunk]->flags |= CHUNK_BAD144;
+ }
+ 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], *cp;
+ int size, subtype;
+ chunk_e partitiontype;
+
+ snprintf(tmp, 20, "%lu", 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 = strtol(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). You can choose other types, 6 for a\n"
+ "DOS partition or 131 for a Linux partition, for example.\n\n"
+ "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 use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+#ifdef __alpha__
+ if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
+ All_FreeBSD(d, 1);
+ else
+#endif
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
+ 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;
+ WINDOW *save = savescr();
+
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). Other popular values are 6 for\n"
+ "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
+ "130 for a Linux swap partition.\n\n"
+ "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.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ restorescr(save);
+ }
+ 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 ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgYesNo("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 (!msgYesNo("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);
+
+ /* 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.
+ */
+#if 0
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#else
+ /*
+ * 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)) &&
+ (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /* 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.
+ */
+#if 0
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#else
+ /*
+ * 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)) &&
+ (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#endif
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ dialog_clear_norefresh();
+ 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);
+}
+
+static u_char *
+bootalloc(char *name)
+{
+ 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 %d bytes from %s\n", sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+
+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 | DITEM_RESTORE;
+}
+
+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))
+ 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))
+ 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 | DITEM_RESTORE;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+ char *cp;
+
+ 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));
+ cp = variable_get(DISK_PARTITIONED);
+ if (cp && !strcmp(cp, "written"))
+ return DITEM_SUCCESS;
+
+ for (i = 0; devs[i]; i++) {
+ Chunk *c1;
+ Disk *d = (Disk *)devs[i]->private;
+ static u_char *boot1;
+#ifndef __alpha__
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#ifdef __alpha__
+ if (!boot1) boot1 = bootalloc("boot1");
+ Set_Boot_Blocks(d, boot1, NULL);
+#else
+ if (!boot1) boot1 = bootalloc("boot1");
+ if (!boot2) boot2 = bootalloc("boot2");
+ Set_Boot_Blocks(d, boot1, boot2);
+#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;
+ }
+
+ /* If we've been through here before, we don't need to do the rest */
+ if (cp && !strcmp(cp, "written"))
+ return DITEM_SUCCESS;
+
+ /* Now scan for bad blocks, if necessary */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->flags & CHUNK_BAD144) {
+ int ret;
+
+ msgNotify("Running bad block scan on slice %s", c1->name);
+ if (!Fake) {
+ ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
+ if (ret)
+ msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
+ ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
+ if (ret)
+ msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
+ }
+ }
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+ u_char *mbrContents;
+ 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));
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ 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 = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ 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));
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find %d free blocks on this disk!", 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]) {
+ dialog_clear();
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ dialog_clear();
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+ mbrContents = getBootMgr(d->name);
+ Set_Boot_Mgr(d, mbrContents);
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
diff --git a/usr.sbin/sade/dispatch.c b/usr.sbin/sade/dispatch.c
new file mode 100644
index 0000000..8d289e4
--- /dev/null
+++ b/usr.sbin/sade/dispatch.c
@@ -0,0 +1,436 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dispatch.c,v 1.26 1998/11/15 09:06:19 jkh Exp $
+ *
+ * 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 struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configRegister", configRegister },
+ { "configUsers", configUsers },
+ { "configXEnvironment", configXEnvironment },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetDES", distSetDES },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installNovice", installNovice },
+ { "installUpgrade", installUpgrade },
+ { "installFixupBin", installFixupBin },
+ { "installFixupXFree", installFixupXFree },
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "loadFloppyConfig", dispatch_load_floppy },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "optionsEditor", optionsEditor },
+ { "register", configRegister }, /* Alias */
+ { "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(msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+ /* If it's got a newline, trim it */
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ }
+ 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) {
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (variable_get(VAR_NO_ERROR))
+ variable_unset(VAR_NO_ERROR);
+ else {
+ 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_RESTORE | DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ dialog_clear_norefresh();
+
+ 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 (!mediaDevice->init(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = mediaDevice->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..f83fada
--- /dev/null
+++ b/usr.sbin/sade/dmenu.c
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ *
+ * $Id: dmenu.c,v 1.39 1999/02/05 22:25:13 jkh Exp $
+ *
+ * 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 | DITEM_RESTORE;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE) |
+ DITEM_RESTORE;
+}
+
+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)
+{
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+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, 1);
+ 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;
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!variable_check(var))
+ variable_set(var, *var != '_');
+ else
+ variable_unset(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ w = savescr();
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ restorescr(w);
+ 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];
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+
+ /* Pop up that dialog! */
+ dialog_clear_norefresh();
+ 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 *)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 *)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 *)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ clearok(stdscr, TRUE);
+ if (exited) {
+ exited = FALSE;
+ return TRUE;
+ }
+ else if (rval)
+ return FALSE;
+ else if (menu->type & DMENU_SELECTION_RETURNS)
+ return TRUE;
+ }
+}
diff --git a/usr.sbin/sade/globals.c b/usr.sbin/sade/globals.c
new file mode 100644
index 0000000..72aecb1
--- /dev/null
+++ b/usr.sbin/sade/globals.c
@@ -0,0 +1,71 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: globals.c,v 1.18 1997/02/22 14:11:43 peter Exp $
+ *
+ * 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? */
+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;
+ 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..6004d75
--- /dev/null
+++ b/usr.sbin/sade/help/partition.hlp
@@ -0,0 +1,118 @@
+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 20MB 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 100MB. 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).
+
+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..33280a4
--- /dev/null
+++ b/usr.sbin/sade/help/slice.hlp
@@ -0,0 +1,59 @@
+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'.
diff --git a/usr.sbin/sade/install.c b/usr.sbin/sade/install.c
new file mode 100644
index 0000000..f8a6d59
--- /dev/null
+++ b/usr.sbin/sade/install.c
@@ -0,0 +1,1134 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: install.c,v 1.229 1999/02/15 00:49:33 jkh Exp $
+ *
+ * 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/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+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)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = 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) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* 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) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Copy our values over */
+ *rdev = rootdev;
+ *sdev = swapdev;
+ *udev = usrdev;
+ *vdev = vardev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
+ msgConfirm("WARNING: No /usr filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
+ "cause you trouble if you're not exactly sure what you are doing!");
+ }
+ if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
+ msgConfirm("WARNING: No /var filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to link /var to someplace else), but it may otherwise\n"
+ "cause your root filesystem to fill up if you receive lots of mail\n"
+ "or edit large temporary files.");
+ }
+ 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 | DITEM_RESTORE;
+
+ 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 (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 */
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return status;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+}
+
+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 CDROM and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->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 CDROM - 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 CDROM 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/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ /* Yet more iggly hardcoded pathnames. */
+ Mkdir("/usr/libexec");
+ if (!file_readable("/usr/libexec/ld.so")) {
+ if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
+ msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
+ }
+ if (!file_readable("/usr/libexec/ld-elf.so.1")) {
+ if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
+ msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
+ "Dynamic executables from the CDROM 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 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 (!mediaDevice->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/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 (!(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);
+ 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);
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", 0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ 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.");
+ (void)waitpid(child, &waitstatus, 0);
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ variable_set2(SYSTEM_STATE, "express", 0);
+#ifndef __alpha__
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+#endif
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i | DITEM_RESTORE;
+}
+
+/* Novice mode installation */
+int
+installNovice(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "novice", 0);
+#ifndef __alpha__
+ dialog_clear_norefresh();
+ 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;
+ }
+#endif
+
+ dialog_clear_norefresh();
+#ifdef __alpha__
+ 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.");
+#else
+ msgConfirm("First, 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.");
+#endif
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear_norefresh();
+ 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 chose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i | DITEM_RESTORE;
+
+ }
+ else {
+ dialog_clear_norefresh();
+ 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: /stand/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;
+
+ dialog_clear_norefresh();
+ tmp = tcpDeviceSelect();
+ dialog_clear_norefresh();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
+ "between interfaces)?"))
+ variable_set2("gateway_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to customize your system console settings?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?")) {
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ systemExecute("tzsetup");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Does this system have a mouse attached to it?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ restorescr(w);
+ }
+
+ /* 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?"))
+ configXEnvironment(self);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of hundreds 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?"))
+ configPackages(self);
+
+ dialog_clear_norefresh();
+ 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)."))
+ configUsers(self);
+
+ dialog_clear_norefresh();
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ {
+ WINDOW *w = savescr();
+
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES", 0);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
+ "PLEASE, take just 5 minutes to do this. If we're ever to get any\n"
+ "significant base of commercial software for FreeBSD, we need to\n"
+ "be able to provide more information about the size of our user community.\n"
+ "This is where your registration can really help us, and you can also\n"
+ "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
+ configRegister(NULL);
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("OK, but if you should change your mind then you always can register\n"
+ "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
+ "web site at http://www.freebsd.org/register.html");
+
+ }
+ /* 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 | DITEM_RESTORE;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ 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 DES dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists)
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ 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 (!mediaDevice->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 | DITEM_RESTORE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* 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 | DITEM_RESTORE;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ dialog_clear_norefresh();
+ if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
+ "any last options?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ restorescr(w);
+ }
+ configRC_conf();
+ sync();
+}
+
+int
+installFixupBin(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+ /* Fix up kernel first */
+ if (!file_readable("/kernel")) {
+ if (file_readable("/kernel.GENERIC")) {
+ if (vsystem("cp -p /kernel.GENERIC /kernel")) {
+ msgConfirm("Unable to copy /kernel into place!");
+ return DITEM_FAILURE;
+ }
+#ifndef __alpha__
+ /* Snapshot any boot -c changes back to the new kernel */
+ if (kget("/boot/kernel.conf")) {
+ msgConfirm("Kernel copied OK, but unable to save boot -c changes\n"
+ "to it. See the debug screen (ALT-F2) for details.");
+ }
+ else {
+ if (!file_readable("/boot/loader.rc")) {
+ FILE *fp;
+
+ if ((fp = fopen("/boot/loader.rc", "w")) != NULL) {
+ fprintf(fp, "load /kernel\n");
+ fprintf(fp, "load -t userconfig_script /boot/kernel.conf\n");
+ fprintf(fp, "autoboot 5\n");
+ fclose(fp);
+ }
+ }
+ else {
+ msgConfirm("You already have a /boot/loader.rc file so I won't touch it.\n"
+ "You will need to add a: load -t userconfig_script /boot/kernel.conf\n"
+ "line to your /boot/loader.rc before your saved kernel changes\n"
+ "(if any) can go into effect.");
+ }
+ }
+#endif
+ }
+ else {
+ msgConfirm("Can't find a kernel image to link to on the root file system!\n"
+ "You're going to have a hard time getting this system to\n"
+ "boot from the hard disk, I'm afraid!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* BOGON #1: Resurrect /dev after bin distribution screws it up */
+ msgNotify("Remaking all devices.. Please wait!");
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Resurrecting /dev entries for slices..");
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs)
+ msgFatal("Couldn't get a disk device list!");
+
+ /* Resurrect the slices that the former clobbered */
+ for (i = 0; devs[i]; i++) {
+ Disk *disk = (Disk *)devs[i]->private;
+ Chunk *c1;
+
+ if (!devs[i]->enabled)
+ continue;
+ 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) {
+ msgNotify("Making slice entries for %s", c1->name);
+ if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
+ msgConfirm("Unable to make slice entries for %s!", c1->name);
+ return DITEM_FAILURE;
+ }
+ }
+ }
+ }
+
+ /* 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;
+}
+
+/* Fix side-effects from the the XFree86 installation */
+int
+installFixupXFree(dialogMenuItem *self)
+{
+ /* BOGON #1: XFree86 requires various specialized fixups */
+ if (directory_exists("/usr/X11R6")) {
+ msgNotify("Fixing permissions in XFree86 tree..");
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+
+ /* Also do bogus minimal package registration so ports don't whine */
+ if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
+ msgNotify("Installing package metainfo..");
+ vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+/* 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, *usrdev, *vardev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ extern int MakeDevChunk(Chunk *c, char *n);
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
+ 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 && (!MakeDevChunk(swapdev, "/dev") || !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;
+ }
+
+ if (!Fake) {
+ if (!swapon(dname))
+ 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/r%s", rootdev->name);
+ if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !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;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = vsystem("%s %s", root->newfs_cmd, dname);
+ if (i) {
+ msgConfirm("Unable to make new root filesystem on %s!\n"
+ "Command returned status %d", dname, i);
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, 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;
+ }
+ }
+
+ /* 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;
+ }
+ if (RunningAsInit && root && (root->newfs || upgrade)) {
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ MakeDevDisk(disk, "/mnt/dev");
+ }
+ else if (!RunningAsInit && !Fake)
+ MakeDevDisk(disk, "/dev");
+
+ 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) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == rootdev)
+ continue;
+
+ if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == swapdev)
+ continue;
+ sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i)
+ 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->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ if (RunningAsInit) {
+ msgNotify("Copying initial device files..");
+ /* Copy the boot floppy's dev files */
+ if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't clone the /dev files!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ command_sort();
+ command_execute();
+ return DITEM_SUCCESS;
+}
+
+static char *
+getRelname(void)
+{
+ static char buf[64];
+ int 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);
+ 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, "lynx", 0);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/lynx", 0);
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ variable_set2(VAR_NFS_SECURE, "YES", 0);
+ variable_set2(VAR_PKG_TMPDIR, "/usr/tmp", 0);
+ variable_set2(VAR_GATED_PKG, "gated", 0);
+ variable_set2(VAR_PCNFSD_PKG, "pcnfsd", 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);
+ 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, 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..459f4cb
--- /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.
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.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..6b69e0f
--- /dev/null
+++ b/usr.sbin/sade/label.c
@@ -0,0 +1,1303 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: label.c,v 1.87 1999/03/09 12:36:28 jkh Exp $
+ *
+ * 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/param.h>
+#include <sys/sysctl.h>
+
+/*
+ * 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
+
+/* The smallest root filesystem we're willing to create */
+#ifdef __alpha__
+#define ROOT_MIN_SIZE 40
+#else
+#define ROOT_MIN_SIZE 30
+#endif
+
+/* The default root filesystem size */
+#ifdef __alpha__
+#define ROOT_DEFAULT_SIZE 60
+#else
+#define ROOT_DEFAULT_SIZE 40
+#endif
+
+/* The smallest swap partition we want to create by default */
+#define SWAP_MIN_SIZE 16
+
+/* The smallest /usr partition we're willing to create by default */
+#define USR_MIN_SIZE 80
+
+/* The smallest /var partition we're willing to create by default */
+#define VAR_MIN_SIZE 20
+
+/* 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 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 | DITEM_RESTORE;
+}
+
+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))
+ 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))
+ 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);
+ }
+ i |= DITEM_RESTORE;
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ char *cp;
+
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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].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 int
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ int 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);
+
+ /* 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;
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
+ }
+ 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, u_long size)
+{
+ PartInfo *ret;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
+ strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
+ ret->newfs = newfs;
+ if (!size)
+ return ret;
+ return ret;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ if (!old) {
+ DialogX = 14;
+ DialogY = 16;
+ }
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ DialogX = DialogY = 0;
+ 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;
+
+ safe_free(tmp);
+ tmp = new_part(val, TRUE, 0);
+ 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[] = {
+ "FS",
+ "A file system",
+ "Swap",
+ "A swap partition.",
+ };
+ DialogX = 7;
+ DialogY = 8;
+ 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, 2, 2, fs_types, selection, NULL, NULL);
+ DialogX = DialogY = 0;
+ if (!i) {
+ 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 *val;
+
+ val = msgGetInput(p->newfs_cmd,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val)
+ sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
+}
+
+#define MAX_MOUNT_NAME 12
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 8
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 7)
+#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;
+ int 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) + 2, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
+
+ 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;
+
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
+ label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
+ sz, (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;
+
+ /*
+ * 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))
+ 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)
+ newfs = "DOS";
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
+ newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
+ else if (label_chunk_info[i].type == PART_SWAP)
+ newfs = "SWAP";
+ else
+ newfs = "*";
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
+ 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 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, 49, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts T = Newfs Toggle U = Undo Q = Finish");
+ mvprintw(20, 0, "A = Auto Defaults for all!");
+ 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();
+}
+
+#ifdef __alpha__
+
+/*
+ * If there isn't a freebsd chunk already (i.e. there is no label),
+ * dedicate the disk.
+ */
+static void
+maybe_dedicate(Disk* d)
+{
+ struct chunk *c;
+
+ for (c = d->chunks->part; c; c = c->next) {
+ if (c->type == freebsd)
+ break;
+ }
+
+ if (!c) {
+ msgDebug("dedicating disk");
+ All_FreeBSD(d, 1);
+ }
+}
+
+#endif
+
+static int
+diskLabel(Device *dev)
+{
+ int sz, key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+#ifdef __alpha__
+ int i;
+#endif
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+#ifdef __alpha__
+ for (i = 0; devs[i]; i++) {
+ maybe_dedicate((Disk*) devs[i]->private);
+ }
+#endif
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+
+ 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 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ msg = "Not enough free space to create a new partition in the slice";
+ else {
+ struct chunk *tmp;
+ int mib[2];
+ int physmem;
+ size_t size, swsize;
+ char *cp;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
+ if (!rootdev) {
+ cp = variable_get(VAR_ROOT_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : ROOT_DEFAULT_SIZE) * ONE_MEG, part, FS_BSDFFS, CHUNK_IS_ROOT);
+ if (!tmp) {
+ msgConfirm("Unable to create the root partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!swapdev) {
+ cp = variable_get(VAR_SWAP_SIZE);
+ if (cp)
+ swsize = atoi(cp) * ONE_MEG;
+ else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ swsize = 16 * ONE_MEG + (physmem * 2 / 512);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ swsize, part, FS_SWAP, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the swap partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = 0;
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!vardev) {
+ cp = variable_get(VAR_VAR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = variable_get(VAR_NO_USR)
+ ? space_free(label_chunk_info[here].c)
+ : VAR_MIN_SIZE * ONE_MEG;
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Less than %dMB free for /var - you will need to\n"
+ "partition your disk manually with a custom install!",
+ (cp ? atoi(cp) : VAR_MIN_SIZE));
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/var", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ cp = variable_get(VAR_USR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Less than %dMB free for /usr - you will need to\n"
+ "partition your disk manually with a custom install!", USR_MIN_SIZE);
+ clear_wins();
+ break;
+ }
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/usr", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ }
+
+ /* At this point, we're reasonably "labelled" */
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ 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;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ DialogX = 3;
+ DialogY = 2;
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing M for\n"
+ "megabytes or C for cylinders. %d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ DialogX = DialogY = 0;
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM) {
+ if ((p = get_mountpoint(NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT)) {
+ if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
+ msgConfirm("This region cannot be used for your root partition as the\n"
+ "FreeBSD boot code cannot deal with a root partition created\n"
+ "in that location. Please choose another location or smaller\n"
+ "size for your root partition and try again!");
+ clear_wins();
+ break;
+ }
+ if (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, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+ if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
+ msgConfirm("This region cannot be used for your root partition as it starts\n"
+ "or extends past the 1024'th cylinder mark and is thus a\n"
+ "poor location to boot from. Please choose another\n"
+ "location (or smaller size) for your root partition and try again!");
+ Delete_Chunk(label_chunk_info[here].c->disk, tmp);
+ clear_wins();
+ break;
+ }
+ if (type != PART_SWAP) {
+ /* This is needed to tell the newfs -u about the size */
+ tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
+ safe_free(p);
+ }
+ else
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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 '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_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->newfs = FALSE;
+ if (label_chunk_info[here].type == PART_FAT
+ && (!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 (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgYesNo("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;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to start this\n"
+ "procedure again from the beginning.");
+ }
+ else if (!msgYesNo("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 '|':
+ if (!msgYesNo("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 (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ 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;
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ dialog_clear();
+ 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;
+#ifdef __alpha__
+ maybe_dedicate(d);
+#endif
+ 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];
+ int entries = 1;
+
+ while (entries) {
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) != NULL) {
+ int sz;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ else {
+ Chunk *tmp;
+
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ }
+ else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ 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;
+ continue;
+ }
+ 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 {
+ tmp->private_data = new_part(mpoint, TRUE, sz);
+ tmp->private_free = safe_free;
+ status = DITEM_SUCCESS;
+ }
+ }
+ entries++;
+ }
+ else {
+ /* No more matches, leave the loop */
+ entries = 0;
+ }
+ }
+ }
+ 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) {
+ dialog_clear();
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(mpoint, newfs, 0);
+ 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..05a9fd5
--- /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.
+ *
+ * $Id: list.h,v 1.1 1997/09/16 17:03:58 pst Exp $
+ *
+ * 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..9f6c812
--- /dev/null
+++ b/usr.sbin/sade/main.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ *
+ * $Id: main.c,v 1.50 1999/01/08 00:14:22 jkh Exp $
+ *
+ * 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>
+
+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;
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+
+ /* 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;
+ }
+
+ /* 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?");
+ }
+
+ /* 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);
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake)
+ 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"
+ "If you can reproduce the problem, please turn Debug on in\n"
+ "the Options menu for the extra information it provides in\n"
+ "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
+#ifdef __alpha__
+ || !msgYesNo("Are you sure you wish to exit? The system will halt.")
+#else
+ || !msgYesNo("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies 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..c0a5bd5
--- /dev/null
+++ b/usr.sbin/sade/menus.c
@@ -0,0 +1,1524 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: menus.c,v 1.192 1999/03/09 12:36:28 jkh Exp $
+ *
+ * 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"
+
+#ifdef __alpha__
+#define _AS(str) str "alpha/"
+#else /* i386 */
+#define _AS(str) str "i386/"
+#endif
+#define _AP(str) _AS(str "/pub/FreeBSD/releases/")
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11All(dialogMenuItem *self)
+{
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11All(dialogMenuItem *self)
+{
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+ Dists &= ~DIST_XF86;
+ 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 | DIST_DES | extra))
+
+#define IS_USER(dist, extra) (_IS_SET(dist, _DIST_USER | extra) || \
+ _IS_SET(dist, _DIST_USER | DIST_DES | 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_BIN;
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_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
+DESFlagCheck(dialogMenuItem *item)
+{
+ return DESDists;
+}
+
+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 [ENTER].\n"
+ "Leave the index page by selecting Cancel [TAB-ENTER].",
+ "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 },
+ { "Console settings", "Customize system console behavior.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Configure", "The system configuration menu.", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Defaults, Load", "Load default settings.", NULL, dispatch_load_floppy },
+ { "Device, Mouse", "The mouse configuration menu.", NULL, dmenuSubmenu, NULL, &MenuMouse },
+ { "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, DES", "DES distribution menu.", NULL, dmenuSubmenu, NULL, &MenuDESDistributions },
+ { "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, 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 },
+ { "Fdisk", "The disk Partition Editor", NULL, diskPartitionEditor },
+ { "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 },
+ { "Install, Novice", "A novice system installation.", NULL, installNovice },
+ { "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", "Select CDROM 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 },
+ { "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 },
+ { "Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { "PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Register", "Register yourself or company as a FreeBSD user.", dmenuVarCheck, configRegister, NULL, "registered" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router" },
+ { "Syscons", "The system console configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Syscons, Font", "The console screen font.", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "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 },
+ { "Syscons, Screenmap", "The console screenmap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "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 },
+ { "XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "/stand/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 by pressing [ENTER] or [TAB-ENTER] to exit the installation.",
+ "Press F1 for Installation Guide", /* help line */
+ "install", /* help file */
+ { { "Select" },
+ { "Exit Install", NULL, NULL, dmenuExit },
+ { "1 Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "2 Novice", "Begin a novice installation (for beginners)", NULL, installNovice },
+ { "3 Express", "Begin a quick installation (for the impatient)", NULL, installExpress },
+ { "4 Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "5 Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "D Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "K Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "O Options", "View/Set various installation options", NULL, optionsEditor },
+ { "F Fixit", "Enter repair mode with CDROM/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "U Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "L Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "I 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",
+ { { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "README" },
+ { "2 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "3 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "4 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "5 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "6 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "7 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuMouseType = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "Select a protocol type for your mouse",
+ "If you are not sure, choose \"Auto\". It should always work for bus\n"
+ "and PS/2 style mice. It may not work for the serial mouse if the mouse\n"
+ "does not support the PnP standard. But, it won't hurt. Many 2-button\n"
+ "serial mice are compatible with \"Microsoft\" or \"MouseMan\". 3-button\n"
+ "serial mice may be compatible with \"MouseSystems\" or \"MouseMan\". If\n"
+ "the mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "Auto", "Bus mouse, PS/2 style mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+ { "GlidePoint", "ALPS GlidePoint pad (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=glidepoint" },
+ { "Hitachi","Hitachi tablet (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmhittab" },
+ { "IntelliMouse", "Microsoft IntelliMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=intellimouse" },
+ { "Logitech", "Logitech protocol (old models) (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=logitech" },
+ { "Microsoft", "Microsoft protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=microsoft" },
+ { "MM Series","MM Series protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmseries" },
+ { "MouseMan", "Logitech MouseMan/TrackMan models (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mouseman" },
+ { "MouseSystems", "MouseSystems protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mousesystems" },
+ { "ThinkingMouse","Kensington ThinkingMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=thinkingmouse" },
+ { NULL } },
+};
+
+DMenu MenuMousePort = {
+ DMENU_RADIO_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,
+ { { "COM1", "Serial mouse on COM1 (/dev/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "BusMouse", "Logitech, ATI or MS bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { "PS/2", "PS/2 style mouse (/dev/psm0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/psm0" },
+ { NULL } },
+};
+
+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 4 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,
+ { { "1 Type", "Select mouse protocol type", NULL, dmenuSubmenu, NULL, &MenuMouseType },
+ { "2 Port", "Select mouse port", NULL, dmenuSubmenu, NULL, &MenuMousePort },
+ { "3 Enable", "Test and run the mouse daemon", NULL, mousedTest, NULL, NULL },
+ { "4 Disable", "Disable the mouse daemon", NULL, mousedDisable, NULL, NULL },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first tool, XF86Setup, is fully graphical and requires the\n"
+ "VGA16 server in order to work (should have been selected by\n"
+ "default, but if you de-selected it then you won't be able to\n"
+ "use this fancy setup tool). The second tool, 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 fancier one\n"
+ "does not.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "XF86Setup", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF86Setup" },
+ { "xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "XF98Setup", "Fully graphical XFree86 configuration tool (PC98).",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF98Setup" },
+ { NULL } },
+};
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CDROM type",
+ "FreeBSD can be installed directly from a CDROM containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CDROM drive was found on your system. Please select one\n"
+ "of the following CDROM 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 chose 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 the Primary site is\n"
+ "guaranteed to carry the full range of possible distributions.",
+ "Select a site that's close!",
+ "install",
+ { { "Primary Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://ftp.freebsd.org/pub/FreeBSD/releases/") },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { "4.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://current.freebsd.org/pub/FreeBSD/snapshots/") },
+ { "3.0 SNAP Server", "releng3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://releng3.freebsd.org/pub/FreeBSD/snapshots/") },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ar.freebsd.org") },
+ { "Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.au.freebsd.org") },
+ { "Australia #2", "ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.au.freebsd.org") },
+ { "Australia #3", "ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.au.freebsd.org") },
+ { "Australia #4", "ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.au.freebsd.org") },
+ { "Australia #5", "ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.au.freebsd.org") },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.br.freebsd.org") },
+ { "Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.br.freebsd.org") },
+ { "Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.br.freebsd.org") },
+ { "Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.br.freebsd.org") },
+ { "Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.br.freebsd.org") },
+ { "Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.br.freebsd.org") },
+ { "Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp7.br.freebsd.org") },
+ { "Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ca.freebsd.org") },
+ { "Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.cz.freebsd.org") },
+ { "Denmark", "ftp.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.dk.freebsd.org") },
+ { "Denmark #2", "ftp2.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.dk.freebsd.org") },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ee.freebsd.org") },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.fi.freebsd.org") },
+ { "France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.fr.freebsd.org") },
+ { "France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.fr.freebsd.org") },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.de.freebsd.org") },
+ { "Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.de.freebsd.org") },
+ { "Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.de.freebsd.org") },
+ { "Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.de.freebsd.org") },
+ { "Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.de.freebsd.org") },
+ { "Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.de.freebsd.org") },
+ { "Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp7.de.freebsd.org") },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.nl.freebsd.org") },
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.hk.super.net") },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.is.freebsd.org") },
+ { "Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ie.freebsd.org") },
+ { "Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.il.freebsd.org") },
+ { "Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.il.freebsd.org") },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.jp.freebsd.org") },
+ { "Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.jp.freebsd.org") },
+ { "Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.jp.freebsd.org") },
+ { "Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.jp.freebsd.org") },
+ { "Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.jp.freebsd.org") },
+ { "Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.jp.freebsd.org") },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.kr.freebsd.org") },
+ { "Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.kr.freebsd.org") },
+ { "Korea #3", "ftp3.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.kr.freebsd.org") },
+ { "Korea #4", "ftp4.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.kr.freebsd.org") },
+ { "Korea #5", "ftp5.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.kr.freebsd.org") },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.pl.freebsd.org") },
+ { "Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.pt.freebsd.org") },
+ { "Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.pt.freebsd.org") },
+ { "Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ru.freebsd.org") },
+ { "Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.ru.freebsd.org") },
+ { "Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.ru.freebsd.org") },
+ { "Russia #4", "ftp4.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.ru.freebsd.org") },
+ { "South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.za.freebsd.org") },
+ { "South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.za.freebsd.org") },
+ { "South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.za.freebsd.org") },
+ { "South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.za.freebsd.org") },
+ { "Spain", "ftp.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.es.freebsd.org") },
+ { "Spain #2", "ftp2.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.es.freebsd.org") },
+ { "Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.se.freebsd.org") },
+ { "Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.se.freebsd.org") },
+ { "Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.se.freebsd.org") },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.tw.freebsd.org") },
+ { "Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.tw.freebsd.org") },
+ { "Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.tw.freebsd.org") },
+ { "Thailand", "ftp.nectec.or.th", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://ftp.nectec.or.th/pub/mirrors/FreeBSD/") },
+ { "UK", "ftp.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.uk.freebsd.org") },
+ { "UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.uk.freebsd.org") },
+ { "UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.uk.freebsd.org") },
+ { "UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.uk.freebsd.org") },
+ { "USA", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.freebsd.org") },
+ { "USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.freebsd.org") },
+ { "USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.freebsd.org") },
+ { "USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.freebsd.org") },
+ { "USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.freebsd.org") },
+ { "USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.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 a fairly recent (2.0R or later)\n"
+ "version of FreeBSD.",
+ "Press F1 to read network configuration manual",
+ "network_device",
+ { { 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 CDROM 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 CDROM", "Install from a FreeBSD CDROM", 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 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "5 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "6 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "7 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "8 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "9 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]. When you are finished, chose the Exit\n"
+ "item or press [ENTER].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "1 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "2 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "3 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "4 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "5 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "6 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "7 Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "8 Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { "8 All", "All sources and binaries (incl X Window System)",
+ checkDistEverything, distSetEverything },
+ { "9 Clear", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "0 Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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 \"bin\". WARNING: Do not export the\n"
+ "DES distribution out of the U.S.! It is for U.S. customers only.",
+ NULL,
+ NULL,
+ { { "bin", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BIN },
+ { "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 },
+ { "DES", "DES encryption code - NOT FOR EXPORT!",
+ DESFlagCheck, distSetDES },
+ { "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 but DES",
+ srcFlagCheck, distSetSrc },
+ { "ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+ { "XFree86", "The XFree86 3.3.3.1 distribution",
+ x11FlagCheck, distSetXF86 },
+ { "All", "All sources, binaries and X Window System binaries",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuDESDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the encryption facilities you wish to install.",
+ "Please check off any special DES-based encryption distributions\n"
+ "you would like to install. Please note that these services are NOT FOR\n"
+ "EXPORT from the United States. For information on non-U.S. FTP\n"
+ "distributions of this software, please consult the release notes.",
+ NULL,
+ NULL,
+ { { "des", "Basic DES encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_DES, },
+ { "krb", "Kerberos encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_KERBEROS },
+ { "skerbero", "Sources for Kerberos IV",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SKERBEROS },
+ { "ssecure", "Sources for DES",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SSECURE },
+ { "scrypto", "Export controlled crypto sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SCRYPTO },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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,
+ { { "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 },
+ { "share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { "sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { "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 },
+ { "All", "Select all of the above",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 3.3.3.1 Distribution",
+ "Please select the components you need from the XFree86 3.3.3.1\n"
+ "distribution sets.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "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 },
+ { "All", "Select all XFree86 distribution sets", NULL, setX11All },
+ { "Clear", "Reset XFree86 distribution list", NULL, clearX11All },
+ { "Exit", "Exit this menu (returning to previous)", checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 3.3.3.1 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.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { "cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { "doc", "READMEs and release notes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { "html", "HTML documentation files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_HTML },
+ { "lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { "lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+ { "lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+ { "man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { "prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { "set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+ { "9set", "XFree86 Setup Utility for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_9SET },
+ { "sources", "XFree86 3.3.3.1 standard sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SRC },
+ { "csources", "XFree86 3.3.3.1 contrib sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CSRC },
+ { "All", "Select all of the above",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "fnts", "Standard 75 DPI and miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_MISC },
+ { "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 },
+ { "non", "Japanese, Chinese and other non-english fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_NON },
+ { "server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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"
+ "If you are unsure as to which server will work for your graphics card,\n"
+ "it is recommended that try the SVGA or VGA16 servers or, for PC98\n"
+ "machines, the 9EGC or 9840 servers.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "SVGA", "Standard VGA or Super VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_SVGA },
+ { "VGA16", "Standard 16 color VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VGA16 },
+ { "Mono", "Standard Monochrome card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MONO },
+ { "8514", "8-bit (256 color) IBM 8514 or compatible card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_8514 },
+ { "AGX", "8-bit AGX card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_AGX },
+ { "I128", "8, 16 and 24-bit #9 Imagine I128 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_I128 },
+ { "Ma8", "8-bit ATI Mach8 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH8 },
+ { "Ma32", "8 and 16-bit (65K color) ATI Mach32 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH32 },
+ { "Ma64", "8 and 16-bit (65K color) ATI Mach64 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH64 },
+ { "P9K", "8, 16, and 24-bit color Weitek P9000 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_P9000 },
+ { "S3", "8, 16 and 24-bit color S3 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3 },
+ { "S3V", "8, 16 and 24-bit color S3 Virge based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3V },
+ { "W32", "8-bit ET4000/W32, /W32i and /W32p cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_W32 },
+ { "nest", "A nested server for testing purposes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { "PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectPC98Server = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "PC98 X Server selection.",
+ "Please check off the types of NEC PC98 X servers you wish to install.\n\
+If you are unsure as to which server will work for your graphics card,\n\
+it is recommended that try the SVGA or VGA16 servers (the VGA16 and\n\
+Mono servers are particularly well-suited to most LCD displays).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "9480", "PC98 8-bit (256 color) PEGC-480 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9480 },
+ { "9EGC", "PC98 4-bit (16 color) EGC card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9EGC },
+ { "9GA9", "PC98 GA-968V4/PCI (S3 968) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GA9 },
+ { "9GAN", "PC98 GANB-WAP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GAN },
+ { "9LPW", "PC98 PowerWindowLB (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9LPW },
+ { "9MGA", "PC98 MGA (Matrox) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9MGA },
+ { "9NKV", "PC98 NKV-NEC (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9NKV },
+ { "9NS3", "PC98 NEC (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9NS3 },
+ { "9SPW", "PC98 SKB-PowerWindow (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9SPW },
+ { "9SVG", "PC98 generic SVGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9SVG },
+ { "9TGU", "PC98 Cyber9320 and TGUI9680 cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9TGU },
+ { "9WEP", "PC98 WAB-EP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WEP },
+ { "9WS", "PC98 WABS (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WS },
+ { "9WSN", "PC98 WSN-A2F (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WSN },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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]. To de-select it, press [SPACE] again.\n\n"
+ "Select OK or Cancel to 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 chose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "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",
+ { { "1 Options", "View/Set various installation options", NULL, optionsEditor },
+#ifdef __alpha__
+ { "2 Label", "Label disk partitions", NULL, diskLabelEditor },
+ { "3 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "4 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#else
+ { "2 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "3 Label", "Label allocated 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 },
+#endif
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_RADIO_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 } },
+};
+
+/* 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",
+ { { "D Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { "P Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { "R Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "L Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { "F Fdisk", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+ { "1 User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { "2 Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "3 Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "4 Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Mouse", "Configure your mouse",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { "6 Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "7 Startup", "Configure system startup options",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { "8 Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { "D HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+ { "X XFree86", "Configure XFree86",
+ NULL, configXEnvironment },
+ { "U Register", "Register yourself or company as a FreeBSD user.", NULL, configRegister },
+ { "E Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { 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. Remember to use SPACE to select items! The\n"
+ "RETURN key will leave this menu (as with all checkbox menus).",
+ NULL,
+ NULL,
+ { { "APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+ { "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" },
+ { " ", " -- ", 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, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { "nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, dmenuToggleVariable, 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" },
+ { "linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "linux_enable=YES" },
+ { "quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { "SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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,
+ { { "Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { "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" },
+ { "AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { "AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { "TCP Extensions", "Allow RFC1323 and RFC1644 TCP extensions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extensions=YES" },
+ { "Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+ { "Ntpdate", "Select a clock-synchronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', "ntpdate_enable=YES" },
+ { "router", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router" },
+ { "Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { "Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_RADIO_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 },
+ { "Australia", "ntp.syd.dms.csiro.au (HP 5061 Cesium Beam)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.syd.dms.csiro.au" },
+ { "Canada", "tick.usask.ca (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.usask.ca" },
+ { "France", "canon.inria.fr (TDF clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=canon.inria.fr" },
+ { "Germany", "ntps1-{0,1,2}.uni-erlangen.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.uni-erlangen.de" },
+ { "Germany #2", "ntps1-0.cs.tu-berlin.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.cs.tu-berlin.de" },
+ { "Japan", "clock.nc.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.nc.fukuoka-u.ac.jp" },
+ { "Japan #2", "clock.tl.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tl.fukuoka-u.ac.jp" },
+ { "Netherlands", "ntp0.nl.net (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.nl.net" },
+ { "Norway", "timer.unik.no (NTP clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timer.unik.no" },
+ { "Sweden", "Time1.Stupi.SE (Cesium/GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=Time1.Stupi.SE" },
+ { "Switzerland", "swisstime.ethz.ch (DCF77 clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=swisstime.ethz.ch" },
+ { "U.S. East Coast", "bitsy.mit.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bitsy.mit.edu" },
+ { "U.S. East Coast #2", "otc1.psu.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=otc1.psu.edu" },
+ { "U.S. West Coast", "apple.com (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=apple.com" },
+ { "U.S. West Coast #2", "clepsydra.dec.com (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clepsydra.dec.com" },
+ { "U.S. West Coast #3", "clock.llnl.gov (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.llnl.gov" },
+ { "U.S. Midwest", "ncar.ucar.edu (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ncar.ucar.edu" },
+ { "U.S. Pacific", "chantry.hawaii.net (WWV/H clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chantry.hawaii.net" },
+ { "U.S. Southwest", "shorty.chpc.utexas.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=shorty.chpc.utexas.edu" },
+ { NULL } },
+};
+
+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,
+ { { "Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeymap = {
+ DMENU_RADIO_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" },
+ { "Croatian ISO", "Croatian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hr.iso" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { "Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "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" },
+ { "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" },
+ { "Latin American", "Latin American ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=lat-amer" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Polish ISO", "Polish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pl_PL.ISO_8859-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 CP866", "Russian CP866 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.cp866" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "Slovenian", "Slovenian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=si.iso.acc" },
+ { "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", "Swiss French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso" },
+ { "Swiss German", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso" },
+ { "U.K. CP850", "United Kingdom Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { "U.K. ISO", "United Kingdom ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { "U.S. Dvorak", "United States Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { "U.S. ISO", "United States ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_RADIO_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_RADIO_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,
+ { { "Blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "Daemon", "\"BSD Daemon\" animated screen saver (text)",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "Fade", "Fade out effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fade" },
+ { "Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "Logo", "\"BSD Daemon\" animated screen saver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=logo" },
+ { "Rain", "Rain drops screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=rain" },
+ { "Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Warp", "A \"stars warping\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=warp" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_RADIO_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,
+ { { "None", "No screenmap, use default font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_RADIO_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,
+ { { "None", "Use default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "IBM 437", "English", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "IBM 866", "Russian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866-8x16" },
+ { "ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "KOI8-R", "Russian, KOI8-R encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=koi8-r-8x8,font8x14=koi8-r-8x14,font8x16=koi8-r-8x16" },
+ { NULL } },
+};
+
+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,
+ { { "Add user", "Add a new user to the system.", NULL, userAddUser },
+ { "Add group", "Add a new user group to the system.", NULL, userAddGroup },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { 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 2nd FreeBSD CDROM, 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",
+{ { "1 CDROM", "Use the 2nd \"live\" CDROM from the distribution", NULL, installFixitCDROM },
+ { "2 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "3 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..bb00639
--- /dev/null
+++ b/usr.sbin/sade/misc.c
@@ -0,0 +1,485 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $Id: misc.c,v 1.37 1998/01/16 15:07:55 jkh Exp $
+ *
+ * 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/dkbad.h>
+#include <sys/disklabel.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 %d!", 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 %d!", size);
+ ptr = realloc(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 *)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 = 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
+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;
+}
+
+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..e61b767
--- /dev/null
+++ b/usr.sbin/sade/msg.c
@@ -0,0 +1,320 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: msg.c,v 1.46 1997/09/09 16:27:50 jkh Exp $
+ *
+ * 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 <machine/console.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;
+
+ 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);
+}
+
+/* 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_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 1 for YES, 0 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+
+ 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);
+ }
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ 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;
+
+ 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);
+ 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;
+
+ 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();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm(str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify(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..c6e7fcb
--- /dev/null
+++ b/usr.sbin/sade/sade.8
@@ -0,0 +1,802 @@
+.\" 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.
+.\"
+.\" $Id: sysinstall.8,v 1.15 1999/02/05 09:28:16 jkh Exp $
+.\"
+.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
+.Nm
+is a utility for installing and configuring FreeBSD systems.
+It is the first utility invoked by the FreeBSD installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed FreeBSD systems for use in later configuring the system.
+.Pp
+The
+.Nm
+program is generally invoked without arguments for the default
+behavior, where the main installation/configuration menu is presented.
+
+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.
+.Pp
+.Sh NOTES
+.Nm
+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 FreeBSD systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the FreeBSD 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
+.Nm
+currently uses the
+.Xr libdialog 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 ``xterm-color'' termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+be replaced in FreeBSD 3.1 (hopefully) by the
+.Xr setup 1
+utility.
+.Sh RUNNING SCRIPTS
+.Nm
+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 -compact
+.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:
+.nf
+
+/stand/sysinstall _ftpPath=ftp:/ziggy/pub/ mediaSetFTP configPackages
+
+.fi
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Pp
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+
+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. When and where a function
+depends on the settings of one or more variables will be noted in the
+following table:
+
+.Pp
+\fBFunction Glossary:\fR
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+\fBVariables:\fR None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g. ``routed'' or ``gated'', otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+\fBVariables:\fR None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It configRegister
+Register the user with the FreeBSD counter.
+.Pp
+\fBVariables:\fR None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+\fBVariables:\fR None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+\fBVariables:\fR None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+\fBVariables:\fR
+.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 FreeBSD,
+.Ar all
+to use the entire disk for FreeBSD but maintain a proper partition
+table,
+.Ar existing
+to use an existing FreeBSD partition (first found),
+.Ar exclusive
+to use the disk in ``dangerously dedicated'' mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new FreeBSD 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.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, a 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
+\fBVariables:\fR 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 FreeBSD
+(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
+FreeBSD partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar da0s2
+for the whole FreeBSD 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 FreeBSD. 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"
+With the balance of free space (around 316MB) going to the /usr
+file system.
+.El
+
+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:
+.nf
+ da0s1=/dos_c N
+
+.fi
+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
+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
+\fBVariables:\fR None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+\fBVariables:\fR None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+\fBVariables:\fR
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indent
+.It Li bin
+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 des
+DES encryption binaries and libraries.
+.It Li compat1x
+Compatibility with FreeBSD 1.x
+.It Li compat20
+Compatibility with FreeBSD 2.0
+.It Li compat21
+Compatibility with FreeBSD 2.1
+.It Li ports
+The ports collection.
+.It Li krb
+Kerberos binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sebones
+/usr/src/eBones
+.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 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 3.3.3.1 binaries.
+.It Li Xcfg
+XFree86 3.3.3.1 configuration files.
+.It Li Xdoc
+XFree86 3.3.3.1 documentation.
+.It Li Xhtml
+XFree86 3.3.3.1 HTML documentation.
+.It Li Xlib
+XFree86 3.3.3.1 libraries.
+.It Li Xlk98
+XFree86 3.3.3.1 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 3.3.3.1 server link-kit for standard machines.
+.It Li Xman
+XFree86 3.3.3.1 manual pages.
+.It Li Xprog
+XFree86 3.3.3.1 programmer's distribution.
+.It Li Xps
+XFree86 3.3.3.1 postscript documentation.
+.It Li Xset
+XFree86 3.3.3.1 graphical setup tool.
+.It Li X8514
+XFree86 3.3.3.1 8514 server.
+.It Li X9480
+XFree86 3.3.3.1 PC98 8-bit (256 color) PEGC-480 server.
+.It Li X9EGC
+XFree86 3.3.3.1 PC98 4-bit (16 color) EGC server.
+.It Li X9GA9
+XFree86 3.3.3.1 PC98 GA-968V4/PCI (S3 968) server.
+.It Li X9GAN
+XFree86 3.3.3.1 PC98 GANB-WAP (cirrus) server.
+.It Li X9LPW
+XFree86 3.3.3.1 PC98 PowerWindowLB (S3) server.
+.It Li X9NKV
+XFree86 3.3.3.1 PC98 NKV-NEC (cirrus) server.
+.It Li X9NS3
+XFree86 3.3.3.1 PC98 NEC (S3) server.
+.It Li X9SPW
+XFree86 3.3.3.1 PC98 SKB-PowerWindow (S3) server.
+.It Li X9TGU
+XFree86 3.3.3.1 PC98 Cyber9320 and TGUI9680 server.
+.It Li X9WEP
+XFree86 3.3.3.1 PC98 WAB-EP (cirrus) server.
+.It Li X9WS
+XFree86 3.3.3.1 PC98 WABS (cirrus) server.
+.It Li X9WSN
+XFree86 3.3.3.1 PC98 WSN-A2F (cirrus) server.
+.It Li XAGX
+XFree86 3.3.3.1 8 bit AGX server.
+.It Li XI128
+XFree86 3.3.3.1 #9 Imagine I128 server.
+.It Li XMa8
+XFree86 3.3.3.1 ATI Mach8 server.
+.It Li XMa32
+XFree86 3.3.3.1 ATI Mach32 server.
+.It Li XMa64
+XFree86 3.3.3.1 ATI Mach64 server.
+.It Li XMono
+XFree86 3.3.3.1 monochrome server.
+.It Li XP9K
+XFree86 3.3.3.1 P9000 server.
+.It Li XS3
+XFree86 3.3.3.1 S3 server.
+.It Li XS3V
+XFree86 3.3.3.1 S3 Virge server.
+.It Li XSVGA
+XFree86 3.3.3.1 SVGA server.
+.It Li XVG16
+XFree86 3.3.3.1 VGA16 server.
+.It Li XW32
+XFree86 3.3.3.1 ET4000/W32, /W32i and /W32p server.
+.It Li Xnest
+XFree86 3.3.3.1 nested X server.
+.It Li Xvfb
+XFree86 3.3.3.1 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 3.3.3.1 base font set.
+.It Li Xf100
+XFree86 3.3.3.1 100DPI font set.
+.It Li Xfcyr
+XFree86 3.3.3.1 Cyrillic font set.
+.It Li Xfscl
+XFree86 3.3.3.1 scalable font set.
+.It Li Xfnon
+XFree86 3.3.3.1 non-english font set.
+.It Li Xfsrv
+XFree86 3.3.3.1 font server.
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+\fBVariables:\fR None
+.It distSetDES
+Interactively select DES subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetXF86
+Interactively select XFree86 3.3.3.1 subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+\fBVariables:\fR None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest lynx package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to lynx.
+.El
+.It installCommit
+.Pp
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+\fBVariables:\fR None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+\fBVariables:\fR None
+.It installNovice
+Start a "novice" installation, the most user-friendly
+installation type available.
+.Pp
+\fBVariables:\fR None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+\fBVariables:\fR None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init.
+.Pp
+\fBVariables:\fR None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+\fBVariables:\fR None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+\fBVariables:\fR None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+\fBVariables:\fR None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+\fBVariables:\fR None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It file
+The fully pathname of the file to load.
+.El
+.It mediaSetCDROM
+Select a FreeBSD CDROM as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+\fBVariables:\fR None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+\fBVariables:\fR
+.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 FreeBSD
+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
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the FreeBSD distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+\fBVariables:\fR
+.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 FreeBSD distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+\fBVariables:\fR None
+.It register
+Bring up the FreeBSD registration form.
+.Pp
+\fBVariables:\fR None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+\fBVariables:\fR None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+\fBVariables:\fR None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+\fBVariables:\fR
+.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
+.El
+.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/release/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted approximately 3 years past
+its expiration date and is greatly in need of death.
+.Sh AUTHOR
+Jordan K. Hubbard <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..8f171d1
--- /dev/null
+++ b/usr.sbin/sade/sade.h
@@ -0,0 +1,722 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: sysinstall.h,v 1.159 1999/03/19 10:54:38 jkh Exp $
+ *
+ * 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 _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 ***/
+
+/* device limits */
+#define DEV_NAME_MAX 64 /* 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_DISK "disk"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_DES "distDES"
+#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_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_GATED_PKG "gated_pkg"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INTERFACES "network_interfaces"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_MOUSED "moused_enable"
+#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_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#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_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PCNFSD_PKG "pcnfsd_pkg"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "routerflags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_XF86_CONFIG "_xf86config"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+
+/* 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 unsigned int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+ dialogMenuItem items[0]; /* Array of menu items */
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_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,
+} 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;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} PartType;
+
+/* The longest newfs command we'll hand to system() */
+#define NEWFS_CMD_MAX 256
+
+typedef struct _part_info {
+ Boolean newfs;
+ char mountpoint[FILENAME_MAX];
+ char newfs_cmd[NEWFS_CMD_MAX];
+} 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 */
+} 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 {
+ 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 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 DESDists; /* 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 */
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+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 MenuMedia; /* Media type menu */
+extern DMenu MenuMouse; /* Mouse type menu */
+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 MenuStartup; /* Startup services menu */
+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 MenuNetworking; /* Network configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuDESDistributions; /* DES 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 MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuDiskDevices; /* 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 */
+
+/* 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, ...);
+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 configRegister(dialogMenuItem *self);
+extern int configResolv(dialogMenuItem *self);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXEnvironment(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+
+/* crc.c */
+extern int crc(int, unsigned long *, unsigned long *);
+
+/* 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);
+
+/* disks.c */
+extern int diskPartitionEditor(dialogMenuItem *self);
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+extern void diskPartition(Device *dev);
+
+/* 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 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 distSetDES(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);
+
+/* 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 plist);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installNovice(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int installFixupBin(dialogMenuItem *self);
+extern int installFixupXFree(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);
+
+/* lndir.c */
+extern int lndir(char *from, char *to);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+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 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 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 Mount(char *, void *data);
+extern WINDOW *openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height);
+extern ComposeObj *initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max);
+extern int layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj,
+ int *n, int max, int *cbutton, int *cancel);
+
+extern WINDOW *savescr(void);
+extern void restorescr(WINDOW *w);
+extern char *sstrncpy(char *dst, const char *src, int size);
+
+/* mouse.c */
+extern int mousedTest(dialogMenuItem *self);
+extern int mousedDisable(dialogMenuItem *self);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...);
+extern void msgYap(char *fmt, ...);
+extern void msgWarn(char *fmt, ...);
+extern void msgDebug(char *fmt, ...);
+extern void msgError(char *fmt, ...);
+extern void msgFatal(char *fmt, ...);
+extern void msgConfirm(char *fmt, ...);
+extern void msgNotify(char *fmt, ...);
+extern void msgWeHaveOutput(char *fmt, ...);
+extern int msgYesNo(char *fmt, ...);
+extern char *msgGetInput(char *buf, char *fmt, ...);
+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_exists(char *name);
+
+/* register.c */
+extern int registerOpenDialog(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 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, ...);
+
+/* 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);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int dump_variables(dialogMenuItem *self);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sade/system.c b/usr.sbin/sade/system.c
new file mode 100644
index 0000000..f916555
--- /dev/null
+++ b/usr.sbin/sade/system.c
@@ -0,0 +1,396 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: system.c,v 1.90 1999/02/14 21:26:29 jkh Exp $
+ *
+ * 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 <sys/reboot.h>
+#include <machine/console.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.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 void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("Are you sure you want to abort the installation?"))
+ systemShutdown(-1);
+ else
+ restorescr(save);
+}
+
+/* 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)
+{
+ int i;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd, type;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1)
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ 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.
+ */
+ type = 0; /* normal */
+ if (OnVTY) {
+ int fd2;
+
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ 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");
+ i = 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);
+ }
+ 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);
+ (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);
+#ifdef __alpha__
+ reboot(RB_HALT);
+#else
+ reboot(0);
+#endif
+ }
+ else
+ exit(status);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+
+ 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;
+ return status;
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+
+ 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);
+ }
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", 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, "/usr/src/release/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/release/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[])
+{
+ extern void init_acs(void);
+
+ if (OnVTY) {
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ init_acs();
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ init_acs();
+ 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)
+{
+ if (OnVTY && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgYesNo("There seems to be an emergency holographic shell\n"
+ "already running von VTY 4.\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 ((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);
+ 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");
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ }
+ }
+}
diff --git a/usr.sbin/sade/termcap.c b/usr.sbin/sade/termcap.c
new file mode 100644
index 0000000..1569479
--- /dev/null
+++ b/usr.sbin/sade/termcap.c
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <machine/console.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 } };
+
+ 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("Your choice: (1-4) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 5) {
+ *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 (getenv("SYSINSTALL_DEBUG"))
+ 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));
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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/variable.c b/usr.sbin/sade/variable.c
new file mode 100644
index 0000000..ab39a47
--- /dev/null
+++ b/usr.sbin/sade/variable.c
@@ -0,0 +1,224 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: variable.c,v 1.24 1998/07/18 09:42:02 jkh Exp $
+ *
+ * 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"
+
+/* 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);
+ 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);
+ 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!");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2()\n");
+ 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, prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is equal to value of
+ variable stored in env */
+int
+variable_check(char *data)
+{
+ char *cp, *cp2, *cp3, tmp[256];
+
+ if (!data)
+ return FALSE;
+ 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 = getenv(tmp);
+ if (cp2) {
+ if (!*cp)
+ return TRUE;
+ else
+ return !strcmp(cp, cp2);
+ }
+ else
+ return FALSE;
+ }
+ else
+ return getenv(tmp) ? TRUE : FALSE;
+}
+
+int
+dump_variables(dialogMenuItem *unused)
+{
+ FILE *fp;
+ Variable *vp;
+
+ if (isDebug())
+ msgDebug("Writing sysinstall variables to file..");
+
+ 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;
+}
diff --git a/usr.sbin/sade/wizard.c b/usr.sbin/sade/wizard.c
new file mode 100644
index 0000000..0009666
--- /dev/null
+++ b/usr.sbin/sade/wizard.c
@@ -0,0 +1,206 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: wizard.c,v 1.12 1998/09/08 10:46:40 jkh Exp $
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+u_char mbr[] = {
+ 250,51,192,142,208,188,0,124,139,244,80,7,80,31,251,252,191,0,6,185,0,1,
+ 242,165,234,29,6,0,0,190,190,7,179,4,128,60,128,116,14,128,60,0,117,28,
+ 131,198,16,254,203,117,239,205,24,139,20,139,76,2,139,238,131,198,16,254,
+ 203,116,26,128,60,0,116,244,190,139,6,172,60,0,116,11,86,187,7,0,180,14,
+ 205,16,94,235,240,235,254,191,5,0,187,0,124,184,1,2,87,205,19,95,115,12,
+ 51,192,205,19,79,117,237,190,163,6,235,211,190,194,6,191,254,125,129,61,
+ 85,170,117,199,139,245,234,0,124,0,0,73,110,118,97,108,105,100,32,112,97,
+ 114,116,105,116,105,111,110,32,116,97,98,108,101,0,69,114,114,111,114,32,
+ 108,111,97,100,105,110,103,32,111,112,101,114,97,116,105,110,103,32,115,
+ 121,115,116,101,109,0,77,105,115,115,105,110,103,32,111,112,101,114,97,
+ 116,105,110,103,32,115,121,115,116,101,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,
+ 1,1,0,4,15,63,60,63,0,0,0,241,239,0,0,0,0,1,61,5,15,63,243,48,240,0,0,144,
+ 208,2,0,0,0,1,244,165,15,63,170,192,192,3,0,144,208,2,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,85,170
+};
+
+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/r");
+ 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;
+
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf(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;
+ }
+ 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;
+ }
+ 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));
+ Free_Disk(d);
+ d = Open_Disk(d->name);
+ 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");
+ printf("create offset size enum subtype flags\n");
+ 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("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+}
diff --git a/usr.sbin/sendmail/Makefile b/usr.sbin/sendmail/Makefile
new file mode 100644
index 0000000..9b2ac5e
--- /dev/null
+++ b/usr.sbin/sendmail/Makefile
@@ -0,0 +1,44 @@
+# @(#)Makefile 8.8 (Berkeley) 3/28/97
+# $Id$
+
+SMDIR= ${.CURDIR}/../../contrib/sendmail/src
+.PATH: ${SMDIR}
+
+PROG= sendmail
+
+# 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
+
+CFLAGS+=-I${SMDIR} ${DBMDEF} ${NIS} -DTCPWRAPPERS ${MAPS}
+CFLAGS+=-D_FFR_MAX_MIME_HEADER_LENGTH
+CFLAGS+=-D_FFR_MAX_HEADERS_LENGTH
+
+SRCS= alias.c arpadate.c clock.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 mime.c parseaddr.c queue.c readcf.c recipient.c \
+ safefile.c savemail.c snprintf.c srvrsmtp.c stab.c stats.c \
+ sysexits.c trace.c udb.c usersmtp.c util.c version.c
+DPADD= ${LIBUTIL} ${LIBWRAP}
+LDADD= -lutil -lwrap
+MAN1= mailq.1 newaliases.1
+MAN5= aliases.5
+MAN8= sendmail.8
+LINKS= ${BINDIR}/sendmail /usr/bin/newaliases \
+ ${BINDIR}/sendmail /usr/bin/mailq \
+ ${BINDIR}/sendmail /usr/bin/hoststat \
+ ${BINDIR}/sendmail ${BINDIR}/purgestat
+BINMODE=4555
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
+ ${DESTDIR}/var/log/sendmail.st
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${SMDIR}/sendmail.hf \
+ ${DESTDIR}/usr/share/misc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sgsc/Makefile b/usr.sbin/sgsc/Makefile
new file mode 100644
index 0000000..a25675d
--- /dev/null
+++ b/usr.sbin/sgsc/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+PROG= sgsc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sgsc/sgsc.1 b/usr.sbin/sgsc/sgsc.1
new file mode 100644
index 0000000..0cbf44c
--- /dev/null
+++ b/usr.sbin/sgsc/sgsc.1
@@ -0,0 +1,97 @@
+.\" sgsc(1) - manual page for the `gsc' scanner device driver utility
+.\"
+.\"
+.\" Copyright (c) 1995 Gunther Schadow. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Gunther Schadow.
+.\" 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 6, 1995
+.Dt SGSC 1
+.Os
+.Sh NAME
+.Nm sgsc
+.Nd set the options of the gsc scanner device
+.Sh SYNOPSIS
+.Nm sgsc
+.Op Fl sq
+.Op Fl b Ar len
+.Op Fl f Ar file
+.Op Fl h Ar height
+.Op Fl r Ar resolution
+.Op Fl t Ar timeout
+.Op Fl w Ar width
+.Sh DESCRIPTION
+The
+.Nm
+utility provides shell level access to the ioctl
+requests served by the handy scanner device driver gsc. Please see
+.Xr gsc 4
+for the exact meaning of the requests. Generally they modify
+the output and behaviour of the gsc scanner device. When
+.Nm
+is called with no option only the current settings are reported.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl s Ar [GSC_SRESSW]
+Set the scanner with the values of the resolution selector switch.
+.It Fl q
+Operate in quiet mode, i.e. do not report any of the current settings.
+Normally the parameters are shown after the modifications have been
+performed.
+.It Fl f Ar file
+Operate on a different scanner device node given by filename. Note
+that even though there may exist more than one node of scanner device
+several of them will refer the same device unit. The modifications are
+performed od the unit regardless of the device node by which it is
+accessed.
+.It Fl r Ar resolution [GSC_SRES]
+Set the resolution in dpi.
+.It Fl w Ar width [GSC_SWIDHT]
+Set the width of the bitmap in pixels.
+.It Fl h Ar height [GSC_SHEIGHT]
+Set the height of the bitmap in lines of pixels.
+.It Fl b Ar len [GSC_SBLEN]
+Set the internal dma buffer to be
+.Ar len
+lines in size.
+.It Fl t Ar timeout [GSC_SBTIME]
+Set the timeout time for reading one dma buffer.
+.El
+.Sh FILES
+.Bl -tag -width /dev/gsc0p -compact
+.It Pa /dev/gsc0
+device node for
+.Em raw
+output
+.It Pa /dev/gsc0p
+device node for output in
+.Em pbm
+file format
+.El
+.Sh SEE ALSO
+.Xr gsc 4
+.Sh AUTHORS
+.An Gunther Schadow Aq gusw@fub46.zedat.fu-berlin.de
diff --git a/usr.sbin/sgsc/sgsc.c b/usr.sbin/sgsc/sgsc.c
new file mode 100644
index 0000000..f9850c7
--- /dev/null
+++ b/usr.sbin/sgsc/sgsc.c
@@ -0,0 +1,163 @@
+/* sgsc(1) - utility for the `gsc' scanner device driver
+ *
+ *
+ * Copyright (c) 1995 Gunther Schadow. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gunther Schadow.
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/gsc.h>
+
+#ifndef DEFAULT_FILE
+#define DEFAULT_FILE "/dev/gsc0"
+#endif
+#ifdef FAIL
+#undef FAIL
+#endif
+#define FAIL -1
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: sgsc [-sq] [-f file] [-r dpi] [-w width] [-h height]",
+ " [-b len] [-t time]");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char c;
+ int fd;
+
+ char *file = DEFAULT_FILE;
+
+ int show_dpi = 0;
+ int show_width = 0;
+ int show_height = 0;
+ int show_blen = 0;
+ int show_btime = 0;
+ int show_all = 1;
+
+ int set_blen = 0;
+ int set_dpi = 0;
+ int set_width = 0;
+ int set_height = 0;
+ int set_btime = 0;
+ int set_switch = 0;
+
+ if (argc == 0) usage();
+
+ while( (c = getopt(argc, argv, "sqf:b:r:w:h:t:")) != FAIL)
+ {
+ switch(c) {
+ case 'f': file = optarg; break;
+ case 'r': set_dpi = atoi(optarg); break;
+ case 'w': set_width = atoi(optarg); break;
+ case 'h': set_height = atoi(optarg); break;
+ case 'b': set_blen = atoi(optarg); break;
+ case 't': set_btime = atoi(optarg); break;
+ case 's': set_switch = 1; break;
+ case 'q': show_all = 0; break;
+ default: usage();
+ }
+ }
+
+ fd = open(file, O_RDONLY);
+ if ( fd == FAIL )
+ err(1, "%s", file);
+
+ if (set_switch != 0)
+ {
+ if(ioctl(fd, GSC_SRESSW) == FAIL)
+ err(1, "GSC_SRESSW");
+ }
+
+ if (set_dpi != 0)
+ {
+ if(ioctl(fd, GSC_SRES, &set_dpi) == FAIL)
+ err(1, "GSC_SRES");
+ }
+
+ if (set_width != 0)
+ {
+ if(ioctl(fd, GSC_SWIDTH, &set_width) == FAIL)
+ err(1, "GSC_SWIDTH");
+ }
+
+ if (set_height != 0)
+ {
+ if(ioctl(fd, GSC_SHEIGHT, &set_height) == FAIL)
+ err(1, "GSC_SHEIGHT");
+ }
+
+ if (set_blen != 0)
+ {
+ if(ioctl(fd, GSC_SBLEN, &set_blen) == FAIL)
+ err(1, "GSC_SBLEN");
+ }
+
+ if (set_btime != 0)
+ {
+ if(ioctl(fd, GSC_SBTIME, &set_btime) == FAIL)
+ err(1, "GSC_SBTIME");
+ }
+
+ if (show_all != 0)
+ {
+ if(ioctl(fd, GSC_GRES, &show_dpi) == FAIL)
+ err(1, "GSC_GRES");
+ if(ioctl(fd, GSC_GWIDTH, &show_width) == FAIL)
+ err(1, "GSC_GWIDTH");
+ if(ioctl(fd, GSC_GHEIGHT, &show_height) == FAIL)
+ err(1, "GSC_GHEIGHT");
+ if(ioctl(fd, GSC_GBLEN, &show_blen) == FAIL)
+ err(1, "GSC_GBLEN");
+ if(ioctl(fd, GSC_GBTIME, &show_btime) == FAIL)
+ err(1, "GSC_GBTIME");
+
+ printf("%s:\n", file);
+ printf("resolution\t %d dpi\n", show_dpi);
+ printf("width\t\t %d\n", show_width);
+ printf("height\t\t %d\n",show_height);
+ printf("buffer length\t %d\n", show_blen);
+ printf("buffer timeout\t %d\n", show_btime);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/sicontrol/Makefile b/usr.sbin/sicontrol/Makefile
new file mode 100644
index 0000000..48dab58
--- /dev/null
+++ b/usr.sbin/sicontrol/Makefile
@@ -0,0 +1,5 @@
+PROG= sicontrol
+SRCS= sicontrol.c
+MAN8= sicontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sicontrol/sicontrol.8 b/usr.sbin/sicontrol/sicontrol.8
new file mode 100644
index 0000000..7360b37
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.8
@@ -0,0 +1,114 @@
+.\" $Id: sicontrol.8,v 1.7 1997/10/15 06:43:52 charnier Exp $
+.\" The following requests are required for all man pages.
+.Dd September 26,1995
+.Dt SICONTROL 8
+.Os FreeBSD
+.Sh NAME
+.Nm sicontrol
+.Nd Specialix SI/XIO driver configuration and debugging
+.Sh SYNOPSIS
+.Nm sicontrol
+device
+.Ar command Op Cm Ar param ...
+.Sh DESCRIPTION
+.Nm Sicontrol
+is used to configure and monitor the SI/XIO device driver.
+.Pp
+.Nm Sicontrol
+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 '/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
+.\" The following requests should be uncommented and used where appropriate.
+.\" This next request is for sections 2 and 3 function return values only.
+.\" .Sh RETURN VALUES
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" .Sh ENVIRONMENT
+.Sh FILES
+.Bl -tag -width /dev/si_control -compact
+.It Pa /dev/si_control
+global driver control file for use by
+.Xr sicontrol 8 .
+.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 EXAMPLES
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" (command return values (to shell) and fprintf/stderr type diagnostics)
+.Sh DIAGNOSTICS
+Generally self explanatory.....
+.\" The next request is for sections 2 and 3 error and signal handling only.
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr si 4 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8 .
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Nm Sicontrol
+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..cda0346
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.c
@@ -0,0 +1,587 @@
+/*
+ * 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[] =
+ "$Id: sicontrol.c,v 1.7 1997/10/15 06:43:54 charnier Exp $";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.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/device.h>
+#include <sys/tty.h>
+
+#include <machine/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 __P((int, char **));
+void debug __P((int, char **));
+void dostat __P((void));
+int getnum __P((char *));
+int islevel __P((char *));
+int lvls2bits __P((char *));
+void mstate __P((int, char **));
+void nport __P((int, char **));
+void onoff __P((int, char **, int, char *, char *, int));
+int opencontrol __P((void));
+void prlevels __P((int));
+void prusage __P((int, int));
+void rxint __P((int, char **));
+void tty_stat __P((int, char **));
+void txint __P((int, char **));
+
+struct opt {
+ char *o_name;
+ void (*o_func)();
+} 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)();
+} 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(argc, argv)
+ char **argv;
+{
+ struct opt *op;
+ void (*func)() = 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, "/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()
+{
+ 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(strn, eflag)
+ int strn, 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()
+{
+ 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(ac, av)
+ 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(ac, av)
+ 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(ac, av)
+ 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(ac, av, cmd, cmdstr, prstr, usage)
+ char **av, *cmdstr, *prstr;
+ int ac, cmd, 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(ac, av)
+ 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(ac, av)
+ 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(ac, av)
+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(ac, av)
+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 0x%x\n", 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(tk)
+ char *tk;
+{
+ register struct lv *lvp;
+ register 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(str)
+ 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;
+ register 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(x)
+ 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..8e9dbb2
--- /dev/null
+++ b/usr.sbin/sliplogin/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= sliplogin
+MAN8= sliplogin.8
+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..3bee951
--- /dev/null
+++ b/usr.sbin/sliplogin/pathnames.h
@@ -0,0 +1,47 @@
+/*-
+ * 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.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#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_LOGIN "/etc/sliphome/slip.login"
+#define _PATH_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..9d10175
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.8
@@ -0,0 +1,313 @@
+.\" 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
+.\"
+.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
+.Nm Sliplogin
+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.
+.Pp
+.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 EXAMPLE
+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
+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 sliplogin .
+The system administrator should make sure that all legitimate users
+are a member of the correct group.
+.Sh DIAGNOSTICS
+.Nm Sliplogin
+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
+.Ar /etc/sliphome/slip.hosts
+list of host login names and parameters.
+.Pp
+.Ar /etc/sliphome/slip.login
+script executed when a connection is made.
+.Pp
+.Ar /etc/sliphome/slip.login.loginname
+script executed when a connection is made by
+.Ar loginname .
+.Pp
+.Ar /etc/sliphome/slip.logout
+script executed when a connection is lost.
+.Pp
+.Ar /etc/sliphome/slip.logout.loginname
+script executed when a connection is lost by
+.Ar loginname .
+.Pp
+.Ar /etc/sliphome/slip.slparms
+extra parameters file.
+.Pp
+.Ar /etc/sliphome/slip.slparms.loginname
+extra parameters file for
+.Ar loginname .
+.Pp
+.Ar /var/run/ttyXn.if
+contains the name of the network interface used by the sliplogin process on
+.Ar ttyXn .
+.Pp
+.Ar /var/run/slX.pid
+contains the PID of the sliplogin process which is using interface
+.Ar slX .
+.Pp
+.Sh SEE ALSO
+.Xr slattach 8 ,
+.Xr syslogd 8 ,
+.Pa /usr/share/examples/sliplogin
+.Sh HISTORY
+The
+.Nm
+command
+.Bt
diff --git a/usr.sbin/sliplogin/sliplogin.c b/usr.sbin/sliplogin/sliplogin.c
new file mode 100644
index 0000000..e8c2f0c
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.c
@@ -0,0 +1,543 @@
+/*-
+ * 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";
+#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 <stdio.h>
+#include <errno.h>
+#include <ctype.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;
+int i;
+
+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_LOGIN, name);
+ if (access(loginfile, R_OK|X_OK) != 0) {
+ (void)strncpy(loginfile, _PATH_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_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_LOGOUT, loginname);
+ if (access(logoutfile, R_OK|X_OK) != 0) {
+ (void)strncpy(logoutfile, _PATH_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] = "/dev/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 /dev/null: %m");
+ 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..a74a30f
--- /dev/null
+++ b/usr.sbin/slstat/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.6 (Berkeley) 4/23/91
+# $Id$
+
+PROG= slstat
+MAN8= slstat.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/slstat/slstat.8 b/usr.sbin/slstat/slstat.8
new file mode 100644
index 0000000..201add7
--- /dev/null
+++ b/usr.sbin/slstat/slstat.8
@@ -0,0 +1,128 @@
+.\" 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
+.\" $Id: slstat.8,v 1.11 1997/10/20 12:41:40 charnier Exp $
+.\"
+.Dd October 11, 1996
+.Dt SLSTAT 8
+.Os BSD 4
+.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..7385fee
--- /dev/null
+++ b/usr.sbin/slstat/slstat.c
@@ -0,0 +1,244 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: slstat.c,v 1.12 1997/10/20 12:41:41 charnier Exp $";
+#endif
+
+#include <sys/param.h>
+#include <sys/mbuf.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 __P((void));
+static void intpr __P((void));
+static void catchalarm __P((int));
+
+#define INTERFACE_PREFIX "sl%d"
+char interface[IFNAMSIZ];
+
+int rflag;
+int vflag;
+unsigned interval = 5;
+int unit;
+int name[6];
+
+int
+main(argc, argv)
+ 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)
+ 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;
+{
+ signalled = 1;
+}
diff --git a/usr.sbin/spkrtest/Makefile b/usr.sbin/spkrtest/Makefile
new file mode 100644
index 0000000..336a305
--- /dev/null
+++ b/usr.sbin/spkrtest/Makefile
@@ -0,0 +1,11 @@
+# $Id$
+
+MAINTAINER= wosch
+
+MAN8= spkrtest.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/spkrtest.pl ${DESTDIR}${BINDIR}/spkrtest
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spkrtest/spkrtest.8 b/usr.sbin/spkrtest/spkrtest.8
new file mode 100644
index 0000000..3e91132
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.8
@@ -0,0 +1,54 @@
+.\" 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.
+.\"
+.\" $Id: spkrtest.8,v 1.6 1997/06/23 04:52:10 steve Exp $
+
+.Dd July 23, 1995
+.Dt SPKRTEST 8
+.Os FreeBSD
+
+.Sh NAME
+.Nm spkrtest
+.Nd test script for the speaker driver
+
+.Sh DESCRIPTION
+.Nm Spkrtest
+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 perl 1 ,
+.Xr spkr 4
+
+.Sh HISTORY
+The
+.Nm
+script appeared in
+.Fx 1.0
diff --git a/usr.sbin/spkrtest/spkrtest.pl b/usr.sbin/spkrtest/spkrtest.pl
new file mode 100644
index 0000000..0af081a
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.pl
@@ -0,0 +1,138 @@
+#!/usr/bin/perl
+#
+# 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.
+#
+# spkrtest - Test script for the speaker driver
+#
+# v1.0 by Eric S. Raymond (Feb 1990)
+# v1.1 rightstuff contributed by Eric S. Tiedemann (est@snark.thyrsus.com)
+# v2.0 dialog+perl by Wolfram Schneider <wosch@FreeBSD.org>, May 1995
+#
+# NOTE for iso-* (latin1) fonts: use TERM=cons25-iso8859-1
+#
+# $Id: spkrtest.pl,v 1.5 1997/02/22 16:13:37 peter Exp $
+
+$title = qq{
+reveille -- Reveille
+contact -- Contact theme from Close Encounters
+dance -- Lord of the Dance (aka Simple Gifts)
+loony -- Loony Toons theme
+sinister -- standard villain's entrance music
+rightstuff -- a trope from "The Right Stuff" score by Bill Conti
+toccata -- opening bars of Bach's Toccata and Fugue in D Minor
+startrek -- opening bars of the theme from Star Trek Classic
+};
+
+$music = qq{
+reveille -- t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f..
+contact -- <cd<a#~<a#>f
+dance -- t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf.
+loony -- t255cf8f8edc<a>~cf8f8edd#e~ce8cdce8cd.<a>c8c8c#def8af8
+sinister -- mst200o2ola.l8bc.~a.~>l2d#
+rightstuff -- olcega.a8f>cd2bgc.c8dee2
+toccata -- msl16oldcd4mll8pcb-agf+4.g4p4<msl16dcd4mll8pa.a+f+4p16g4
+startrek -- l2b.f+.p16a.c+.p l4mn<b.>e8a2mspg+e8c+f+8b2
+};
+
+@checklist = ('/usr/bin/dialog', '--title', 'Speaker test', '--checklist',
+ 'Please select the melodies you wish to play (space for select)',
+ '-1', '-1', '10');
+
+$speaker = '/dev/speaker';
+
+sub Exit {
+ unlink $tmp if $tmp; exit;
+}
+
+$SIG{'INT'} = $SIG{'HUP'} = $SIG{'TRAP'} = $SIG{'QUIT'} =
+ $SIG{'TERM'} = '&Exit';
+
+
+# make assoc array from variable 'var'
+# 'name -- description' -> $var{$name} = $description
+sub splitconfig {
+ local(*var) = @_;
+ local($t, $name, $description);
+
+ foreach $t (split('\n', $var)) {
+ ($name, $description) = split('--', $t);
+
+ $name =~ s/^\s+//; $name =~ s/\s+$//;
+ $description =~ s/\s+//; $description =~ s/\s+$//;
+
+ $var{$name} = $description if $name && $description;
+ }
+}
+
+&splitconfig(*title);
+&splitconfig(*music);
+
+foreach (sort keys %title) {
+ push(@checklist, ($_, $title{$_}, 'OFF'));
+}
+
+srand;
+$tmp = ($ENV{'TMP'} || "/tmp") . "/_spkrtest" . rand(9999);
+
+if (!open(SPEAKER, "> $speaker")) {
+ warn "You have no write access to $speaker or the speaker device is not " .
+ "enabled\nin kernel. Cannot play any melody! See spkr(4).\a\n";
+ sleep 2;
+}
+
+open(SAVEERR, ">&STDERR") || die "open >&STDERR: $!\n";
+open(STDERR, "> $tmp") || do { die "open > $tmp: $!\n"; };
+system @checklist; # start dialog
+open(STDERR, ">&SAVEERR") || die "open >&SAVEERR: $!\n";
+$return = $? >> 8;
+
+# die if speaker device not avaiable
+if (fileno(SPEAKER) eq "") {
+ print "\nSorry, cannot play any melody!!!\n"; &Exit;
+}
+
+
+if (!$return) { # not cancel
+ select(SPEAKER); $| = 1;
+ select(STDOUT); $| = 1;
+
+ if (! -z $tmp) { # select melod(y/ies)
+ print STDOUT "\n";
+ open(STDIN, $tmp) || do { die "open $tmp: $!\n"; };
+ foreach $melody (split($", <STDIN>)) {
+ $melody =~ s/^"//; $melody =~ s/"$//;
+ print STDOUT "$title{$melody}\n";
+ print SPEAKER "$music{$melody}";
+ sleep 1;
+ }
+ } else { # use default melody
+ $melody = (sort keys %title)[0];
+ print STDOUT "Use default melody: $title{$melody}\n";
+ print SPEAKER "$music{$melody}";
+ }
+ close SPEAKER;
+}
+
+&Exit;
diff --git a/usr.sbin/spray/Makefile b/usr.sbin/spray/Makefile
new file mode 100644
index 0000000..d008c7f
--- /dev/null
+++ b/usr.sbin/spray/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= spray
+MAN8= 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..d57abd9
--- /dev/null
+++ b/usr.sbin/spray/spray.8
@@ -0,0 +1,72 @@
+.\"
+.\" 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.
+.\"
+.Dd July 10, 1995
+.Dt SPRAY 8
+.Os FreeBSD
+.Sh NAME
+.Nm spray
+.Nd send many packets to host
+.Sh SYNOPSIS
+.Nm spray
+.Op Fl c Ar count
+.Op Fl d Ar delay
+.Op Fl l Ar length
+.Ar host
+.Sh DESCRIPTION
+.Nm Spray
+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.
+.Nm Spray
+rounds up to the nearest possible value.
+.El
+.Pp
+.Nm Spray
+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" .
+.Pp
+.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..d6f53f1
--- /dev/null
+++ b/usr.sbin/spray/spray.c
@@ -0,0 +1,226 @@
+/*
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#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 print_xferstats ();
+
+/* 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(argc, argv)
+ int argc;
+ char **argv;
+{
+ spraycumul host_stats;
+ sprayarr host_array;
+ CLIENT *cl;
+ int c;
+ int i;
+ 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) {
+ clnt_pcreateerror("spray");
+ exit(1);
+ }
+
+
+ /*
+ * 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, (caddr_t)&NO_DEFAULT);
+
+
+ /* Clear server statistics */
+ if (clnt_call(cl, SPRAYPROC_CLEAR, xdr_void, NULL, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(cl, "spray");
+ exit(1);
+ }
+
+
+ /* Spray server with packets */
+ printf ("sending %d packets of lnth %d to %s ...", count, length, *argv);
+ fflush (stdout);
+
+ for (i = 0; i < count; i++) {
+ clnt_call(cl, SPRAYPROC_SPRAY, xdr_sprayarr, &host_array, xdr_void, NULL, ONE_WAY);
+
+ if (delay) {
+ usleep(delay);
+ }
+ }
+
+
+ /* Collect statistics from server */
+ if (clnt_call(cl, SPRAYPROC_GET, xdr_void, NULL, xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(cl, "spray");
+ exit(1);
+ }
+
+ 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);
+}
+
+
+void
+print_xferstats(packets, packetlen, xfertime)
+ 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 ()
+{
+ fprintf(stderr,
+ "usage: spray [-c count] [-l length] [-d delay] host\n");
+ exit(1);
+}
diff --git a/usr.sbin/stallion/Makefile b/usr.sbin/stallion/Makefile
new file mode 100644
index 0000000..44c3b63
--- /dev/null
+++ b/usr.sbin/stallion/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+SUBDIR= bootcode stlload stlstats
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/stallion/Makefile.inc b/usr.sbin/stallion/Makefile.inc
new file mode 100644
index 0000000..53b9b3e
--- /dev/null
+++ b/usr.sbin/stallion/Makefile.inc
@@ -0,0 +1,7 @@
+# $Id$
+
+BOOTDIR= /usr/libdata/stallion
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/stallion/bootcode/2681.sys.uu b/usr.sbin/stallion/bootcode/2681.sys.uu
new file mode 100644
index 0000000..db3dd28
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/2681.sys.uu
@@ -0,0 +1,690 @@
+begin 444 2681.sys
+M````````````````````````````````````````````````````````````
+M``````!E`0``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`-(1SV0``````````````````````````````````````!``````8V1K:"YC
+M```F`````````````````.P5````````````````&`8```,`````````````
+M;F\@96-P86YE;"!D<FEV97(@:6YS=&%L;&5D`&YO(&5C8G5S(&1R:79E<B!I
+M;G-T86QL960`;65M;W)Y(&UA;&QO8R!F86EL960``````%!%3D0@8W1R;&EN
+M(2`E9`!014Y$(&-T<FQI;B$@)60`4$5.1"!C=')L:6XA("5D`$%34T525$E/
+M3B!&04E,55)%(&%T("5S*"5D*0H``-@`24Y&3U)-`%=!4DY)3D<`1D%404P`
+M4$%.24,`15)23U(@)7,Z("5S*"5D*3H@``H`^`#_``<!#0$3`3$N,2XP`"A#
+M*2!#;W!Y<FEG:'0@4W1A;&QI;VX@5&5C:&YO;&]G:65S(#$Y.3,@+2`Q.3DV
+M``H*+2TM+2TM+2TM+2TM+2TM("!3=&%L;&EO;B!);G1E;&QI9V5N="!#;VUM
+M=6YI8V%T:6]N<R!0<F]C97-S;W(@("TM+2TM+2TM+2TM+2TM+0H`("`@("`@
+M("`@("`@("`@("5S"@H`("!";V%R9"!4>7!E("`@("`@("`@("`@("`@("`@
+M("4R9"`@("`@("`@("`@($-O9&4@5F5R<VEO;B`@("`@("`@("`@("`@("5S
+M"@`@($UE;6]R>2!4;W1A;"`@("`@("`@("`@,'@E,#9L>"`@("`@("`@("`@
+M($UE;6]R>2!5<V5D("`@("`@("`@("`@(#!X)3`V;'@*`"`@365M;W)Y($9R
+M964@("`@("`@("`@("`P>"4P-FQX("`@("`@("`@("`@365M;W)Y($UA<"`@
+M("`@("`@("`@("`@,'@E,#9L>`H`("!#;V1E(%-I>F4@("`@("`@("`@("`@
+M(#!X)3`V;'@@("`@("`@("`@("!$871A(%-I>F4@("`@("`@("`@("`@("`P
+M>"4P-FQX"@`@($-O9&4@4V5G;65N="`@("`@("`@("`@("`P>"4P-'@@("`@
+M("`@("`@("!$871A(%-E9VUE;G0@("`@("`@("`@("`@(#!X)3`T>`H`("!3
+M=&%C:R!396=M96YT("`@("`@("`@("`@,'@E,#1X("`@("`@("`@("`@17AT
+M<F$@4V5G;65N="`@("`@("`@("`@("`P>"4P-'@*"@`@($%S>6YC:')O;F]U
+M<R!086YE;',@("`@("`@("`@("5D("`@("`@("`@("`@07-Y;F-H<F]N;W5S
+M(%!O<G1S("`@("`@("`@("`@)3)D"@`*+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+0H*```R`3@!,#$R,S0U-C<X.6%B8V1E9@``3`2O*[4K
+MNRO!*\<KS2O3*]DKWROE*^LK\2OW*_TK!"P++!(L&2P@+"<L+BPU+#PL0RQ*
+M+%$L6"Q?+&8L;2QT+'LL@BR)+)`LERR>+*4LK"RS++HLP2S(+,\LUBS=+.0L
+MZRSR+/DL`"T'+0XM%2T<+2,M*BTQ+3@M/RU&+4TM5"U;+59%0U]&055,5"`H
+M)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60I
+M```R-C@Q+F,``!H%`````````````#(```!+````;@```(8```"6````R```
+M`"P!``!8`@``L`0```@'``!@"0``P!(``(`E````2P```)8```#A``!@`&``
+MX`!@`&``X`!@`&``8`!@`.``8`!@`&``X`!@`&``````````$0`B`#,`,P!$
+M`%4`9@"J`(@`F0"[`,P`S`#,```````0`(``D````1`!@`&0`0`"$`(@`C`"
+M0`)0`F`"<`(``!``@`"0```!$`$@`3`!0`%0`6`!<`&``9`!H`&P`<`!T`'@
+M`?`!``(0`B`",`)``E`"8`)P`H`"D`*@`K`"0`!``#1-%D@``-8OIC&:,@``
+M%#<H/!0^3%$````````````````````````E9``N````````````````^KP`
+M!#/`CM".P+@``8[8,\".P":!/@`"I_)U)R:!/@("2:%U'B:!/@0"4F-U%2:!
+M/@8"(?%U#":A"@(FBQX,`NMBD+E:T#/VCL8FH0``)HD.``"^`!"+%@``CL8F
+M.0X``'0*@<8`$('^`(!RZ3/2CL(FHP``C,B[4&*#X_#!ZP0#V$..PR:A```F
+MB0X``(L6```F.0X``'4))J,``$,[WG+A,\"C#`#'!@X```"+T\'B!`$6#`"#
+M%@X``(O3P>H,`18.`*$,`(L6#@"Y4&*)#A@`@\$?@^'PB_&+^2O!@]H`P>@$
+MP>(,"]".PD].C,B.V/WSI/P&:/H`R[@``8[8N5`?B_&+^8O9B1XD``,>(`"#
+MPP^#X_"+P\'H!"O0CL).3_WSI/R,P([8CL".T(OC@^P$OU`&,\"Y4!^)#AP`
+M*\_\\ZJ,R*,&`(S8HP0`+J-P`8S0HPH`C,"C"`",V,'@!*,4`(S8P>@,HQ8`
+MZ#8>Z_X``,@$``!75HM&")F+'GX'.5<0<F]W!3E'#G)HBW<$F8OXB]H#?@03
+M7@:#[P&#VP"+S@K)=`C1Z]'?_LEU^(O'F8O/B]J+1@2+5@:)3OR)7OZ+S@K)
+M=`C1ZM'8_LEU^#M&_'4%.U;^=#F+QYF+S@K)=`C1X-'2_LEU^(E&!(E6!NL@
+MD)"9.5<0=QAR!3E'#G,1:.(`_S8N`/\V]@#HI2*#Q`:+1@2+5@9>7\G#R!``
+M`%=6BQY^!XM'$HM7%#D6%@!W'W(&.084`',7H10`BQ86`"M&!!M6!M'JT=@#
+M1@035@8K1@0;5@:)1O")5O(K_XM'#HM7$(E&](E6]CO7=00[QW0-4E#_=O+_
+M=O#HA%Z+^`O_=`B+1O8+1O1U#[\!`(M&\(M6\HE&](E6]HM&"-'@`\<KTO?W
+MB_`+]G4#O@$`:@!6_W;V_W;TZ$=>B4;XBT8(T>`#QVH`4/]V\O]V\.@Q7HE&
+M_HM&^#E&_G8&BT;^B4;X/8``<P7'1OB``(M&^%Y?R<.0R!P``%=6QT;N``/'
+M1O```,<&2A^``L<&3!\``,0>2A\FBT<$HTX?)HI''"4!`#T!`!K`_L"B6``F
+MBT<2)@M'$'0*)HM'$*-<!^@6&8M&[HM6\-'JT=C1ZM'8T>K1V-'JT=B*3NZ#
+MX0^)#EX'HV`'@4;N@`"#5O``:(``4%'H62>#Q`;$'EX')L='!@$`Q!Y>!R;'
+M1T+_`,0>7@<FQD=,`<0>7@<FQD=-`<0>7@<FQD=.`,0>2A\FBT<,B4;DBQY^
+M!XL'+1<`?`1(2'X.H4H`HT`?QP84'VA9ZQ)H60!H9P'_-BX`:@+HX1J#Q`B+
+M1N1`HU(&Q!Y>!R:)1U*A4@9(F3/"*\+!^`,SPBO"0*,$'Z%2!@4'`)DSPBO"
+MP?@#,\(KPD`D_HE&[(M&[HM6\,0>7@<FB4=8)HE76HM&["O2`4;N$5;PBT;N
+MBU;PQ!Y>!R:)1UPFB5=>BT;L*](!1NX15O!0Q!Y>!R:+1U@FBU=:T>K1V-'J
+MT=C1ZM'8T>K1V":*5UB#X@^+RE!1Z$TF@\0&_W;LQ!Y>!R:+1UPFBU=>T>K1
+MV-'JT=C1ZM'8T>K1V":*5UR#X@^+RE!1Z!TF@\0&H5(&B4;LN`8`]V;LB4;L
+MBT;NBU;PQ!Y>!R:)1U0FB5=6BT;L*](!1NX15O#$'EX')HM'5":+5U;1ZM'8
+MT>K1V-'JT=C1ZM'8)HI/5(/A#XE.Z(E&ZO]V[%!1Z+TE@\0&BQY^!XL'+1<`
+M?`1(2'X(QP92```#ZQ#'!E(```&+'GX'@W\H`'0(QT;\`0#K!I#'1OP"`&B2
+M`/]V\/]V[N@?_(/$!HE&[HE6\(M&_,1>Z":)!XM&[HM6\,1>Z":)1P(FB5<$
+M:)(`BT;NBU;PT>K1V-'JT=C1ZM'8T>K1V(I6[H/B#XO*4%'H,26#Q`:!1NZ2
+M`(-6\`"#1N@&BQY^!X-_*`!^$(M&Y"M'*)GW?RB)1O3K!I#'1O0``(-^]`!]
+M!<=&]```QT;^``#'1N8``"OVZ0(!D(M^_M'GBT$8BT[T0??I.T;F?@O$7N@F
+MQP<`!.L)D,1>Z";'!P``BT;NBU;PQ%[H)HE'`B:)5P1HO`"+1NZ+5O#1ZM'8
+MT>K1V-'JT=C1ZM'8BE;N@^(/B\I04>B*)(/$!H%&[KP`@U;P`/]&YHM^_M'G
+MBQY^!XM!&(M.]$'WZ3M&YG]YQT;F``#_1OZ#?OX(?&O$'DH?)HM'%(E&\B:+
+M1QB)1O:#?O(`=`D+P'4%QT;V``&#?O8`=`N#?O(`=07'1O(``8-^\@!U&X-^
+M]@!U%?]VY/]V\/]V[NA;^X/$!HE&\HE&]L0>7@<FBT=4)HM75HE&Z(E6ZBOV
+MZT60D$:#1N@&.7;D?HMHO`#_=O#_=N[H>OJ#Q`:)1NZ)5O"+1N:+?O[1YXL>
+M?@<Y01A_`^G1_L1>Z";'!R``Z>S^1H-&Z`8Y-E(&?P/IH@#$7N@F]P?@_W3H
+M)HM'`B:+5P31ZM'8T>K1V-'JT=C1ZM'8)HI/`H/A#XE.^(E&^O]V\O]V\/]V
+M[N@)^H/$!HE&[HE6\,1>^":)A[(`)HF7M`"+1O+$7O@FB8>V`(M&\BO2`4;N
+M$5;P_W;V_W;P_W;NZ-'Y@\0&B4;NB5;PQ%[X)HF'I@`FB9>H`(M&]L1>^":)
+MAZH`BT;V*](!1NX15O#I4/]J`&H$:*P*:@SH#B>#Q`B+1NZ+5O"CP`:)%L(&
+MHQ``B182`"O`7E_)PY#(%@``5U9K!E(&*E#HF1J#Q`*C>`:)%GH&BQY`'_\7
+MH7H&"P9X!G42:(\`:#H"_S8N`&H"Z(T6@\0(:P92!BI0_S9Z!O\V>`;HB"*#
+MQ`;$'EX')HM'5":+5U;1ZM'8T>K1V-'JT=C1ZM'8)HI/5(/A#XE.[HE&\,=&
+M]#``H7@&BQ9Z!HE&^HE6_,=&_O__QT;R`0`K_ROVZ8@!)HM'!":+5P:)1NJ)
+M5NS$7NHFBX>R`":+E[0`T>K1V-'JT=C1ZM'8T>K1V":*C[(`@^$/Q%[Z)HE/
+M"":)1PK$7NHFBX>F`":+EZ@`T>K1V-'JT=C1ZM'8T>K1V":*CZ8`@^$/Q%[Z
+M)HE/#":)1P[$7OHF]T<FX`-U`^FG`(-^\@!U`^F>`/]&_HM>_M'CB;=*!XM&
+M_HL>?@<Y1Q9^!3T(`'Q\BQY^!XM?%M'CB;=*!\0>7@<FBT=8)HM76M'JT=C1
+MZM'8T>K1V-'JT=@FBD]8@^$/B0X&'Z,('R:+1UPFBU=>T>K1V-'JT=C1ZM'8
+MT>K1V":*3UR#X0^)#F('HV0'QT;V8@+'1O@``,1>]B;&!PW$7O8F@#\`=0/I
+MY`#K\BO_B7[RB\<JY)F+R(I&_HO:F8/*_XKRBM2*X"K`"\$+T\1>^B:)!R:)
+M5P*+'D`?@W\"`'08C4;T4,1>^B;_=P(F_S>+'D`?_U<"@\0&1XM>_M'C`QY^
+M!SE_&'\%QT;R`0!&@T;Z*H-&[@8Y-E(&?P/I$?_$7OHFB7<DQ%[N)HL'Q%[Z
+M)HE')L1>[B:+1P(FBU<$T>K1V-'JT=C1ZM'8T>K1V":*3P*#X0_$7OHFB4\$
+M)HE'!HO&P>`#F?<^4@:+R+`!TN#$7OHFB$<HQ%[Z)O9')A]UC":#?R8`=`/I
+M`_[I?__$'EX')H-_0@!U!L<&4@```"O`7E_)PY#H'".#/E```'0*4.@@(X/$
+M`BO`P_\&4`!0Z!(C@\0"Z`@`QP90````Z^?(#```5U;$'EX')O]'0,0>7@<F
+M@W]0`'1"H:0`_P:D`#UD`'8*_P:F`,<&I````":`?T\`=`O'!J8````FQD=/
+M`*&F`,0>7@<F.4=0=PWH?1#$'EX')L='4```Q!Y>!R:`?V$`=&`K_^LGA7;X
+M=!1K1OHJ`P9X!HL6>@924.@0`8/$!/]&^M'FBT;^.4;Z?-I'.3X$'WXOQ!YB
+M!R:*`9B)1O@+P'3JB\?!X`.)1OH%"`")1OXY!E(&?0:A4@:)1OZ^`0#KPY#$
+M'EX')H!_8`!U`^F)`,=&]@``*__K09"%=OAT+6M&^BH#!G@&BQ9Z!E)0Z#((
+M@\0$"\!]%6M>^BH#'G@&C@9Z!B:*1R@JY`E&]O]&^M'FBT;^.4;Z?,%'.3X$
+M'WXNQ!X&'R:*`9B)1O@+P'3JB\?!X`.)1OH%"`")1OXY!E(&?0:A4@:)1OZ^
+M`0#KPXI&]L0>7@<FB$=@@SY2``!T&8,^5```=!)J`/\V4@#H*QZ#Q`3'!E0`
+M``"+'D`?@W\4`'0#_U<4*\!>7\G#R"P``%=6Q%X$)HM'!":+5P:)1NB)5NHF
+M]D<F'W0,)HI')BO2M/\*].L')HL')HM7`HE&_(E6_L1>Z":`/P!U`^E]`2:+
+M1P(FBU<$B4;@B5;B*\`FB4<$)HE'`L1>Z";&!P"+'D`?.4<&=#O$7N@F_W<$
+M)O]W`O]V_O]V_(L>0!__5P:#Q`B+^(/_`70F"_]\`XU%`8E&W)G$7N@FB4<"
+M)HE7!.L.D,1>Z";'1P(!`":)1P2+'D`?@W\*`'4#Z<L`C4;LB\B+%@0`4E%J
+M`&@-8?]V_O]V_(L>0!__5PJ#Q`R+^(/_`74B:!L#:*@`Z%X7@\0$B\=(=1%H
+M'`/_-BX`_S;V`.A(%X/$!@O_?'[V1O`$=2*`3O`$Q%X$)H!/$`C$7N@FBX>V
+M`+D%`"O2]_'$7@0FB4<2]D;P`G4,@$[P`L1>!":`3Q`0]D;Q`74,@$[Q`<1>
+M!":`3Q"`Q%X$)O9'$)AT((U&[(O(BQ8$`%)1:@!H#F'_=O[_=OR+'D`?_U<*
+M@\0,Q%X$)H!G$)O'!E0``0#$7@0FBD\D@.$'L`'2X":+7R3!ZP,#'@8?C@8(
+M'R8(!\1>!":*1RC$'EX')@A'8,1>Z":+1PPF"T<*=0/I[P+$7N@FBT<*)HM7
+M#(E&X(E6XHM&X(M6XB8Y1PIUXR8Y5PQUW2O`)HE'$":)1P[$7N@FB4<,)HE'
+M"HE&Y(M&X.E1`9"+1NB+5NH%$@")1MB)5MK$7M@F]D<$!'43Q%X$)H!/$`C$
+M7M@F@$\$!.L)D,1>!":`9Q#WQ%[8)O9'!`)U$L1>!":`3Q`0Q%[8)H!/!`+K
+M",1>!":`9Q#OQ%[8)O9'!0%U%,1>!":`3Q"`Q%[8)H!/!0'I!P&0Q%X$)H!G
+M$'_I^P"0Q%[H)HM'$HE&Y/]VY/]V!O]V!.CF"X/$!NG>`,=&Y`(`@7[@#&%U
+M!H-^X@!T#8%^X`IA==6#?N(`=<^`3N0!Z\F0QT;D`@"!?N`'874&@W[B`'0-
+M@7[@!6%U"H-^X@!U!(!.Y`'_=N3_=@;_=@3HBPN#Q`:+1NB+5NH%$@")1MB)
+M5MK$7N@FBX>V`,1>V"8Y1R1R$<1>Z":+A[8`2$C$7M@FB4<DQ%[8)H-_)`!U
+M$<1>Z":+A[8`N04`*]+W\>L$)HM'),1>!":)1Q+K+9`M`6$]#0!W)-'@DR[_
+MI]@/^@[T#_0/;@\\#SP/00_T#_0/%@\6#QL/]`]X#HL>0!^#?PH`=0/IW0#_
+M=N+_=N#HU`J#Q`0+P'0-Q%[H)HM'$B:+5Q3K"8M&Z(M6Z@42`(E&V(E6VE)0
+M_W;B_W;@_W;^_W;\BQY`'_]7"H/$#(OX"_]U8XM&X"T2875;BT;HBU;J!1(`
+MB4;4B5;6BT;H!;(`4E#H]02#Q`3$7M0F`0>+1NB+5NH%I@!24.B<!(/$!,1>
+MU"8!1P+$7N@FBX>V`$C$7M0F`4<$Q%[H)HN'J@!(Q%[4)@%'!H/_`70="_]\
+M!8U%`>L"B\>)1M29Q%[H)HE'#B:)5Q#K(9!HI@-HN`#HVQ.#Q`3K$L1>Z";'
+M1PX!`";'1Q```+___\<&5``!`,1>!":*3R2`X0>P`=+@)HM?),'K`P,>!A^.
+M!@@?)@@'Q%X$)HI'*,0>7@<F"$=@Q%X$)O9'$0%T3\=&\`(`QT;R```KP(E&
+M]HE&](E&[HE&[":`9Q'^Q%X$)H!/$"#'!E8``0"-3NR+%@0`4E%J`&@/8<1>
+M!";_=P(F_S?H=P2#Q`S'!E8```#$7@0F]D<0!'0#Z<@!BT;HBU;J!;(`4E#H
+MR0.#Q`2+\`OV?P/IK@'$7@0F.7<2=P4F@&<0W\1>!":`9Q"_QT;>``"`/E@`
+M`'0QBQY`'X-_"@!T)XU&X!90:@!H$V'_=O[_=OR+'D`?_U<*@\0,/0$`&\!`
+MB4;>"\!T""O`B4;BB4;@@W[>`74,:-H#:,@`Z*42@\0$BT;B"T;@=`/I-@&+
+M'D`?@W\0`'4#Z2D!Q%[H)HN'M@`FBX^Z`"O!.\9W#R:+A[H`)HN_M@`K^.L#
+MD(O^5\1>!":+1P@FBU<*Q%[H)HN/N@`#P5)0_W;^_W;\BQY`'_]7$(/$"HE&
+MU`O`?07'1M0``#EVU'TS.7[4=2Z+QBM&U%#$7@0F_W<*)O]W"/]V_O]V_(L>
+M0!__5Q"#Q`J+^`O_?0(K_P%^U.L"*_^#?M0`?P/IC@`Y=M1T",1>!":`3Q`$
+M*W;4Q%[H)HN'N@`!1M2+1M0F.8>V`'<#B7[4BT;4)HF'N@#$7@0F.7<2=E4F
+M]D<0*'5.*\")1O*)1O")1O:)1O2)1NZ)1NPF]D<0('4)@$[P!":`3Q`@QP96
+M``$`C4[LBQ8$`%)1:@!H#V'$7@0F_W<")O\WZ*("@\0,QP96````Q%[H)H!_
+M`0!U`^F6`":+1P8FBU<(B4;@B5;B*\`FB4<()HE'!L1>Z";&1P$`BQY`'SE'
+M"'0K_W;^_W;\_U<(@\0$B_B#_P%T)0O_?`.-10&)1M29Q%[H)HE'!B:)5PCK
+M#<1>Z";'1P8!`":)1PC'!E0``0#$7@0FBD\D@.$'L`'2X":+7R3!ZP,#'@8?
+MC@8('R8(!\1>!":*1RC$'EX')@A'8"O`7E_)P\@$``!6Q%X$)HM'!":+5P:)
+M1OR)5O[$7OPF@+^2``!T![C__U[)PY#$7@0F]D<0`71`:A`%E`!24(O#C,(%
+M%`!24.A8%H/$"L1>!":*1Q`D@CP"=0G$7OPF@(^9``'$7OPFQH>2``'$7@0F
+M@&<0_NNPD":*1Q`D@CP"=3QJ$(M&_`64`%)0Z$@6@\0&Q%[\)H"/F0`!BT;\
+MBU;^!90`4E!J`&@/8<1>!";_=P(F_S?H/@&#Q`SI:?_$7OPFBT<$)@M'`G0#
+MZ5G_)HM'""8+1P9T`^E,_R:+1Q`F"T<.=`/I/__$7@0F]D<F'W45BT;\!:8`
+M4E#H+0"#Q`0+P'0#Z2#_Q%X$)HI/)(#A![`!TN#VT":+7R3!ZP/$-@8?)B``
+M*\!>R</(`@``5U;$7@0FBW<()HM_"#OW=`J+]R:+?P@[]W7V)HM'!HE&_CEV
+M_G()BT;^*\9>7\G#)HM'!"O&`T;^7E_)PY#(`@``5U;$7@0FBW<&)HM_!CO^
+M=`J+]R:+?P8[_G7V)HM'"(E&_CEV_G<)B\8K1OY>7\G#)HM'!"M&_@/&7E_)
+MPY!5B^Q6BW8(@_X!=2"+1@2+5@8%L@!24.BD_X/$!,1>!"8KA[8`]]A(7LG#
+MD(M&!(M6!@6F`%)0Z$+_@\0$Q%X$)BN'J@#KWI#(%```5U:#?@3_=0:#?@;_
+M=!.+1@2+5@8JTCW__W4?@?H`_W49:.H$_S8N`/\V]@#HE0Z#Q`:X__]>7\G#
+MD(M6!BK`*M(]`/]U1CO0=4(K]NL!1CDV4@9^%HI&!"KD:]XJ`QYX!HX&>@8F
+M.4<F=>.*1@0JY&O>*@,>>`:.!GH&)CE')G0%:/,$ZYYKQBKK79"+1@2+5@:*
+MQ(K6*O8JY(E&_HI&!(E&\(-^_@!\#(M&_HL>?@<Y1Q9_!VC[!.EI_Y"+V-'C
+MB[=*!X-^\`!\$(M&\`/&BU[^T>,YATP'?P9H``7I1/^+1O`#QFO`*@,&>`:+
+M%GH&B4;RB5;TQ%[R)HM'!":+5P:)1OJ)5OR!?@@/870#Z?T"@WX*`'0#Z?0"
+MBT8."T8,=1*+1@X+1@QT`^D%_V@*!>GQ_I"+1@R+5@Z)1O:)5O@F]D<F'W0#
+MZ0H!)O9'$`)U#<1>]B;V1P4!=0/I]@!J`/]V_/]V^NA+_H/$!HE&[`O`=0O$
+M7O(F@$\0`NG6`,1>^B:+AZH`)HN/K``KP3M&['<.)HN'K``FB[^J`"OXZP.+
+M?NQ7Q%[R)HM'#":+5P[$7OHFBX^L``/!4E#_=@;_=@2+'D`?_U<.@\0*B_`+
+M]GT"*_8+]GXQ._YU+8M&["O&4,1>\B;_=PXF_W<,_W8&_W8$BQY`'_]7#H/$
+M"HOX"_]]`BO_`_?K`BO_.7;L?B%J`/]V!O]V!(L>0!__5Q*#Q`8+P'4+Q%[R
+M)H!G$/WK"9#$7O(F@$\0`@OV?AC$7OHFBX>L``/P)CFWJ@!W`HOW)HFWK`"#
+M/E8``'0#Z:``Q%[V)O='!`8!=0/ID@`F]D<$!G0(Q%[R)H!G$/O$7O8F@&<$
+M^\1>]B;V1P0"=!6+1OJ+5OP%L@!24.C7_(/$!`O`?PK$7O(F]D<00'0(Q%[V
+M)H!G!/W$7O(F]D<0$'0(Q%[V)H!G!/W$7O(F]D<0@'0(Q%[V)H!G!?[$7O8F
+MBT<")@L'=1HFBT<*)@M'"'40)HM'!B8+1P1U!BO`7E_)P\1>^B:`OY(``'4#
+MZ:H`Q%[R)O9'$`%U$6H0B\.,P@44`%)0Z+D1@\0&Q%[V)HL')HM7`L1>\B8)
+M1Q0F"5<6Q%[V)O9'!!YT",1>\B:`9QCAQ%[V)O9'!1YT",1>\B:`9QGAQ%[V
+M)HM'!":+5P;$7O(F"4<8)@E7&L1>]B:+1P@FBU<*Q%[R)@E''"8)5Q[$7O8F
+MBT<*)@M'"'03)HM'#":+5P[$7O(FB4<@)HE7(L1>\B:`3Q`!ZR!J$(O#C,(%
+ME`!24/]V#O]V#.C?$(/$"L1>^B;&AY(``<<&5``!`,1>\B:*3R2`X0>P`=+@
+M)HM?),'K`P,>!A^.!@@?)@@'Q%[R)HI'*,0>7@<F"$=@Z8$`@7X(`&=U(X-^
+M"@!U'8-^#@!\`_]&#(M&#(M6#L1>^B:)1PXFB5<0ZUB0@7X(`6=U(X-^"@!U
+M'8-^#@!\`_]&#(M&#(M6#L1>^B:)1P(FB5<$ZRZ0@7X(`F=T`^G&^X-^"@!T
+M`^F]^X-^#@!\`_]&#(M&#(M6#L1>^B:)1P8FB5<(QP94``$`Q%[R)HI/)(#A
+M![`!TN`FBU\DP>L#`QX&'XX&"!\F"`?$7O(FBD<HQ!Y>!R8(1V"#/E(``'4#
+MZ2K^@SY4``!U`^D@_FH`_S92`.BD#X/$!,<&5````.D+_I!5B^R+1@0M`&%\
+M"4A(?P6X`0#)PRO`R</("```5HMV".C*$HE&^,1>!":+1P0FBU<&B4;ZB5;\
+M]\8!`'0JQ%[Z)HN'K``FB8>N`,1>^B:+AZX`B4;^BT;^)CF'K`!UWL1>!":`
+M9Q#]]\8"`'1+Q%X$)O9'$!!U%\1>^B:+A[H`)CF'N`!T",1>!":`3Q$!Q%[Z
+M)HN'N``FB8>Z`,1>^B:+A[@`B4;^BT;^)CF'N@!UWL1>!":`9Q";_W;XZ#X2
+M@\0"*\!>R</($```5U;H'A*+^*%X!HL6>@:)1OR)5OZ+P@M&_'4'*\!>7\G#
+MD&H,C4[PBQ8$`%)1Z/4.@\0&QT;T___'1O;__ROVZUR0Q%[\)O9')B!T+HL>
+M0!^#?PH`="2-1O"+R(L6!`!246H`:`EABU[\)O]W`B;_-XL>0!__5PJ#Q`R+
+M'D`?@W\(`'04Q%[\)O]W`B;_-XL>0!__5PB#Q`1&@T;\*CDV4@9_GU?HBQ&#
+MQ`+I;O^0R`H``%=6QP9^!Q8?QT;\``+'1OX``,1>_":!/Z?R=`/I*`$F@7\"
+M2:%T`^D=`2:!?P128W0#Z1(!)H%_!B'Q=`/I!P$FBT<4HQ8?ZT^0QP8H'P``
+MQP8J'P$`QP8D'P!`QP8F'P``QP8:'PX`ZU+'!B@?``#'!BH?`0#'!B0?``#'
+M!B8?`0#'!AH?$`#K,L<&*!\``,<&*A\"`.O>2#T*`'<>T>"3+O^G`!W$'*0<
+MQ!S$'*0<I!SD'!8=Q!S$',0<QP88'P$`Q%[\)H-_&`!U!B;'1QA0`&H!:*"&
+MQ%[\:@`F_W<8Z"M$HQP?B18>'VH`:@124.AK0J,@'XD6(A_$7OPF@W\:`'00
+M)HM'&HE&]L<&/A\0`.L.D":+1PZ)1O;'!CX?```K]NL"D$:#_A!]"XO.BT;V
+MT^`+P'7O`38^'\<&+!\!`*$^'Z,N'\=&^``"QT;Z``#$7O@F@3]%0W0#Z0`!
+M)H%_`E`A=`/I]0#'!A8?%P"#/EP'`'0=@SY<!QAU"<<&%A\8`.L.D(,^7`<9
+M=0;'!A8?&0"A%A\M&`!T%<<&)!\`$,<&)A\``,<&&A\,`.L3D,<&)!\``,<&
+M)A\!`,<&&A\0`,<&*!\``,<&*A\"`,1>^":+1P:C&!_'!AP?0'C'!AX??0%J
+M`&H$_S8>'_\V'!_H:T&C(!^)%B(?QP8^'P``QP8L'P``*_;K!2O``_!&@_X(
+M?4K$7OB#PP@FB@`JY(E&]CW_`'0WBD;V)0<`.\9U+8I&]B4@`#T!`!O_@^?X
+M@\<0`3X^'XL>+!_1XXF_+A__!BP?@_\0=;&X`0#KKBO`7E_)PU6+[/]V"/]V
+M!HM>!-'C_[<H`?\V,`'HW06+Y8U&#%#_=@IJ`&H`Z`D&B^5H)@'HAP4KP,G#
+MD&AH`>BZ!8/$`O\V2@1HNP'HK06#Q`3_-D@$_S86'VC0`>B<!8/$!O\V1A__
+M-D0?_S8.`/\V#`!H'0+H@P6#Q`K_-L(&_S;`!O\V;@?_-FP':&H"Z&H%@\0*
+M_S8>`/\V'`#_-AH`_S88`&BW`NA1!8/$"O\V!`#_-@8`:`0#Z$`%@\0&_S8(
+M`/\V"@!H4P/H+P6#Q`;_-CX?_S8L'VBC`^@>!8/$!FCT`^@5!8/$`L.0Z*_\
+MZ)(.Z#D+Z$3CZ`\`Z$3HZ#O_Z*8-Z7L*D,.0R`0``*$4`(L6%@`K!A``&Q82
+M`*-L!XD6;@>A#`"+%@X`*P9L!QL6;@>C1!^)%D8?H1``BQ82`-'JT=C1ZM'8
+MT>K1V-'JT=B*#A``@^$/B4[\B4;^H6P'BQ9N!\1>_":)!R:)5P+$7OPKP":)
+M1P8FB4<$Q%[\)HE'"B:)1PB+1OR+5OZC>@>)%GP'HPP?B18.'RO`R<.0R`0`
+M`*%Z!XL6?`?K"9`FBT<$)HM7!HE&_(E6_HO""T;\=!:+1@2+5@;$7OPF.5<"
+M<MQW!28Y!W+5BT;\BU;^R<.0R`0``(M&!@M&!'17H7H'BQ9\!\1>!":)1P0F
+MB5<&Q%X$*\`FB4<*)HE'"*%\!PL&>@=U$(M&!(M6!J,,'XD6#A_K$Y"+1@2+
+M5@;$'GH')HE'"":)5PJ+1@2+5@:C>@>)%GP'*\#)PY!5B^R+1@8+1@1T9\1>
+M!":+1PHF"T<(=1(FBT<$)HM7!J-Z!XD6?`?K%9`FBT<$)HM7!B;$7P@FB4<$
+M)HE7!L1>!":+1P8F"T<$=1,FBT<()HM7"J,,'XD6#A\KP,G#)HM'"":+5PHF
+MQ%\$)HE'"":)5PHKP,G#R!0``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'JT=C1
+MZM'8T>K1V`-&!(/2`(E&^(E6^L1>!":+!R:+5P(#1O@35OJ)1NR)5NZA>@>+
+M%GP'ZTB+1O2+5O8Y1OAU-3E6^G4PQ%X$)HL')HM7`L1>_"8!!R815P+_=O[_
+M=OSH#?^#Q`2+1OR+5OZ)1@2)5@;IB`"0)HM'!":+5P:)1OR)5OZ+P@M&_'1R
+MBT;\@.0/BL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`T;\@](`B4;PB5;RQ%[\
+M)HL')HM7`@-&\!-6\HE&](E6]HM&\(M6\CE&['0#Z5W_.5;N=`/I5?\&4^B*
+M_H/$!,1>_":+!R:+5P+$7@0F`0<F$5<"BT;^"T;\=`/IXOZ+1@2+5@;)P\@2
+M``"+1@0%!0`D_HE&!"T,`!O)]]$CP04,`(E&],=&]@``Z`,+B4;R_W;V_W;T
+MZ(3]@\0$B4;\B5;^B\(+1OQU#O]V\NCP"H/$`BO`F<G#4O]V_.@)_H/$!(M&
+M_(M6_HE&[HE6\,1>_":+!R:+5P(K1O0;5O:)1OB)5OH+TG4%/0P`<F6+PXS"
+M@.0/BL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`\.#T@`#1O035O;1ZM'8T>K1
+MV-'JT=C1ZM'8BD[\`D[T@^$/B4[\B4;^BT;XBU;ZQ%[\)HD')HE7`O]V_O]V
+M_.@1_8/$!/]V\NA("HM&](M6]L1>[B:)!R:)5P*#1NX$BT;TBU;V`09$'Q$6
+M1A\I!FP'&19N!XM&[HM6\,G#R`X``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'J
+MT=C1ZM'8T>K1V`-&!(/2`#D6$@!W%G(&.080`'<..186`'<-<@8Y!A0`<P6X
+M___)PX-N!`2+1@2+5@:)1O*)5O3$7O(FBP<FBU<"B4;\B5;^Z)@)B4;V_W;T
+M_W;RZ#G]@\0$B4;RB\(+1O)T"E+_=O+H1_R#Q`3_=O;H?@F+1OR+5OXI!D0?
+M&19&'P$&;`<1%FX'*\#)PU6+[(,^3A\`=!"#/A0?`'0)_W8$:@#_%A0?*\#)
+MPU6+[(,^3A\`=!&#/A0?`'0*_W8&_W8$_Q84'RO`R<.058OLC48&4/]V!&H`
+M:@#H*P#)PY!5B^R-1@A0_W8&_W8$:@#H%@#)PU6+[(U&"%#_=@9J`/]V!.@"
+M`,G#R`X``%=6BT8*B4;RBUX(_T8(B@>8B_B#_R5T,@O_=1(Y?@9T!HM>!L8'
+M`"O`7E_)PY"#?@8`=`J+7@:(!_]&!NO*5_]V!.A;_X/$!.N^BUX(@#\E=1*#
+M?@8`=`B+7@;&!R7KV6HEZ]N`/RUU"_]&",=&]@$`ZP:0QT;V``"+7@B`/S!U
+M"_]&",=&_`$`ZP:0QT;\``"+7@B`/RMU"_]&",=&^@$`ZP:0QT;Z``"+7@B`
+M/R!U"_]&",=&]`$`ZP:0QT;T``"+7@B`/R-U`_]&""OVZQ6#_SE_((O&P>`"
+M`\;1X`/'+3``B_"+7@C_1@B*!YB+^(/_,'W;@_\N=16+7@C_1@B*!YB+^(/_
+M,'P%@_\Y?NL]9`!U`^G[`'X#Z0P"/54`=0/I(P%^`^GG`2U$`'4#Z18!+0L`
+M=0/I#@'I'`*#?O8`=2S'1O@!`.L/D&H@_W8$Z$[^@\0$_T;X.7;X?1&#?@8`
+M=.>+7@;&!R#_1@;KYX-^!@!T'HM>\HH'BUX&B`?_1@:#?O8`=0/IS@''1O@!
+M`.LCD(M>\HH'*N10_W8$Z`#^@\0$Z]R0:B#_=@3H\OV#Q`3_1O@Y=OA\`^F<
+M`8-^!@!TY(M>!L8'(/]&!NODD&H`:@#_=O;_=OQ6:@*+7O)J`/\WC48&4/]V
+M!.AT`8/$%.EF`6H`:@#_=O;_=OQ6:A#KVI!J`&H`_W;V_W;\5FH(Z\J0_W;T
+M_W;Z_W;V_W;\5FH*BU[RBP>94E#KMY!J`&H`_W;V_W;\5FH*ZZ"0BUX(_T8(
+MB@>8B_B#_T%\"(/_6G\#@\<@B\<]:0!T5W]M+6(`=`9(2'1,ZR)J`&H`_W;V
+M_W;\5FH"BU[R_W<"_S>-1@90_W8$Z-D`@\04@T;R`NG'`)!J`&H`_W;V_W;\
+M5FH0Z]20:@!J`/]V]O]V_%9J".O$D/]V]/]V^NL$:@!J`/]V]O]V_%9J"NNL
+MD"UO`'33+08`=.8M`P!TN>NOD(M>\HL'B4;^BU[^_T;^B@>8B_@+_W1E@WX&
+M`'0+BUX&B`?_1@;KX9!7_W8$Z)7\@\0$Z]0M6`!U`^DO_RT*`'4#Z:[^2'4#
+MZ1;^ZS`M:0`]#P!W*-'@DR[_IRPH!B=,*$PH,"=,*$PH]B9,*$PH3"C0)TPH
+M("=,*$PHYB:#1O("Z:G\D,@F``!75HM&"(M6"HE&_(E6_@O2?0KW7OR#5OX`
+M]U[^*_:+1@R94E#_=O[_=OSHUS>+V`,>7@2*!XA"W$:+1@R94E"-1OQ0Z-@V
+M@W[^`'_2?`:#?OP`=<J#?@H`?0^#?A0`=0:#?A8`=`/_3@Z#?A(`=3&#?A``
+M=2N+_NL-D&H@_W8$Z+K[@\0$1SE^#GX5BUX&@S\`=.>+'\8'((M>!O\'Z^:0
+M@WX6`'4&@WX4`'08@WX*`'T2BUX&@S\`="J+'\8'+8M>!O\'@WX4`'0S@WX*
+M`'PMBUX&@S\`=!J+'\8'*XM>!O\'ZQEJ+?]V!.A0^X/$!.O3D&HK_W8$Z$+[
+M@\0$@WX6`'0>@WX4`'48@WX*`'P2BUX&@S\`=!N+'\8'((M>!O\'@WX2`'4_
+M@WX0`'0YB_[K&Y!J(/]V!.@"^X/$!.OBD&HP_W8$Z/3Z@\0$1SE^#GX5BUX&
+M@S\`=.>+'\8',(M>!O\'Z^:0C43_B4;:ZQB+7MJ-1MP#V(H'F%#_=@3HO/J#
+MQ`3_3MJ#?MH`?""+7@:#/P!TVHM>VHU&W`/8B@>+7@:+'X@'BUX&_P?KUX-^
+M$@!T*HO^ZPQJ(/]V!.A^^H/$!$<Y?@Y^%8M>!H,_`'3GBQ_&!R"+7@;_!^OF
+MD"O`7E_)PX,&@`<!@Q:"!P#K]`````````````#ZNB+_N`"`[[HH_[!M[NL`
+MNF;_,\#OZP`SP([8CM".P.H``/__B]R+5P**1P3NPXO<BU<",\#LPXO<BU<"
+MBT<$[\.+W(M7`NW#58OL5E<>!L1^",5V!(M.#(O&,\>I`0!U$_?&`0!T!@O)
+M=`ND2='I\Z6#T0#SI`<?7UY=PU6+[%<&Q'X$BTX(,\#1Z?.K<P&J!U]=PP#(
+M`@``:@!J9/\V(A__-B`?Z`DVB4;^:`'@:&;_Z(/_@\0$_W;^:&+_Z'?_@\0$
+M:@!J!&BH86H3Z,H#@\0(T6[^_W;^:&#_Z%G_@\0$ZPJ#!G('`8,6=`<`:&#_
+MZ$[_@\0".T;^=^CK"H,&<@<!@Q9T!P!H8/_H-/^#Q`([1OYRZ&H(:'('Z"HT
+M*\#)PP``````````````````'@93+HX><`&Z*O_M4(N'R@;O^_\&$A__EX0>
+M^KHB_[@`@.]8NBK_[X/$`@<?8<]@NP``Z\M@NP(`Z\5@NP0`Z[]@NP8`Z[E@
+MNP@`Z[-@NPH`ZZU@NPP`ZZ=@NPX`ZZ%@NQ``ZYM@NQ(`ZY5@NQ0`ZX]@NQ8`
+MZXE@NQ@`ZX-@NQH`Z7S_8+L<`.EU_V"['@#I;O]@NR``Z6?_8+LB`.E@_V"[
+M)`#I6?]@NR8`Z5+_8+LH`.E+_V"[*@#I1/]@NRP`Z3W_8+LN`.DV_V"[,`#I
+M+_]@NS(`Z2C_8+LT`.DA_V"[-@#I&O]@NS@`Z1/_8+LZ`.D,_V"[/`#I!?]@
+MNSX`Z?[^8+M``.GW_F"[0@#I\/Y@NT0`Z>G^8+M&`.GB_F"[2`#IV_Y@NTH`
+MZ=3^8+M,`.G-_F"[3@#IQOY@NU``Z;_^8+M2`.FX_F"[5`#IL?Y@NU8`Z:K^
+M8+M8`.FC_F"[6@#IG/Y@NUP`Z97^8+M>`.F._F"[8`#IA_Y@NV(`Z8#^8+MD
+M`.EY_F"[9@#I<OY@NV@`Z6O^8+MJ`.ED_F"[;`#I7?Y@NVX`Z5;^8+MP`.E/
+M_F"[<@#I2/Y@NW0`Z4'^8+MV`.DZ_F"[>`#I,_Y@NWH`Z2S^8+M\`.DE_F"[
+M?@#I'O[ZNBK_[8O8N`<`[_N+P\/ZNBK_[8O8N`8`[_N+P\/ZNBK_[8O8N`4`
+M[_N+P\/ZNBK_[8O8N`0`[_N+P\/ZNBK_[8O8N`,`[_N+P\/ZNBK_[8O8N`(`
+M[_N+P\/ZNBK_[8O8N`$`[_N+P\/ZNBK_[8O8N```[_N+P\/ZB]R+1P*Z*O_O
+M^\.Z*O_M]]`E!P##^L/[PYSZ6,.+W/]W`IW#S,,`PY!5B^S_=B#_=A[_=AS_
+M=AK_=AC_=A;_=A3_=A+_=A#_=@[_=@S_=@K_=@C_=@;_=@1HX`3H>O;)P\@(
+M``!6:`"`:"+_Z#[\@\0$:`"`:"+_Z#+\@\0$:/T`:"C_Z";\@\0$:@!H*O_H
+M&_R#Q`1J"&@X_^@0_(/$!&H(:#K_Z`7\@\0$:@AH//_H^ON#Q`1J"&@^_^CO
+M^X/$!"OVB][1X\>'A!X$+HO>T>/'A\H&``!&@_Y`?.8KP(E&^HE&^"OVCD;Z
+MB][1XXN'8`2+#@8`BU[X)HD')HE/`D:#1O@$@_Y`?.`KP%[)PU6+[(-^!`!\
+M$H-^!$!]#(-^"`!\!H-^"`=^!;C__\G#N`@`*T8(B48(/0<`?@7'1@@'`(M&
+M!HM>!-'CB8>$'HM&"$B+7@31XXF'R@:+1@3K;Y#_=@AH,O_H0_N+Y>F(`(-^
+M"@'U&\`E$``+1@A0:#C_Z^60@WX*`?4;P"40``M&"%!H.O_KT9"#?@H!]1O`
+M)1``"T8(4&@\_^N]D(-^"@'U&\`E$``+1@A0:#[_ZZF0_W8(:#3_ZZ#_=@AH
+M-O_KF"T(`#T+`'<@T>"3+O^GNB_2+](OFB^B+THO7B]R+X8OTB_2+](O/"\K
+MP,G#R`(``%=6BWX$@>?_`'P&.3X0'W\'N/__7E_)PVGWN`"!QH0']H2!`(!U
+MZO:$@``!=`8KP%Y?R<.`C($`$(U%`3L&0A]]`Z%"'Z-"'\=$!(`EQT0&``#'
+M1`B`)<=$"@``QD0V`,9$-`C&1#4`QT0,@!#'1`X``"O`B402B400B406B404
+MB40>B40<QT0D`0#'1"8!`,9$-Q/&1#@1QD0Y$\9$.A&A:`>)1"BA=@:)1"JA
+M9@>)1"RAQ`:)1"ZA"A^)1##'1OX``(M>_L9`/0#_1OZ#?OX+?/`KP(E$5HE$
+M5(E$6HE$6(E$7HE$7(E$9HE$9(E$:HE$:(E$;HE$;,:$F0`!QH28``")A(8`
+M4%!6Z)41@\0&5NAX$(/$`CET>'46BX24``4<`%#H?OF#Q`+'1OX``.L&D,=&
+M_@$`BUQXBX>4``4(`%#H8/F#Q`**3OZ`P0+3^"4!`#T!`!K`]MB(A)P`BUQX
+MBX>4``4(`%#H.?F#Q`**3O[3^"4!`#T!`!K`]MB(A)X`:B"+A)0`!00`4.@,
+M^8/$!&H!BX24``4$`%#H_/B#Q`1J!(N$E``%!`!0Z.SX@\0$@(R```&`I($`
+M[\1<=":#!P$F@U<"`.EC_I#(!```5U:+?@2!Y_\`?`8Y/A`??P>X__]>7\G#
+M:?>X`('&A`?VA(```74'*\!>7\G#D/:$@`"`=`=6Z-<3@\0".71X=0N+7'B`
+MIZ(`^>L)D(M<>("GH@"?BUQXBH>B`"KD4/^TC`#H9OB#Q`1J((N$E``%!`!0
+MZ%;X@\0$QH28``#HRON)1O['A*8```#'A+0```#'A+8```#'A*P```#'A*X`
+M``!6Z)<4@\0"*\")A((`B82``(F$A`!6Z`(/@\0"_W;^Z(O[@\0"H4(?2(E&
+M_.L0D&E>_+@`]H<$"`%U"?]._(-^_`!]ZXM&_$"C0A_I./^0R%0``%=6BD8$
+M*N2)1O@+P'P&.080'W\(N/__7E_)PY!I\+@`@<:$!\=&^@``BT8(Z2T#D/]V
+M#%;HBR"#Q`3I5P.0_W8,5NC!%.OPD(M&#(M6#HE&\(E6\E:-=`3$?O"Y(@#S
+MI5[I,`.+1@R+5@Z)1O")5O+$7O`F]D<$$'0;Z-GZB4;V@*2``/=6Z(T3@\0"
+M_W;VZ,CZ@\0"Q%[P)O9'!"!T&^BT^HE&]H",@``(5NB:$X/$`O]V]NBC^H/$
+M`L1>\";V!R!T&.B0^HE&]FH`5NB?$H/$!/]V]NB"^H/$`L1>\";V!Q!T&.AO
+M^HE&]FH!5NA^$H/$!/]V]NAA^H/$`L1>\";V1P1`=0/IBP)J`8O#!0D`4/]V
+M!O]V!.AP"(/$".ET`BO_@7X(!6%U!3E^"G0,@7X(!V%U"#E^"G4#OP$`@7X(
+M!6%U!H-^"@!T#8%^"`9A=0F#?@H`=0.#SP)75NAC'X/$!(M&#(M6#HE&\(E6
+M\E:-?JR-=`2,T([`N2(`\Z5>BT;PBU;R5AZ-?`2+\!X'CMJY(@#SI1]>C4:L
+M%E!6Z#X.@\0&Z>X!_W8._W8,5NC&'^ON_W8._W8,5N@*$^OB5N@J(H/$`E;H
+M:2"#Q`*+1@R+5@Z)1JR)5JY6C71(Q'ZLN08`\Z5>BUZL)L='!#\`)L='!@``
+M@&1)?^F9`9`K_X%^"`IA=04Y?@IT#(%^"`QA=0@Y?@IU`[\!`(%^"`IA=0:#
+M?@H`=`V!?@@+874)@WX*`'4#@\\"5U;HAQZ#Q`2+1@R+5@Z)1JR)5J[$7JPF
+M]D<$!'0:)HI'""4$`#T!`/4;P"4$``P!4%;H12*#Q`3$7JPF]D<$`74#Z1D!
+M)HI'""4!`#T!`/4;P"4$``P!4%;H[B+IHOV0BT8,BU8.B4:LB5:N5HUT5,1^
+MK+D(`.FO_9"+1@R+5@Z)1JR)5JY6'HU\5(OP'@>.VKD(`/.E'UZ-1`0>Z<;^
+MD(M&#(M6#HE&K(E6KHM$=(M4=E8>B_".VL1^K+DZ`/.E'UX&_W:L5NA`(.F=
+M_I"+1'8+1'1U`^F%`,1<=":+1W")1O9J=/]T=O]T=.@+]8/$!HM&]IG$7'0F
+MB4=P)HE7<O\V%@:-A+``4.AB(X/$!)G$7'0FB4<$)HE7!NM!D,=&^O__ZSF0
+M+0%A/1,`=_#1X),N_Z<*-LXRW#+F,@`TOC.^,[XS7#3<-)HTFC2:-#0U3C7R
+M-0(S=#5$-%`THC6+1OI>7\G#D,@,``!75HM>#(L'B4;VBUX$BX>H`(N7J@")
+M1OJ)5OR+1@XK1@H;R2/!`T8*B48.BT<,B4;XBD;X)0@`/0$`&\!`B4;^*_;K
+M.XI&])@M"@!T!RT#`'0:ZPKV1O@0=`3&1O0-BD;TQ%X&_T8&)H@'ZQ+V1O@@
+M=`;&1O0*Z^;V1OA`=.!&.78.?D+$7OJ+?O8FB@&(1O3_1O:+1O8Y!A0&=P7'
+M1O8``(!^]"!\G8!^]'Y_EX-^_@!TJX!^]$%\I8!^]%I_GX!&]"#KF9"+1O:+
+M7@R)!XM&#EY?R<.0R`@``%=6BD8$*N2)1OQI\+@`@<:$!_:$@``!=0>X__]>
+M7\G#BX2L`(E&_HN$K@")1OJ+1OXY1OIW"(OX*W[ZZPJ0B_@K?OH#/A0&.7PL
+M<P6`I(4`^_9$#'AT&U>-1OI0_W8,_W8*_W8(5NBY_H/$#(E&#.FM`(M&##O'
+M?@*+QXE&#`O`?P/IF@"A%`8K1OH[1@Q^`XM&#(E&^`O`=1%H+`/_-B(%_S;V
+M`.CT[(/$!J$4!CE&^'(1:"T#_S8B!?\V]@#HV^R#Q`;_=OC_=@K_=@B+A*@`
+MBY2J``-&^E)0Z*3R@\0*BT8,.4;X?1TK1OA0BT8(BU8*`T;X4E#_M*H`_[2H
+M`.A_\H/$"H-^#`!^%"M^#(M&#`%&^J$4!CE&^G(#*4;ZH10&.4;Z<A%H0@/_
+M-B(%_S;V`.AG[(/$!NBA]8E&_(M&^HF$K@"+1OXY1OIU"H"D@`"_@*2%`(`Y
+M?"YV!8"DA0#WQX2&````_W;\Z'/U@\0"BT8,"\!]`BO`F<1<="8!1P@F$5<*
+M.7PL<@[VA(``@'0'5N@-#8/$`HM&#%Y?R<.0R"8``%:+7@R+-XM>!(N'L`"+
+ME[(`B4;\B5;^H18&B4;>BT<0BU<2B4;FB5;HBH>D`"KDB4;@QT;T``#'1NP`
+M`(I&YB4!`#T!`!O`0(E&]O9&YX!T$?9&Z`%U"\9&\`#'1NH%`.L7]D;G@'4&
+M]D;H`70&QD;P?^OGQT;J`P#'1NP``.L)_TX._T;@_T;LBT8*.4;L?`/IK@+$
+M7@;_1@8FB@>(1MH\('PR/'Y_+H-^#@!U`^F1`H-^]@!T##QA?`@\>G\$@&[:
+M((I&VL1>_":(`$8Y=MYWKBOVZZJ+1N")1N3'1MP``,=&\@``BT;JB4;XBD;P
+MB$;ZBD;:F.D_`9#V1N9`=`K'1MP%`,=&\@$`@W[@`'\#Z4$!_T[@Z3L!D/9&
+MYA!T`^G.`/9&Y@)U`^FD`/9&Y@AT"8-^X`!U`^FV`,9&X@V`3O@(Z:L`]D;G
+M`G0BBD;@)0<`+0@`]]C1^(E&W`O`?@:X`@#K`Y`KP(E&\NLPD/9&YP1T#,=&
+MW`H`QT;R`@#K'?9&YPAT%\=&^`0`BD;@)0<`+0@`]]B)1O+&1OH@BT;@#`=`
+MB4;@Z:L`D/9&YH!U`^FA`,=&W,@`QT;R___IE`#V1N<!Z^CV1N8$=!;&1MH*
+M]D;F('1^QT;<"@#'1O("`.MR]D;F"'0)@W[@`'4#Z97^]D;G$'0;BT;@P?@"
+M!04`/0H`?0.X"@")1MS'1O("`.L>]D;G('0(QT;<"@#K[)#V1N=`=`K'1MP/
+M`,=&\@0`QT;@``#K'9`M"``]!0!W%-'@DR[_IP0[MCD".M8Y9CI\.H(ZQT;N
+M``#V1O@!=`/_1N[V1O@(=`/_1N[V1O@$=`:+1O(!1NZ+1@XY1NY^#XM&Y(E&
+MX,=&#@``Z;4`D(-^W`!^(/9&^`)T&H-^[`!U"XM>!/>'@``"('0)BT;DB4;@
+MZ8X`]D;X`701BD;:Q%[\)H@`1CEVWG<"*_;V1O@(=!&*1N+$7OPFB`!&.7;>
+M=P(K]H-^W`!^'O9&^`)T&(M>!("/@0`@BD;<BUX$B(>E`/]&[.L]D(-^\@!^
+M+/9&^`1T)HM&\D@!1O3K!)#_3O*#?O(`?A.*1OK$7OPFB`!&.7;>=^@K]NOD
+MBT;N*48.Z47]D(M&]`-&[)F+7@3$7W0F`4<$)A%7!HI&X(M>!(B'I`"+7@R)
+M-XM&[%[)P\@*``!75HI&!"KDB4;\:?"X`('&A`?VA(```74'N/__7E_)P^BE
+M\8E&^O:$@0`(=`>+A*8`ZP60BX2T`(E&_HN$M@")1OC_=OKHA/&#Q`*+1OXY
+M1OAW"XL^%@8K^`-^^.L%BW[X*_A/.4;X=06`I(0`@*$6!BO'0#M$*G,%@*2$
+M`/>#[P0Y/A8&<Q-HA@3_-B(%_S;V`.CSYX/$!BO_BT02"T00=!Q7C4;^4/]V
+M#/]V"O]V"%;HV/N#Q`R)1@SIO@"0BT8,.\=^`HO'B48,"\!_`^FJ`*$6!BM&
+M_CM&#'X#BT8,B4;V"\!U$6B6!/\V(@7_-O8`Z)3G@\0&H18&.4;V<A%HEP3_
+M-B(%_S;V`.A[YX/$!O]V]HN$L`"+E+(``T;^4E#_=@K_=@CH1.V#Q`J+1@PY
+M1O9]'2M&]E#_M+(`_[2P`(M&"(M6"@-&]E)0Z!_M@\0*@WX,`'XDBT8,F<1<
+M="8!1P0F$5<&BT8,`4;^BT;^.086!G<&H18&*4;^BT;^.086!G<1:*H$_S8B
+M!?\V]@#H]N:#Q`;H,/")1OKVA($`"'0*BT;^B82F`.L(D(M&_HF$M`"A%@8K
+MQT"+^#E\*',%@*2$`/OVA(``"G4'5NBX"(/$`O]V^NCS[X/$`HM&#%Y?R<.0
+M*\##D,@"``!6BW8(BD8$*N2)1@3'1@8``*$0'YD[5@9W#G(%.T8$=P>X__]>
+MR<.0:48$N``%A`>+V/:'@0"`=>@+]G41_S84!@6H`%#HXAJ#Q`1>R<.#_@%U
+M"?\V%@8%L`#KZ(/^`G4'H10&2%[)PX/^`W4%H18&Z_(KP%[)PY#(!@``5U:+
+M=@3VA($`('07BH2E`/Z,I0`*P'0&*\!>7\G#@*2!`-_VA(```G4<]H2```1T
+M#H!,:`*`C(``(("DA`"`@*2``/OKTO:$@0`(=`>+A*8`ZP60BX2T`(E&_HN$
+MM@")1OR+1OXY1OQW"(OX*W[\ZPJ0BSX6!BM^_`/X.7PH<ACVA(0`!'61@(R$
+M``2`3&@$@(R``"#K@9`Y?"IV`^EX__9$6`AU`^EO__:$A``(=`/I9?^`C(0`
+M"(!,:`CKTL@&``!75HMV!(N$K`")1OZ+A*X`B4;\BT;^.4;\=P>+^"M^_.L)
+MBSX4!BM^_`/XB\<KA(8`]H2%``%U&3F$B`!S!3E\)G<.@(R%``&`3&D!@(R`
+M`"`Y?"YW&/9$60AT,O:$A0`(=2N`C(4`"(!,:0CK&SE\+'(;]D19!'05]H2%
+M``1U#H",A0`$@$QI!(",@``@B;R&`"O`7E_)P\@(``!75HMV"/?&$`!T%XM>
+M!,1?=":#1R0!)H-7)@"+7@2`3VH0]\8@`'07BUX$Q%]T)H-'+`$F@U<N`(M>
+M!(!/:B#WQD``=!>+7@3$7W0F@T<H`2:#5RH`BUX$@$]J0/?&@`!T'HM>!,1?
+M=":#1V0!)H-79@"+7@2`3TF`BUX$@$]I@/?&\`!T"(M>!("/@``@BUX$BH>?
+M`"KD]]`C\(J'H``JY(7&=`<KP%Y?R<.0BX>H`(N7J@")1OR)5OZ+OZP`BX>N
+M`(E&^CO'=PNA%`8KQP-&^NL#D"O'B4;X_T[XBH>A`"KDA<9T2H-^^`-](HM>
+M!,1?=":#1S`!)H-7,@"+7@3$7W0F@T<T`2:#5S8`ZY?$7OPFQ@'_1SD^%`9W
+M`BO_Q%[\)L8!`$<Y/A0&=R,K_^L?]\;P`'09@W[X`7RP]\:``'0&BD<\ZP20
+MBD<[F(E&!HI&!L1>_":(`4<Y/A0&=P(K_XM>!(F_K`"+7@2`CX``0.DR_Y#(
+M`@``5U:+7@2+=WB-O+@`BIVC`"K_T>.+AVX%B4;^]X2```$0=`Z`3OX$]D0<
+M('0$@$[^`?>%@``!$'0.@$[^"/9%'"!T!(!._@+_=OZ+A)0`!0@`4.BVZ(/$
+M!%Y?R</(`@``5U:+=@2+A)0`B4;^@'PV`G4$*__K#X!\-@%U!K\$`.L$D+\0
+M`(!\-`5T((!\-`9U!8//`>L5@'PT!W4&@\\"ZPJ0@'PT"'4#@\\#]D0<$'0$
+M@<^``&H0BT;^!00`4.A*Z(/$!%?_=O[H0.B#Q`0K__9$'"!T`[\0`(!\-0)U
+M!8//#^L/@'PU`74&@\\(ZP20@\\']D08$'0$@<^``%?_=O[H!^B#Q`16Z/'^
+M@\0"7E_)P\@4``!75HMV!(M$>(E&_(U\!,=&_@``]D4)$'0$@$[^</9%"(!T
+M!(!._H"*1OZ(A)\`QT;V``#V10D@=`2`3O9P]D4)`70$@$[V@(I&]HB$H`#'
+M1O0``/9%"4!T!(!.]'"*1O2(A*$`]D44`70$@$T8$/9%%`)T!(!-&""#?20`
+M=`BA%@8Y121R!J%H!XE%)(-])@!T"*$6!CE%)G(&H78&B44F@WTH`'0(H10&
+M.44H=@:A9@>)12B#?2H`=`BA%`8Y12IR!J'$!HE%*H-]+`!T"*$4!CE%+'(&
+MH0H?B44LQT;X``#K!)#_1OB#?O@1?1F+!8M5`HM>^,'C`CF7+`5RYG<&.8<J
+M!7+>@W[X$74#_T[X@SXD!0!T!NA;ZHE&^HM&"`M&!G0.Q%X&)HM'&":+5QKK
+M!Y"+11B+51I24%;H?`&#Q`:+1@@+1@9T%8M%"(M5"L1>!B8Y1PAU!B8Y5PIT
+M5/9%"`%T&CEV_'4+BU[\@*>B`/GK(Y"+7OR`IZ(`G^L8.7;\=0N+7OR`CZ(`
+M!NL)D(M>_("/H@!@BU[\@(^B`("+7OR*AZ(`*N10_[2,`.A&YH/$!(M&"`M&
+M!G1"BD4RQ%X&)CA',G4VBD4P)CA','4MBT48BU4:)CE'&'4A)CE7&G4;BD4Q
+M)CA',742BT44BU46)CE'%'4&)CE7%G0'5NA&_8/$`HM%`@L%=0/IE@"+1@@+
+M1@9T$XL%BU4"Q%X&)CD'=08F.5<"='N*1OB(A*,`:@!J9&H`:@7_=0+_->A#
+M'%)0Z#X<0(F$E@`Y='AT'%;HE/R#Q`**G*,`*O_1X_^WD`6+A)0`0$#K,Y#_
+M=`;_=`1J`6@`PN@*'(E&\(I&\2KD4(N$E``%#`!0Z&GE@\0$BD;P*N10BX24
+M``4.`%#H5>6#Q`3HS`R#/B0%`'0)_W;ZZ,7H@\0"7E_)PU6+[%:+=@2+1!R+
+M5!XQ1@8Q5@CVA(``@'1$]D8&$'03]D0<$'0$:@+K`FH&5NCD$8/$!/9&!@)T
+M%/9$'`)T!6H`ZP.0:@%6Z(@`@\0$]D8&0'0I]D0<0'0::@+K&)#V1@80=`EJ
+M!E;HJA&#Q`3V1@9`=`EJ!E;H:Q*#Q`1>R<.058OL5U:+=@3VA(``@'0^Z"CH
+MB_CV1!P"=`EJ`5;H,@"#Q`3V1!P0=`EJ!E;H91&#Q`3V1!Q`=`EJ!E;H)A*#
+MQ`2`I(``?U?H].>#Q`)>7\G#D,@"``!75HMV!(-^!@!T%8I$.HA&_L1<=":#
+M1S@!)H-7.@#K$XI$.8A&_L1<=":#1SP!)H-7/@#_M)``Z#7D@\0"J`1T$8I&
+M_IA0_[22`.@8Y(/$!.M`B[RV`/:$@0`(=1B`C($`"(N$M`")A*8`B;RT`%;H
+M,@"#Q`(YO+0`=0D+_W4$BSX6!D_$G+``BD;^)H@!B;RV`,1<=":#1P0!)H-7
+M!@!>7\G#D%6+[%:+=@2#?`(`=2&A)@6)!,=$`B8%@SXF!0!T!XL>)@6)=P*)
+M-B8%@(R```)>R<.058OL5HMV!(-\`@!T)X,\`'0(BT0"BQR)1P*+!(M<`HD'
+MQP0``,=$`@``@(R```2`I(``_5[)PY#(!```5HMV!(M$'(M4'HE&_/9&_`)T
+M"6H`5NC1_H/$!/9&_!!T"6H"5N@$$(/$!/9&_$!T"6H"5NC%$(/$!(",@`"`
+M7LG#58OL5HMV!%;H&P^#Q`+$7@8KP":)1P(FB0?VA(``"'0(Q%X&)H!/`@'V
+M1!P@=`[V1%`(=0C$7@8F@$\"!%[)PU6+[%=6BW8$Z$7FB_B#?@;^=1UJ<(N$
+ME``%!`!0Z+'B@\0$@*2!`/O'A(H```#K-H",@0`$:F"+A)0`!00`4.B/XH/$
+M!(-^!O]T#HM&!KD*`"O2]_&)A(H`Q%QT)H-'8`$F@U=B`%?HZN6#Q`)>7\G#
+MD,@"``!75HMV"(-^!/]U!H-^!O]T$XM&!(M6!BK2/?__=0R!^@#_=08KP%Y?
+MR<.*1@0JY(E&_@O`?`BA$!\Y1OY\"+C__UY?R<.0:7[^N`"!QX0'"_9TZXL$
+MB45ZZ\I5B^Q75BO_B_?K$I#1__9&!<!T!('/@`#!9@0"1H/^"'SJB\?WT%Y?
+MR<-75O\VQ@;HS?^#Q`*+\/\VR`;HP?^#Q`*+^(,^R`8`=3^#/E`&`'4#Z?T`
+M:A=H@`+HI.&#Q`1J(&B"`NB9X8/$!&H':(("Z([A@\0$5FB"`NB$X8/$!&B`
+M`&B``NG#`)!J%6C``NAOX8/$!&H@:,("Z&3A@\0$:@-HP@+H6>&#Q`1J!VC"
+M`NA.X8/$!&C\`&C"`NA"X8/$!&B``&C``N@VX8/$!&H5:,0"Z"OA@\0$:B!H
+MQ@+H(.&#Q`1J`&C&`N@5X8/$!&H%:,8"Z`KA@\0$5FC&`N@`X8/$!&B``&C$
+M`NCTX(/$!&H5:,@"Z.G@@\0$:BAHR@+HWN"#Q`1J`6C*`NC3X(/$!&H%:,H"
+MZ,C@@\0$5VC*`NB^X(/$!&B``&C(`NBRX(/$!%Y?PY#(&```5U:A$!\]$`!^
+M`[@0`(E&^,=&Z@``*__I]`%J=%+_='3HW."#Q`:+QR3^B4;^:<"X``6$!XE&
+M]HO'T>`%U`6)1O2+1O[1X`74!8E&Z(O8BP<%"@")A(X`BU[HBP<%"@")A(P`
+MBU[HBP<%"@"+7O:)AXX`BU[HBP<%"@"+7O:)AXP`BU[TBP>)A)0`BU[HBP>+
+M7O:)AY0`BU[TBP=`0(F$D`"+7O2+!P4&`(F$D@"+1O:)1'B+QRKDF8#._XE$
+M?(E4?HO'N1``F??YB\>+RIDSPBO"P?@$,\(KPHO8T>.X`(#3Z(6'Q@9U`^D!
+M`H-^Z@!]`^GX`8M&[(M6[M'JT=C1ZM'8T>K1V-'JT=B*3NR#X0^)C+``B82R
+M`,>$I@```,>$M````,>$M@```*$6!BO2`4;L$5;NBT;PBU;RT>K1V-'JT=C1
+MZM'8T>K1V(I.\(/A#XF,J`")A*H`QX2L````QX2N````H10&*](!1O`15O(Y
+M='AU3U*+A)0`!1H`4.@FWX/$!&HCBX24``4<`%#H%M^#Q`1JW(N$E``%'@!0
+MZ`;?@\0$:-T`BX24`$!`4.CVWH/$!&IOBX24``4(`%#HYMZ#Q`0Y='AU!2K)
+MZP.0L0&`P0*X`0#3X%"+7'B+AY0`!1X`4.C`WH/$!#ET>'4%*LGK`Y"Q`;@!
+M`-/@4(M<>(N'E``%'@!0Z)W>@\0$1SD^$!]_`^GE`&GWN`"!QH0']\</`'0#
+MZ:P`BT;X]R84!@40`%#HGM:#Q`*)1O")5O*+1OCW)A8&!1``4.B'UH/$`HE&
+M[(E6[HM&\@M&\'0'B\(+1NQU!<=&ZO__BT;PBU;R@.0/BL2*XHK6*O;1ZM'8
+MT>K1V-'JT=C1ZM'8`T;P@](`!0\`@](`)/")1O")5O*+1NR+5NZ`Y`^*Q(KB
+MBM8J]M'JT=C1ZM'8T>K1V-'JT=@#1NR#T@`%#P"#T@`D\(E&[(E6[FITZ/O5
+M@\0"B41TB51VB\(+1'1T`^DN_<>$@```@,>$@@```.D2_Y"A%`;!X`*Y!0`K
+MTO?QH\0&H10&*P;$!CT0`'8(H10&+1``ZP6A%`9(2*,*'Z'$!CD&"A]S!J$*
+M'Z/$!J$4!M'HHVH'H10&*]+W\:-F!Z$6!BO2]_&C:`>A%@;!X`(KTO?QHW8&
+M*\!>7\G#R!8``%=6QT;X``+'1OH``,1>^":+1PZCQ@8FBT<:H\@&)HM'$*-0
+M!J'&!HE&\*'(!HE&\L=&]!\`ZQJ0BT;TF3/"*\+!^`0SPBO"B_C1Y]%K\/].
+M](-^]`!\&8M&])DSPBO"P?@$,\(KPHOXT>?V0_`!=,B+1O1`HQ`?/2``?@;'
+M!A`?(`"#?O(`=2#'1O0``(M>]-'CBX>T!8M>]-'CB8?4!?]&](-^]!!\Y<<&
+M)@4``,<&*`4``&H`:@5H?EIJ".CRX(/$".B@^H,^$!\`?E^#/E`&`'0Z:@!J
+M!6A]7FH@Z-+@@\0(:@!J!6B07FHAZ,/@@\0(:@!J!6@0/FH-Z+3@@\0(:B)H
+M.O_H1]R#Q`3K'FH`:@5H?5YJ#>B8X(/$"&H`:@5HD%YJ#NB)X(/$"(,^$!\$
+M?AYJ`&H%:*->:B+H<^"#Q`AJ`&H%:+9>:B/H9."#Q`B#/A`?"'X>:@!J!6C)
+M7FHDZ$[@@\0(:@!J!6C<7FHEZ#_@@\0(@SX0'PQ^'FH`:@5H[UYJ)N@IX(/$
+M"&H`:@5H`E]J)^@:X(/$"(,^$!\0?GAJ`&H%:!5?:BCH!."#Q`AJ`&H%:"%?
+M:BGH]=^#Q`AJ`&H%:"U?:BKHYM^#Q`AJ`&H%:#E?:BOHU]^#Q`AJ`&H%:$5?
+M:BSHR-^#Q`AJ`&H%:%%?:BWHN=^#Q`AJ`&H%:%U?:B[HJM^#Q`AJ`&H%:&E?
+M:B_HF]^#Q`BA(!^+%B(?B4;\B5;^QT;T``!J`&H*_S90!?\V3@7HCQ%24/]V
+M_O]V_.@Z$(M>]-'CB8=4!O]&](-^]`I\U,=&]`H`:@!J"HM>],'C`O^W+`7_
+MMRH%Z%@14E#_=O[_=OSH`Q"+7O31XXF'5`;_1O2#?O01?,['!A0&``#'!A8&
+M``"#/A`?`'YGH1`?T>"94E"#/A`?$'X%N%``ZP.X*`"9BPYL!XL>;@<KR!O:
+M:P80'WB9*\@;VE-1Z/40B4;JB5;L"])U!3WP#W8(QP84!O`/ZQ<+TG4./4``
+M<PG'!A0&0`#K!I`D\*,4!J$4!J,6!NB&^;Z$!\=$!(`EQT0&``#&1#0(QD0V
+M`,9$-0`KP(E$'HE$'%!05N@1\H/$!L=&]```Z)KM_T;T@W[T9'STQT;J(`#'
+M1NP``*$&`,1>ZB;'!S!:)HE'`L=&ZDP`QT;L``"A!@#$7NHFQP=&6B:)1P+'
+M!B0%`0`KP%Y?R<.0R`0``%:+=@2#?'H`=`F+7'J#?PP`=0N`I(``WRO`7LG#
+MD(M$5(M45B%$9"%49HM$6(M46B%$:"%4:HM$7(M47B%$;"%4;HM$9@M$9'40
+MBT1J"T1H=0B+1&X+1&QTN8U$9!Y0:@!H#V'_='[_='R+7'K_5PR#Q`PKP(E$
+M9HE$9(E$:HE$:(E$;HE$;.N*D,@"``!75FD&0A^X``6$!XE&_BO_:38P!K@`
+M@<:$!^M@.7;^=P.^A`?VA(```71,]H2```9T!U;H"NV#Q`+VA(``0'0'5NB^
+M[8/$`H"\F@``=`=6Z,@`@\0"@[R*``!T#?^,B@!U!U;H"@>#Q`+VA(``('0'
+M5NCZ_H/$`H'&N`!'.3Y"'W^:H4(?.08P!GP-QP8P!@``*\!>7\G#D/\&,`8K
+MP%Y?R</(`@``5U:^A`?'1OX``"O_ZQKVA(```70.BH2C`"KD.T;^=@.)1OY'
+M@<:X`#D^$!]_X*&R!3E&_G0YBT;^H[(%"\!U!E!H5O_K(V@!X&A6_^A#V(/$
+M!(M>_M'C_[=4!FA2_^@QV(/$!&H`:%#_Z";8@\0$7E_)PY#(!```5HMV!.A_
+MVXE&_HM<>(N'E``%"`!0Z/G7@\0""H2:`(A&_,:$F@``_W;^Z%W;@\0".71X
+M=0N`9OQ5:@%J!.L)D(!F_*IJ`FH(BD;\4%;H!@"#Q`A>R</(`@``5HMV!(I&
+M"")&!CP!&L#VV(B$G`"*1@HB1@8\`1K`]MB(A)X`BD8&P.@$B$;^A$8(=!?V
+M1!A`=1'$7'0F@T=(`2:#5TH`@$QL`HI&_H1&"G0J]D08"'03Q%QT)H-'2`$F
+M@U=*`(!,;`+K$<1<=":#1U`!)H-74@"`3&P(5NA#`X/$`HM$4(M44HE$<(E4
+M<H",@``@*\!>R</(`@``5U:+=@3V1@8"=$KH@MJ)1O[_-A8&C82P`%#HP06#
+MQ`2+^)G$7'0F*4<$)AE7!H"DA`#`]H2!``AT"HN$M`")A*8`ZPB+A+8`B82T
+M`/]V_NA"VH/$`O9&!@%T4.@RVHE&_O\V%`:-A*@`4.AQ!8/$!(OXF<1<="8!
+M1P@F$5<*QX2L````QX2N````@*2%`,"`I(``O_]V_NCZV8/$`O:$@`"`=`=6
+MZ*[Q@\0"7E_)PY#(`@``5HMV!*$6!DC$7@8FB4<$H10&2,1>!B:)1P;HO]F)
+M1O[_-A8&C82P`%#H_@2#Q`3$7@8FB0?_-A0&C82H`%#HZ02#Q`3$7@8FB4<"
+M_W;^Z(_9@\0"Q%X&*\`FB4<*)HE'"/:$@``"=`:X`0#K!)"X`@`KTL1>!B8)
+M1PCVA(``0'0%N``!ZP.X``+$7@8F"4<(]H2``(!T!;@`0.L"*\#$7@8F"4<(
+M]H2```AT",1>!B:`3PH!7LG#D,@(``!75HMV!(U\2(IE`24`@(D%QT4"``#H
+M"-F)1OS_-A8&C82P`%#H1P2#Q`2)1OC_-A0&C82H`%#H-02#Q`2)1OK_=OSH
+MW]B#Q`+VA(```G0%N`$`ZP.X`@`KT@D%]H2!``1T!KB``.L#D"O`"07VA(``
+M"'0$@$T"`?9$'"!T"O9%"`AU!(!-`@3V10('=`.`#4"+1O@Y1"AR`X`-!(M&
+M^#E$*G<#@`T(H18&*T;X2'4#@`T0]H2``$!T!;@``>L#N``""07VA(``@'0&
+MN`!`ZP.0*\`)!8M&^CE$+'($@$T!!(M&^CE$+G<$@$T!"(M&^CE$,'<$@$T!
+M(*$4!BM&^DAU!(!-`1!>7\G#D%6+[%:+=@0+]G4#Z9L`BT8("T8&=0/ID`!6
+MZ(\`@\0"5NC._H/$`HM$2(M42L1>!B:)1VPFB5=NBT10BU12Q%X&)HE':":)
+M5VK_-A8&C82P`%#H#P.#Q`29Q%X&)HE'#":)5P[_-A0&C82H`%#H]`*#Q`29
+MQ%X&)HE'$":)5Q+$7@8FBT<,)HM7#B8I1P0F&5<&Q%X&)HM'$":+5Q(F`4<(
+M)A%7"H!D27]>R</("```5HMV!(I$4"4$`(E$4,=$4@``BUQXBX>4``4(`%#H
+MT=.#Q`*)1OXY='AU!\=&_```ZP7'1OP!`(I._(#!`M/X)0$`/0$`&\#WV(E&
+M^(I._(M&_M/X)0$`/0$`&\#WV(E&^O9$&`AT"X!,4`B#?OH`ZPV0"\!T!(!,
+M4`B#?O@`=`2`3%`"]D080'0$@$Q0`O9$&`1T"("\G0``ZQB0@+R=``!T!X!,
+M4`3K!9"`9%#[@+R;``!T!(!,4`&`3%`07LG#R`8``%=6BW8$BWX&]D08!'0#
+MZ;8`]\<"`'4&Z(C6B4;Z]\<!`'0'QT;\``'K!<=&_``"QT;^``#WQP0`=!2+
+M1OR+5O[WT/?2(82``"&4@@#K#HM&_(M6_@F$@``)E((`]\<$`'0BQH2=``$Y
+M='AU!"K)ZP*Q`;@!`-/@4(M<>(N'E``%'`#K(,:$G0``.71X=00JR>L"L0&X
+M`0#3X%"+7'B+AY0`!1X`4.A[TH/$!,1<=":#1U0!)H-75@#WQP(`=0G_=OKH
+MXM6#Q`)>7\G#D,@$``!75HMV!(M^!O?'`@!U!NC!U8E&_O9$&`1T",=&_```
+MZP:0QT;\`@#WQP0`="?&A)L``3ET>'4%*LGK`Y"Q`0)._+@!`-/@4(M<>(N'
+ME``%'`#K))#&A)L``#ET>'4$*LGK`K$!`D[\N`$`T^!0BUQXBX>4``4>`%#H
+MV-&#Q`3$7'0F@T=,`2:#5TX`]\<"`'4)_W;^Z#_5@\0"7E_)PU6+[%:+=@1J
+M<(N$E``%!`!0Z*'1@\0$@*2!`/O'A(H```!>R<.058OL5U:+=@3H`]6+^("D
+M@`#O:@2+A)0`!00`4.APT8/$!%?H[-2#Q`)>7\G#D%6+[%=6BW8$Z-74B_B`
+MC(``$&H(BX24``4$`%#H0M&#Q`17Z+[4@\0"7E_)PY!5B^Q75HM>!(MW!(M_
+M!CO^=PB+QBO'7E_)PXM&!BO'`\9>7\G#D,@&``!75HMV!.B`U(OX:<:X``6$
+M!XE&^HO8BX>``(N7@@")1OSV1OP0=`=3Z$__@\0"@WX&"G4IB][1XXN'U`5`
+M0%#HT="#Q`*H!'3K:@V+WM'CBX?4!04&`%#HK]"#Q`2+WM'CBX?4!4!`4.BH
+MT(/$`J@$=.O_=@:+WM'CBX?4!04&`%#HA="#Q`3V1OP0=!Z+WM'CBX?4!4!`
+M4.AXT(/$`J@$=.O_=OKH`/^#Q`)7Z-W3@\0"7E_)PP``````````````````
+M8!X&+HX><`'H0P"Z(O^X`(#O!Q]ASV`>!BZ.'G`!NB+_N`"`[XL64`"#^@!U
+M'/\&4`"Z*O_M4+@$`._[Z#D'^EBZ*O_O,\"C4``''V'/NR8%BQ^#^P!T+HN7
+MD`#LJ`1T\(N/M`"+O[8`._ET'L2WL``FB@6+EY(`[D<[/A8&=`>)O[8`Z\O#
+M,__K]?>'@```"'0:BX^F`(F/M`"!IX``__<[^70(]X>```@`=,"+?P*+-XDU
+M@_X`=`.)?`+'1P(``,<'``"#CX``!(.G@`#]B][K@(.E@`#W@WT"`'484U:[
+M)@6+-X/^`'0#B7P"B3^)-8E=`EY;PX.-@``(]X6````(=1Q35HM=`HLUB3>#
+M_@!T`XE<`EY;QT4"``#'!0``PXN5E`"#P@CLBN`D4'0$"(6:`(OW@<:X`(#D
+MH'0$"*2:`.L7D(N5C``SP.Z+E8X`["*%H@"+Z*B`=<;WQ08`=0/I9P'$A:@`
+MB[6L`(N=K@`[\W('B\8KP^L(D"O>H10&*\,[12YR$?>%@`"``'4)!E?H6NN#
+MQ`('BYV4`(/#!HN-D`"+T>R*X/;$`74#Z?,`B]/L]L3P=`/IS``\`'4']T4.
+M`@!UW?=%#`0`=`(D?XI5'/;"`74#ZU"0BK6``#I%.70M.D4Z=!#VQ@AT//;"
+M!'0WZ-[^ZS*0B],&Q%UT)H-'0`$F@U="``>+VNC%_NN3B],&Q%UT)H-'1`$F
+M@U=&``>+VNC1_NEZ__:%H0#_=!@\_W44)H@$1CLV%`9U!"LV%`8[M:X`=!<F
+MB`1&.S84!G4$*S84!CNUK@!T`^E$_U<&Q'UT)H-%,`$F@U4R`":#130!)H-5
+M-@`'7X/^`'4$BS84!D[I'/^)M:P`8#+VBM12,N105^@FXX/$!F&+M:P`Z0#_
+MB;6L`/?%!`!T"HN5E`"#P@2P4.Z#C8``0("]F```=0B*A9D`B(68`/?%8`!U
+M`^EO`8''N`#$A:@`B[6L`(N=K@`[\W('B\8KP^L(D"O>H10&*\,[12YR$?>%
+M@`"``'4)!E?HYNF#Q`('BYV4`(/#!HN-D`"+T>R*X/;$`74#Z?,`B]/L]L3P
+M=`/IS``\`'4']T4.`@!UW?=%#`0`=`(D?XI5'/;"`74#ZU"0BK6``#I%.70M
+M.D4Z=!#VQ@AT//;"!'0WZ&K]ZS*0B],&Q%UT)H-'0`$F@U="``>+VNA1_>N3
+MB],&Q%UT)H-'1`$F@U=&``>+VNA=_>EZ__:%H0#_=!@\_W44)H@$1CLV%`9U
+M!"LV%`8[M:X`=!<FB`1&.S84!G4$*S84!CNUK@!T`^E$_U<&Q'UT)H-%,`$F
+M@U4R`":#130!)H-5-@`'7X/^`'4$BS84!D[I'/^)M:P`8#+VBM12,N105^BR
+MX8/$!F&+M:P`Z0#_B;6L`/?%0`!T"HN5E`"#P@2P4.Z#C8``0("]F```=0B*
+MA9D`B(68`('ON`"+E8P`BH6B`.[#@S[(!@!T!KK$`K`@[K^$!^GE_(,^R`8`
+M=`:ZQ`*P(.Z_]`CITOR#/L@&`'0&NL0"L"#NOV0*Z;_\@S[(!@!T!KK$`K`@
+M[K_4"^FL_(,^R`8`=`:ZQ`*P(.Z_1`WIF?R#/L@&`'0&NL0"L"#NO[0.Z8;\
+M@S[(!@!T!KK$`K`@[K\D$.ES_(,^R`8`=`:ZQ`*P(.Z_E!'I8/RZR`*P(.Z_
+M!!/I5/RZR`*P(.Z_=!3I2/RZR`*P(.Z_Y!7I//RZR`*P(.Z_5!?I,/RZR`*P
+M(.Z_Q!CI)/RZR`*P(.Z_-!KI&/RZR`*P(.Z_I!OI#/RZR`*P(.Z_%!WI`/P`
+M58OLBUX$_W8(_W8&_W<"_S?H+@"+7@2)5P*)!XOE7<(&`%6+[(M>!(L'BU<"
+MBTX&Z/,!BUX$B0>)5P*+Y5W"!```58OL5U93,_^+1@8+P'T2]]>+5@3WV/?:
+M'0``B48&B58$BT8*"\!]$O?7BU8(]]CWVAT``(E&"HE6"`O`=1:+3@B+1@8S
+MTO?QB]B+1@3W\8O3ZSR0B]B+3@B+5@:+1@31Z]'9T>K1V`O;=?3W\8OP,])2
+M4/]V"O]V".@T`3M6!G<'<@@[1@1V`X/N`3/2B\8+_W0']]KWV(/:`%M>7XOE
+M7<((``!5B^Q35S/_BT8&"\!]$O?7BU8$]]CWVAT``(E&!HE6!(M&"@O`?1"+
+M5@CWV/?:'0``B48*B58("\!U&HM."(M&!C/2]_&+1@3W\8O",](+_W5%ZTJ0
+MB]B+3@B+5@:+1@31Z]'9T>K1V`O;=?3W\3/24E#_=@K_=@CHD0`[5@9W!W(+
+M.T8$=@8K1@@;5@HK1@0;5@8+_W4']]KWV(/:`%];B^5=P@@`58OL4U:+1@H+
+MP'46BTX(BT8&,]+W\8O8BT8$]_&+T^L\D(O(BUX(BU8&BT8$T>G1V]'JT=@+
+MR77T]_.+\#/24E#_=@K_=@CH'``[5@9W!W((.T8$=@.#[@$STHO&7EN+Y5W"
+M"```58OLBT8&BUX*"]B+7@AU"XM&!/?CB^5=P@@`]^.+R(M&!/=F"@/(BT8$
+M]^,#T8OE7<((`#+MXP;1X-'2XOK#`(,&O`8!@Q:^!@#_!G`'@SYP!V1S`^F!
+M`(,&=@<!@Q9X!P#'!G`'``"A=`<+!G('="C_-G0'_S9R!VH`:F2A<@>+%G0'
+M*P:`!QL6@@=24.AO_U)0Z`3_HT@?@SY('P!\!X,^2!]D?@;'!D@?``"#/DX?
+M`WP-_S9('VA`!NB.PH/$!(,^3A\"?`EH0P;H?L*#Q`(KP*."!Z.`!^F2J```
+*`````````````.B.
+`
+end
diff --git a/usr.sbin/stallion/bootcode/Makefile b/usr.sbin/stallion/bootcode/Makefile
new file mode 100644
index 0000000..d026830
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/Makefile
@@ -0,0 +1,20 @@
+# $Id: Makefile,v 1.7 1997/10/20 12:50:56 charnier Exp $
+
+.SUFFIXES: .uu
+.uu:
+ rm -f ${.TARGET}
+ uudecode ${.IMPSRC}
+
+FILES= 2681.sys cdk.sys
+CLEANFILES= ${FILES}
+
+MAN4= stl.4
+MLINKS= stl.4 stli.4
+
+all: $(FILES)
+
+beforeinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${NOBINMODE} \
+ ${FILES} ${DESTDIR}${BOOTDIR}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/stallion/bootcode/cdk.sys.uu b/usr.sbin/stallion/bootcode/cdk.sys.uu
new file mode 100644
index 0000000..f8b33ed
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/cdk.sys.uu
@@ -0,0 +1,733 @@
+begin 444 cdk.sys
+M````````````````````````````````````````````````````````````
+M``````!@`0``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`-(1SV0``````````````````````````````````````!``````8V1K:"YC
+M```F`````````````````.`5`````````````,($``!F!0,``````````&YO
+M(#(V.#$@9')I=F5R(&EN<W1A;&QE9`!M96UO<GD@;6%L;&]C(&9A:6QE9```
+M`````$%34T525$E/3B!&04E,55)%(&%T("5S*"5D*0H``(H`24Y&3U)-`%=!
+M4DY)3D<`1D%404P`4$%.24,`15)23U(@)7,Z("5S*"5D*3H@``H`J@"Q`+D`
+MOP#%`#$N,"XS`"A#*2!#;W!Y<FEG:'0@4W1A;&QI;VX@5&5C:&YO;&]G:65S
+M(#$Y.3,@+2`Q.3DU``H*+2TM+2TM+2TM+2TM+2TM("!3=&%L;&EO;B!);G1E
+M;&QI9V5N="!#;VUM=6YI8V%T:6]N<R!0<F]C97-S;W(@("TM+2TM+2TM+2TM
+M+2TM+0H`("`@("`@("`@("`@("`@("5S"@H`("!";V%R9"!4>7!E("`@("`@
+M("`@("`@("`@("`@("4R9"`@("`@("`@("`@($-O9&4@5F5R<VEO;B`@("`@
+M("`@("`@("`@("5S"@`@($UE;6]R>2!4;W1A;"`@("`@("`@("`@,'@E,#9L
+M>"`@("`@("`@("`@($UE;6]R>2!5<V5D("`@("`@("`@("`@(#!X)3`V;'@*
+M`"`@365M;W)Y($9R964@("`@("`@("`@("`P>"4P-FQX("`@("`@("`@("`@
+M365M;W)Y($UA<"`@("`@("`@("`@("`@,'@E,#9L>`H`("!#;V1E(%-I>F4@
+M("`@("`@("`@("`@(#!X)3`V;'@@("`@("`@("`@("!$871A(%-I>F4@("`@
+M("`@("`@("`@("`P>"4P-FQX"@`@($-O9&4@4V5G;65N="`@("`@("`@("`@
+M("`P>"4P-'@@("`@("`@("`@("!$871A(%-E9VUE;G0@("`@("`@("`@("`@
+M(#!X)3`T>`H`("!3=&%C:R!396=M96YT("`@("`@("`@("`@,'@E,#1X("`@
+M("`@("`@("`@17AT<F$@4V5G;65N="`@("`@("`@("`@("`P>"4P-'@*"@`@
+M($%S>6YC:')O;F]U<R!086YE;',@("`@("`@("`@("5D("`@("`@("`@("`@
+M07-Y;F-H<F]N;W5S(%!O<G1S("`@("`@("`@("`@)3)D"@`*+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+0H*``#D`.H`,#$R,S0U-C<X.6%B
+M8V1E9@``_@,``````````````````)\KI2NK*[$KMRN]*\,KR2O/*]4KVROA
+M*^<K[2OT*_LK`BP)+!`L%RP>+"4L+"PS+#HL02Q(+$\L5BQ=+&0L:RQR+'DL
+M@"R'+(XLE2R<+*,LJBRQ++@LORS&+,TLU"S;+.(LZ2SP+/<L_BP%+0PM$RT:
+M+2$M*"TO+38M/2U$+4LM8V0Q-#`P+F,``*`$````````@````(``"``@`(``
+M``(`".I-UB^04"@P4#'L,0``5CP8.J`UE%#P8V-D,30P,"YC`&-D,30P,"!N
+M;W0@<F5S<&]N9&EN9RP@8F]A<F0])60@<&%N96P])60@9&5V:6-E/25D``H`
+M`````&-D,30P,"YC``H`8V0Q-#`P+F,`8V0Q-#`P(&YO="!R97-P;VYD:6YG
+M+"!B;V%R9#TE9"!P86YE;#TE9"!D979I8V4])60`"@``DF```````````)9@
+M`````````````,AA``!C9#$T,#`Z('-T<F%Y(%)8(&EN=&5R<G5P="`H86-K
+M/25X*0H`8V0Q-#`P.B!S=')A>2!46"!I;G1E<G)U<'0@*&%C:STE>"D*`&-D
+M,30P,#H@<W1R87D@34]$14T@:6YT97)R=7!T("AA8VL])7@I"@``````)60`
+M+@```````/J\``0SP([0CL"X``&.V#/`CL`F@3X``J?R=2<F@3X"`DFA=1XF
+M@3X$`E)C=14F@3X&`B'Q=0PFH0H")HL>#`+K8I"Y6M`S]H[&)J$``":)#@``
+MO@`0BQ8``([&)CD.``!T"H'&`!"!_@"`<NDSTH[")J,``(S(NS!J@^/PP>L$
+M`]A#CL,FH0``)HD.``"+%@``)CD.``!U"2:C``!#.]YRX3/`HPP`QP8.````
+MB]/!X@0!%@P`@Q8.``"+T\'J#`$6#@"A#`"+%@X`N3!JB0X8`(/!'X/A\(OQ
+MB_DKP8/:`,'H!,'B#`O0CL)/3HS(CMC]\Z3\!FCZ`,NX``&.V+D0.HOQB_F+
+MV8D>)``#'B``@\,/@^/PB\/!Z`0KT(["3D_]\Z3\C,".V([`CM"+XX/L!+\`
+M!C/`N1`ZB0X<`"O/_/.JC,BC!@",V*,$`"ZC<`&,T*,*`(S`HP@`C-C!X`2C
+M%`",V,'H#*,6`.@F'NO^``#(!```5U:+1@B9BQXP.3E7$')O=P4Y1PYR:(MW
+M!)F+^(O:`WX$$UX&@^\!@]L`B\X*R70(T>O1W_[)=?B+QYF+SXO:BT8$BU8&
+MB4[\B5[^B\X*R70(T>K1V/[)=?@[1OQU!3M6_G0YB\>9B\X*R70(T>#1TO[)
+M=?B)1@2)5@;K()"0F3E7$'<8<@4Y1PYS$6C?`/\V+@#_-J@`Z)4B@\0&BT8$
+MBU8&7E_)P\@0``!75HL>,#F+1Q*+5Q0Y%A8`=Q]R!CD&%`!S%Z$4`(L6%@`K
+M1@0;5@;1ZM'8`T8$$U8&*T8$&U8&B4;PB5;R*_^+1PZ+5Q")1O2)5O8[UW4$
+M.\=T#5)0_W;R_W;PZ&IFB_@+_W0(BT;V"T;T=0^_`0"+1O"+5O*)1O2)5O:+
+M1@C1X`/'*]+W]XOP"_9U`[X!`&H`5O]V]O]V].@M9HE&^(M&"-'@`\=J`%#_
+M=O+_=O#H%V:)1OZ+1O@Y1OYV!HM&_HE&^#V``',%QT;X@`"+1OA>7\G#D,@<
+M``!75L=&[@`#QT;P``#'!@@Z@`+'!@HZ``#$'@@Z)HM'!*,,.B:*1QPE`0`]
+M`0`:P/[`HE8`)HM'$B8+1Q!T"B:+1Q"C5@[H"AF+1NZ+5O#1ZM'8T>K1V-'J
+MT=C1ZM'8BD[N@^$/B0Y8#J-:#H%&[H``@U;P`&B``%!1Z$DG@\0&Q!Y8#B;'
+M1P8!`,0>6`XFQT="_P#$'E@.)L9'3`'$'E@.)L9'30#$'E@.)L9'3@#$'@@Z
+M)HM'#(E&Y(L>,#F+!RT7`'P$2$A^%&A7`&A\`?\V+@!J`NC?&H/$".MS:@!J
+M`&H`:`=#:O]J__\6<`6#Q`P+P'TPBQXP.8-_*`!T)J%(`*/V.8O8@W\6`'0/
+M:@%J!?]W%FH-Z)0J@\0(QP;*.3Q?ZRR0H4P`H_8YB]B#?Q8`=`]J`6H%_W<6
+M:@WH;BJ#Q`C'1N0``(L>,#G'1R@``(M&Y$"C$`;$'E@.)HE'4J$0!DB9,\(K
+MPL'X`S/"*\)`H[XYH1`&!0<`F3/"*\+!^`,SPBO"0"3^B4;LBT;NBU;PQ!Y8
+M#B:)1U@FB5=:BT;L*](!1NX15O"+1NZ+5O#$'E@.)HE'7":)5UZ+1NPKT@%&
+M[A%6\%#$'E@.)HM'6":+5UK1ZM'8T>K1V-'JT=C1ZM'8)HI76(/B#XO*4%'H
+MUB6#Q`;_=NS$'E@.)HM'7":+5U[1ZM'8T>K1V-'JT=C1ZM'8)HI77(/B#XO*
+M4%'HIB6#Q`:A$`:)1NRX!@#W9NR)1NR+1NZ+5O#$'E@.)HE'5":)5U:+1NPK
+MT@%&[A%6\,0>6`XFBT=4)HM75M'JT=C1ZM'8T>K1V-'JT=@FBD]4@^$/B4[H
+MB4;J_W;L4%'H1B6#Q`:+'C`YBP<M%P!\!$A(?@G'!E````/K$9#'!E````&+
+M'C`Y@W\H`'0(QT;\`0#K!I#'1OP"`&B2`/]V\/]V[NBW^X/$!HE&[HE6\(M&
+M_,1>Z":)!XM&[HM6\,1>Z":)1P(FB5<$:)(`BT;NBU;PT>K1V-'JT=C1ZM'8
+MT>K1V(I6[H/B#XO*4%'HN22#Q`:!1NZ2`(-6\`"#1N@&BQXP.8-_*`!^$(M&
+MY"M'*)GW?RB)1O3K!I#'1O0``(-^]`!]!<=&]```QT;^``#'1N8``"OVZ0(!
+MD(M^_M'GBT$8BT[T0??I.T;F?@O$7N@FQP<`!.L)D,1>Z";'!P``BT;NBU;P
+MQ%[H)HE'`B:)5P1HO`"+1NZ+5O#1ZM'8T>K1V-'JT=C1ZM'8BE;N@^(/B\I0
+M4>@2)(/$!H%&[KP`@U;P`/]&YHM^_M'GBQXP.8M!&(M.]$'WZ3M&YG]YQT;F
+M``#_1OZ#?OX(?&O$'@@Z)HM'%(E&\B:+1QB)1O:#?O(`=`D+P'4%QT;V``&#
+M?O8`=`N#?O(`=07'1O(``8-^\@!U&X-^]@!U%?]VY/]V\/]V[NCS^H/$!HE&
+M\HE&]L0>6`XFBT=4)HM75HE&Z(E6ZBOVZT60D$:#1N@&.7;D?HMHO`#_=O#_
+M=N[H$OJ#Q`:)1NZ)5O"+1N:+?O[1YXL>,#DY01A_`^G1_L1>Z";'!R``Z>S^
+M1H-&Z`8Y-A`&?P/IH@#$7N@F]P?@_W3H)HM'`B:+5P31ZM'8T>K1V-'JT=C1
+MZM'8)HI/`H/A#XE.^(E&^O]V\O]V\/]V[NBA^8/$!HE&[HE6\,1>^":)A[(`
+M)HF7M`"+1O+$7O@FB8>V`(M&\BO2`4;N$5;P_W;V_W;P_W;NZ&GY@\0&B4;N
+MB5;PQ%[X)HF'I@`FB9>H`(M&]L1>^":)AZH`BT;V*](!1NX15O#I4/]J`&H$
+M:!0+:@SH8":#Q`B+1NZ+5O"CN@V)%KP-HQ``B182`"O`7E_)PY#(%@``5U9K
+M!A`&*E#H(1J#Q`*C$@:)%A0&BQ[V.?\7H10&"P82!G42:'``:#<"_S8N`&H"
+MZ!46@\0(:P80!BI0_S84!O\V$@;H$"*#Q`;$'E@.)HM'5":+5U;1ZM'8T>K1
+MV-'JT=C1ZM'8)HI/5(/A#XE.[HE&\,=&]#``H1(&BQ84!HE&^HE6_,=&_O__
+MQT;R`0`K_ROVZ8@!)HM'!":+5P:)1NJ)5NS$7NHFBX>R`":+E[0`T>K1V-'J
+MT=C1ZM'8T>K1V":*C[(`@^$/Q%[Z)HE/"":)1PK$7NHFBX>F`":+EZ@`T>K1
+MV-'JT=C1ZM'8T>K1V":*CZ8`@^$/Q%[Z)HE/#":)1P[$7OHF]T<FX`-U`^FG
+M`(-^\@!U`^F>`/]&_HM>_M'CB;=$#HM&_HL>,#DY1Q9^!3T(`'Q\BQXP.8M?
+M%M'CB;=$#L0>6`XFBT=8)HM76M'JT=C1ZM'8T>K1V-'JT=@FBD]8@^$/B0[`
+M.:/".2:+1UPFBU=>T>K1V-'JT=C1ZM'8T>K1V":*3UR#X0^)#MXXH^`XQT;V
+M8@+'1O@``,1>]B;&!PW$7O8F@#\`=0/IY`#K\BO_B7[RB\<JY)F+R(I&_HO:
+MF8/*_XKRBM2*X"K`"\$+T\1>^B:)!R:)5P*+'O8Y@W\"`'08C4;T4,1>^B;_
+M=P(F_S>+'O8Y_U<"@\0&1XM>_M'C`QXP.3E_&'\%QT;R`0!&@T;Z*H-&[@8Y
+M-A`&?P/I$?_$7OHFB7<DQ%[N)HL'Q%[Z)HE')L1>[B:+1P(FBU<$T>K1V-'J
+MT=C1ZM'8T>K1V":*3P*#X0_$7OHFB4\$)HE'!HO&P>`#F?<^$`:+R+`!TN#$
+M7OHFB$<HQ%[Z)O9')A]UC":#?R8`=`/I`_[I?__$'E@.)H-_0@!U!L<&4```
+M`"O`7E_)PY#(#```5U;$'E@.)H-_4`!T0J&&`/\&A@`]9`!V"O\&B`#'!H8`
+M```F@']/`'0+QP:(````)L9'3P"AB`#$'E@.)CE'4'<-Z#L0Q!Y8#B;'1U``
+M`,0>6`XF@']A`'1>*__K)H5V^'03:\`J`P82!HL6%`924.@-`8/$!/]&^M'F
+MBT;Z.4;^?]M'.3Z^.7XNQ![>.":*`9B)1O@+P'3JB\?!X`.)1OH%"`")1OZA
+M$`8Y1OY^`XE&_KX!`.O$D,0>6`XF@']@`'4#Z8<`QT;V```K_^M`D(5V^'0L
+M:\`J`P82!HL6%`924.CS!X/$!`O`?15K7OHJ`QX2!HX&%`8FBD<H*N0)1O;_
+M1OK1YHM&^CE&_G_"1SD^OCE^+<0>P#DFB@&8B4;X"\!TZHO'P>`#B4;Z!0@`
+MB4;^H1`&.4;^?@.)1OZ^`0#KQ(I&]L0>6`XFB$=@@SY0``!T&8,^4@``=!)J
+M`/\V4`#HZ1V#Q`3'!E(```"+'O8Y@W\4`'0#_U<4*\!>7\G#R"H``%=6Q%X$
+M)HM'!":+5P:)1NB)5NHF]D<F'W0,)HI')BO2M/\*].L')HL')HM7`HE&_(E6
+M_L1>Z":`/P!U`^EL`2:+1P(FBU<$B4;@B5;B*\`FB4<$)HE'`L1>Z";&!P"+
+M'O8Y.4<&=#O$7N@F_W<$)O]W`O]V_O]V_(L>]CG_5P:#Q`B+^(/_`70F"_]\
+M`XU%`8E&WIG$7N@FB4<")HE7!.L.D,1>Z";'1P(!`":)1P2+'O8Y@W\*`'4#
+MZ;H`C4;LB\B+%@0`4E%J`&@-8?]V_O]V_(L>]CG_5PJ#Q`R+^(/_`741:/\"
+M_S8N`/\VJ`#H%Q>#Q`8+_WQ^]D;P!'4B@$[P!,1>!":`3Q`(Q%[H)HN'M@"Y
+M!0`KTO?QQ%X$)HE'$O9&\`)U#(!.\`+$7@0F@$\0$/9&\0%U#(!.\0'$7@0F
+M@$\0@,1>!";V1Q"8=""-1NR+R(L6!`!246H`:`YA_W;^_W;\BQ[V.?]7"H/$
+M#,1>!":`9Q";QP92``$`Q%X$)HI/)(#A![`!TN`FBU\DP>L#`Q[`.8X&PCDF
+M"`?$7@0FBD<HQ!Y8#B8(1V#$7N@FBT<,)@M'"G4#Z>("Q%[H)HM'"B:+5PR)
+M1N")5N*+1N"+5N(F.4<*=>,F.5<,==TKP":)1Q`FB4<.Q%[H)HE'#":)1PJ)
+M1N2+1N#I4@&0D(M&Z(M6Z@42`(E&VHE6W,1>VB;V1P0$=1/$7@0F@$\0",1>
+MVB:`3P0$ZPF0Q%X$)H!G$/?$7MHF]D<$`G42Q%X$)H!/$!#$7MHF@$\$`NL(
+MQ%X$)H!G$._$7MHF]D<%`744Q%X$)H!/$(#$7MHF@$\%`>D'`9#$7@0F@&<0
+M?^G[`)#$7N@FBT<2B4;D_W;D_W8&_W8$Z+@+@\0&Z=X`QT;D`@"!?N`,874&
+M@W[B`'0-@7[@"F%UU8-^X@!USX!.Y`'KR9#'1N0"`(%^X`=A=0:#?N(`=`V!
+M?N`%874*@W[B`'4$@$[D`?]VY/]V!O]V!.A="X/$!HM&Z(M6Z@42`(E&VHE6
+MW,1>Z":+A[8`Q%[:)CE')'(1Q%[H)HN'M@!(2,1>VB:)1R3$7MHF@W\D`'41
+MQ%[H)HN'M@"Y!0`KTO?QZP0FBT<DQ%X$)HE'$NLMD"T!83T-`'<DT>"3+O^G
+M^@\<#Q80%A"0#UX/7@]C#Q80%A`X#S@//0\6$)H.BQ[V.8-_"@!U`^G/`/]V
+MXO]VX.BF"H/$!`O`=`W$7N@FBT<2)HM7%.L)BT;HBU;J!1(`B4;:B5;<4E#_
+M=N+_=N#_=O[_=OR+'O8Y_U<*@\0,B_@+_W5CBT;@+1)A=5N+1NB+5NH%$@")
+M1M:)5MB+1N@%L@!24.C'!(/$!,1>UB8!!XM&Z(M6Z@6F`%)0Z&X$@\0$Q%[6
+M)@%'`L1>Z":+A[8`2,1>UB8!1P3$7N@FBX>J`$C$7M8F`4<&@_\!="\+_WP%
+MC44!ZP*+QXE&UIG$7N@FB4<.)HE7$.L3D,1>Z";'1PX!`";'1Q```+___\<&
+M4@`!`,1>!":*3R2`X0>P`=+@)HM?),'K`P,>P#F.!L(Y)@@'Q%X$)HI'*,0>
+M6`XF"$=@Q%X$)O9'$0%T3\=&\`(`QT;R```KP(E&]HE&](E&[HE&[":`9Q'^
+MQ%X$)H!/$"#'!E0``0"-3NR+%@0`4E%J`&@/8<1>!";_=P(F_S?H5P2#Q`S'
+M!E0```#$7@0F]D<0!'0#Z:@!BT;HBU;J!;(`4E#HJ0.#Q`2+\`OV?P/IC@'$
+M7@0F.7<2=P4F@&<0W\1>!":`9Q"_@#Y6``!T*(L>]CF#?PH`=!Z-1N`64&H`
+M:!-A_W;^_W;\BQ[V.?]7"H/$#`O`=`@KP(E&XHE&X(M&X@M&X'0#Z38!BQ[V
+M.8-_$`!U`^DI`<1>Z":+A[8`)HN/N@`KP3O&=P\FBX>Z`":+O[8`*_CK`Y"+
+M_E?$7@0FBT<()HM7"L1>Z":+C[H``\%24/]V_O]V_(L>]CG_5Q"#Q`J)1M8+
+MP'T%QT;6```Y=M9],SE^UG4NB\8K1M90Q%X$)O]W"B;_=PC_=O[_=OR+'O8Y
+M_U<0@\0*B_@+_WT"*_\!?M;K`BO_@W[6`'\#Z8X`.7;6=`C$7@0F@$\0!"MV
+MUL1>Z":+A[H``4;6BT;6)CF'M@!W`XE^UHM&UB:)A[H`Q%X$)CEW$G95)O9'
+M$"AU3BO`B4;RB4;PB4;VB4;TB4;NB4;L)O9'$"!U"8!.\`0F@$\0(,<&5``!
+M`(U.[(L6!`!246H`:`]AQ%X$)O]W`B;_-^BB`H/$#,<&5````,1>Z":`?P$`
+M=0/IE@`FBT<&)HM7"(E&X(E6XBO`)HE'"":)1P;$7N@FQD<!`(L>]CDY1PAT
+M*_]V_O]V_/]7"(/$!(OX@_\!="4+_WP#C44!B4;6F<1>Z":)1P8FB5<(ZPW$
+M7N@FQT<&`0`FB4<(QP92``$`Q%X$)HI/)(#A![`!TN`FBU\DP>L#`Q[`.8X&
+MPCDF"`?$7@0FBD<HQ!Y8#B8(1V`KP%Y?R</(!```5L1>!":+1P0FBU<&B4;\
+MB5;^Q%[\)H"_D@``=`>X__]>R<.0Q%X$)O9'$`%T0&H0!90`4E"+PXS"!10`
+M4E#H5!:#Q`K$7@0FBD<0)((\`G4)Q%[\)H"/F0`!Q%[\)L:'D@`!Q%X$)H!G
+M$/[KL)`FBD<0)((\`G4\:A"+1OP%E`!24.A$%H/$!L1>_":`CYD``8M&_(M6
+M_@64`%)0:@!H#V'$7@0F_W<")O\WZ#X!@\0,Z6G_Q%[\)HM'!"8+1P)T`^E9
+M_R:+1P@F"T<&=`/I3/\FBT<0)@M'#G0#Z3__Q%X$)O9')A]U%8M&_`6F`%)0
+MZ"T`@\0$"\!T`^D@_\1>!":*3R2`X0>P`=+@]M`FBU\DP>L#Q#;`.28@`"O`
+M7LG#R`(``%=6Q%X$)HMW"":+?P@[_G0*B_<FBW\(._YU]B:+1P:)1OXY=OYR
+M"8M&_BO&7E_)PR:+1P0KQ@-&_EY?R<.0R`(``%=6Q%X$)HMW!B:+?P8[_G0*
+MB_<FBW\&._YU]B:+1PB)1OXY=OYW"8O&*T;^7E_)PR:+1P0K1OX#QEY?R<.0
+M58OL5HMV"(/^`74@BT8$BU8&!;(`4E#HI/^#Q`3$7@0F*X>V`/?82%[)PY"+
+M1@2+5@8%I@!24.A"_X/$!,1>!"8KAZH`Z]Z0R!0``%=6@WX$_W4&@WX&_W03
+MBT8$BU8&*M(]__]U'X'Z`/]U&6C&!/\V+@#_-J@`Z)$.@\0&N/__7E_)PY"+
+M5@8JP"K2/0#_=48[T'5"*_;K`48Y-A`&?A:*1@0JY&O>*@,>$@:.!A0&)CE'
+M)G7CBD8$*N1KWBH#'A(&C@84!B8Y1R9T!6C/!.N>:\8JZUV0BT8$BU8&BL2*
+MUBKV*N2)1OZ*1@2)1O"#?OX`?`R+1OZ+'C`Y.4<6?P=HUP3I:?^0B]C1XXNW
+M1`Z#?O``?!"+1O`#QHM>_M'C.8=&#G\&:-P$Z43_BT;P`\9KP"H#!A(&BQ84
+M!HE&\HE6],1>\B:+1P0FBU<&B4;ZB5;\@7X(#V%T`^G]`H-^"@!T`^GT`HM&
+M#@M&#'42BT8."T8,=`/I!?]HY@3I\?Z0BT8,BU8.B4;VB5;X)O9')A]T`^D*
+M`2;V1Q`"=0W$7O8F]D<%`74#Z?8`:@#_=OS_=OKH2_Z#Q`:)1NP+P'4+Q%[R
+M)H!/$`+IU@#$7OHFBX>J`":+CZP`*\$[1NQW#B:+AZP`)HN_J@`K^.L#BW[L
+M5\1>\B:+1PPFBU<.Q%[Z)HN/K``#P5)0_W8&_W8$BQ[V.?]7#H/$"HOP"_9]
+M`BOV"_9^,3O^=2V+1NPKQE#$7O(F_W<.)O]W#/]V!O]V!(L>]CG_5PZ#Q`J+
+M^`O_?0(K_P/WZP(K_SEV['XA:@#_=@;_=@2+'O8Y_U<2@\0&"\!U"\1>\B:`
+M9Q#]ZPF0Q%[R)H!/$`(+]GX8Q%[Z)HN'K``#\"8YMZH`=P*+]R:)MZP`@SY4
+M``!T`^F@`,1>]B;W1P0&`74#Z9(`)O9'!`9T",1>\B:`9Q#[Q%[V)H!G!/O$
+M7O8F]D<$`G05BT;ZBU;\!;(`4E#HU_R#Q`0+P'\*Q%[R)O9'$$!T",1>]B:`
+M9P3]Q%[R)O9'$!!T",1>]B:`9P3]Q%[R)O9'$(!T",1>]B:`9P7^Q%[V)HM'
+M`B8+!W4:)HM'"B8+1PAU$":+1P8F"T<$=08KP%Y?R</$7OHF@+^2``!U`^FJ
+M`,1>\B;V1Q`!=1%J$(O#C,(%%`!24.BU$8/$!L1>]B:+!R:+5P+$7O(F"4<4
+M)@E7%L1>]B;V1P0>=`C$7O(F@&<8X<1>]B;V1P4>=`C$7O(F@&<9X<1>]B:+
+M1P0FBU<&Q%[R)@E'&"8)5QK$7O8FBT<()HM7"L1>\B8)1QPF"5<>Q%[V)HM'
+M"B8+1PAT$R:+1PPFBU<.Q%[R)HE'(":)5R+$7O(F@$\0`>L@:A"+PXS"!90`
+M4E#_=@[_=@SHVQ"#Q`K$7OHFQH>2``''!E(``0#$7O(FBD\D@.$'L`'2X":+
+M7R3!ZP,#'L`YC@;".28(!\1>\B:*1RC$'E@.)@A'8.F!`(%^"`!G=2.#?@H`
+M=1V#?@X`?`/_1@R+1@R+5@[$7OHFB4<.)HE7$.M8D(%^"`%G=2.#?@H`=1V#
+M?@X`?`/_1@R+1@R+5@[$7OHFB4<")HE7!.LND(%^"`)G=`/IQON#?@H`=`/I
+MO?N#?@X`?`/_1@R+1@R+5@[$7OHFB4<&)HE7",<&4@`!`,1>\B:*3R2`X0>P
+M`=+@)HM?),'K`P,>P#F.!L(Y)@@'Q%[R)HI'*,0>6`XF"$=@@SY0``!U`^DJ
+M_H,^4@``=0/I(/YJ`/\V4`#HH`^#Q`3'!E(```#I"_Z058OLBT8$+0!A?`E(
+M2'\%N`$`R<,KP,G#R`@``%:+=@CHQA*)1OC$7@0FBT<$)HM7!HE&^HE6_/?&
+M`0!T*L1>^B:+AZP`)HF'K@#$7OHFBX>N`(E&_HM&_B8YAZP`==[$7@0F@&<0
+M_??&`@!T2\1>!";V1Q`0=1?$7OHFBX>Z`"8YA[@`=`C$7@0F@$\1`<1>^B:+
+MA[@`)HF'N@#$7OHFBX>X`(E&_HM&_B8YA[H`==[$7@0F@&<0F_]V^.@Z$H/$
+M`BO`7LG#R!```%=6Z!H2B_BA$@:+%A0&B4;\B5;^B\(+1OQU!RO`7E_)PY!J
+M#(U.\(L6!`!24>CQ#H/$!L=&]/__QT;V__\K]NM<D,1>_";V1R8@="Z+'O8Y
+M@W\*`'0DC4;PB\B+%@0`4E%J`&@)88M>_";_=P(F_S>+'O8Y_U<*@\0,BQ[V
+M.8-_"`!T%,1>_";_=P(F_S>+'O8Y_U<(@\0$1H-&_"HY-A`&?Y]7Z(<1@\0"
+MZ6[_D,@*``!6QP8P.<PYQT;\``+'1OX``,1>_":!/Z?R=`/I)P$F@7\"2:%T
+M`^D<`2:!?P128W0#Z1$!)H%_!B'Q=`/I!@$FBT<4H\PYZT['!MXY``#'!N`Y
+M`0#'!MHY`$#'!MPY``#'!M`Y#@#K4L<&WCD``,<&X#D!`,<&VCD``,<&W#D!
+M`,<&T#D0`.LRQP;>.0``QP;@.0(`Z]Y(/0H`=Q[1X),N_Z?R'+8<EARV'+8<
+MEAR6'-8<"!VV'+8<MAS'!LXY`0#$7OPF@W\8`'4&)L='&%``:@%HH(;$7OQJ
+M`";_=QCH'TRCTCF)%M0Y:@!J!%)0Z%]*H]8YB1;8.<1>_":#?QH`=!`FBT<:
+MB4;VQP;T.1``ZPZ0)HM'#HE&]L<&]#D``"OVZP*01H/^$'T+B\Z+1O;3X`O`
+M=>\!-O0YQP;B.0$`H?0YH^0YQT;X``+'1OH``,1>^":!/T5#=`/I_@`F@7\"
+M4"%T`^GS`,<&S#D7`(,^5@X`=!V#/E8.&'4)QP;,.1@`ZPZ0@SY6#AEU!L<&
+MS#D9`*',.2T8`'05QP;:.0`0QP;<.0``QP;0.0P`ZQ.0QP;:.0``QP;<.0$`
+MQP;0.1``QP;>.0``QP;@.0(`Q%[X)HM'!J/..<<&TCE`>,<&U#E]`6H`:@3_
+M-M0Y_S;2.>A?2:/6.8D6V#G'!O0Y``#'!N(Y```K]NL6@P;T.0B+'N(YT>/'
+MA^0Y"`#_!N(Y1H/^"'TWQ%[X@\,()HH`*N2)1O8]_P!T)(I&]B4'`#O&=1KV
+M1O8@=,*#!O0Y$(L>XCG1X\>'Y#D0`$;KORO`7LG#D%6+[/]V"/]V!HM>!-'C
+M_[?:`/\VX@#HW06+Y8U&#%#_=@IJ`&H`Z`D&B^5HV`#HAP4KP,G#D&@:`>BZ
+M!8/$`O\V_`-H;0'HK06#Q`3_-OH#_S;,.6B"`>B<!8/$!O\V^CG_-O@Y_S8.
+M`/\V#`!HSP'H@P6#Q`K_-KP-_S:Z#?\VY#C_-N(X:!P"Z&H%@\0*_S8>`/\V
+M'`#_-AH`_S88`&AI`NA1!8/$"O\V!`#_-@8`:+8"Z$`%@\0&_S8(`/\V"@!H
+M!0/H+P6#Q`;_-O0Y_S;B.6A5`^@>!8/$!FBF`^@5!8/$`L.0Z+/\Z%P.Z#D+
+MZ%3CZ`\`Z+SHZ#O_Z*8-Z7L*D,.0R`0``*$4`(L6%@`K!A``&Q82`*/B.(D6
+MY#BA#`"+%@X`*P;B.!L6Y#BC^#F)%OHYH1``BQ82`-'JT=C1ZM'8T>K1V-'J
+MT=B*#A``@^$/B4[\B4;^H>(XBQ;D.,1>_":)!R:)5P+$7OPKP":)1P8FB4<$
+MQ%[\)HE'"B:)1PB+1OR+5OZC+#F)%BXYH\0YB1;&.2O`R<.0R`0``*$L.8L6
+M+CGK"9`FBT<$)HM7!HE&_(E6_HO""T;\=!:+1@2+5@;$7OPF.5<"<MQW!28Y
+M!W+5BT;\BU;^R<.0R`0``(M&!@M&!'17H2PYBQ8N.<1>!":)1P0FB5<&Q%X$
+M*\`FB4<*)HE'"*$N.0L&+#EU$(M&!(M6!J/$.8D6QCGK$Y"+1@2+5@;$'BPY
+M)HE'"":)5PJ+1@2+5@:C+#F)%BXY*\#)PY!5B^R+1@8+1@1T9\1>!":+1PHF
+M"T<(=1(FBT<$)HM7!J,L.8D6+CGK%9`FBT<$)HM7!B;$7P@FB4<$)HE7!L1>
+M!":+1P8F"T<$=1,FBT<()HM7"J/$.8D6QCDKP,G#)HM'"":+5PHFQ%\$)HE'
+M"":)5PHKP,G#R!0``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'JT=C1ZM'8T>K1
+MV`-&!(/2`(E&^(E6^L1>!":+!R:+5P(#1O@35OJ)1NR)5NZA+#F+%BXYZTB+
+M1O2+5O8Y1OAU-3E6^G4PQ%X$)HL')HM7`L1>_"8!!R815P+_=O[_=OSH#?^#
+MQ`2+1OR+5OZ)1@2)5@;IB`"0)HM'!":+5P:)1OR)5OZ+P@M&_'1RBT;\@.0/
+MBL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`T;\@](`B4;PB5;RQ%[\)HL')HM7
+M`@-&\!-6\HE&](E6]HM&\(M6\CE&['0#Z5W_.5;N=`/I5?\&4^B*_H/$!,1>
+M_":+!R:+5P+$7@0F`0<F$5<"BT;^"T;\=`/IXOZ+1@2+5@;)P\@2``"+1@0%
+M!0`D_HE&!"T,`!O)]]$CP04,`(E&],=&]@``Z`,+B4;R_W;V_W;TZ(3]@\0$
+MB4;\B5;^B\(+1OQU#O]V\NCP"H/$`BO`F<G#4O]V_.@)_H/$!(M&_(M6_HE&
+M[HE6\,1>_":+!R:+5P(K1O0;5O:)1OB)5OH+TG4%/0P`<F6+PXS"@.0/BL2*
+MXHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`\.#T@`#1O035O;1ZM'8T>K1V-'JT=C1
+MZM'8BD[\`D[T@^$/B4[\B4;^BT;XBU;ZQ%[\)HD')HE7`O]V_O]V_.@1_8/$
+M!/]V\NA("HM&](M6]L1>[B:)!R:)5P*#1NX$BT;TBU;V`0;X.1$6^CDI!N(X
+M&1;D.(M&[HM6\,G#R`X``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'JT=C1ZM'8
+MT>K1V`-&!(/2`#D6$@!W%G(&.080`'<..186`'<-<@8Y!A0`<P6X___)PX-N
+M!`2+1@2+5@:)1O*)5O3$7O(FBP<FBU<"B4;\B5;^Z)@)B4;V_W;T_W;RZ#G]
+M@\0$B4;RB\(+1O)T"E+_=O+H1_R#Q`3_=O;H?@F+1OR+5OXI!O@Y&1;Z.0$&
+MXC@1%N0X*\#)PU6+[(,^##H`=!"#/LHY`'0)_W8$:@#_%LHY*\#)PU6+[(,^
+M##H`=!&#/LHY`'0*_W8&_W8$_Q;*.2O`R<.058OLC48&4/]V!&H`:@#H*P#)
+MPY!5B^R-1@A0_W8&_W8$:@#H%@#)PU6+[(U&"%#_=@9J`/]V!.@"`,G#R`X`
+M`%=6BT8*B4;RBUX(_T8(B@>8B_B#_R5T,@O_=1(Y?@9T!HM>!L8'`"O`7E_)
+MPY"#?@8`=`J+7@:(!_]&!NO*5_]V!.A;_X/$!.N^BUX(@#\E=1*#?@8`=`B+
+M7@;&!R7KV6HEZ]N`/RUU"_]&",=&]@$`ZP:0QT;V``"+7@B`/S!U"_]&",=&
+M_`$`ZP:0QT;\``"+7@B`/RMU"_]&",=&^@$`ZP:0QT;Z``"+7@B`/R!U"_]&
+M",=&]`$`ZP:0QT;T``"+7@B`/R-U`_]&""OVZQ6#_SE_((O&P>`"`\;1X`/'
+M+3``B_"+7@C_1@B*!YB+^(/_,'W;@_\N=16+7@C_1@B*!YB+^(/_,'P%@_\Y
+M?NL]9`!U`^G[`'X#Z0P"/54`=0/I(P%^`^GG`2U$`'4#Z18!+0L`=0/I#@'I
+M'`*#?O8`=2S'1O@!`.L/D&H@_W8$Z$[^@\0$_T;X.7;X?1&#?@8`=.>+7@;&
+M!R#_1@;KYX-^!@!T'HM>\HH'BUX&B`?_1@:#?O8`=0/IS@''1O@!`.LCD(M>
+M\HH'*N10_W8$Z`#^@\0$Z]R0:B#_=@3H\OV#Q`3_1O@Y=OA\`^F<`8-^!@!T
+MY(M>!L8'(/]&!NODD&H`:@#_=O;_=OQ6:@*+7O)J`/\WC48&4/]V!.AT`8/$
+M%.EF`6H`:@#_=O;_=OQ6:A#KVI!J`&H`_W;V_W;\5FH(Z\J0_W;T_W;Z_W;V
+M_W;\5FH*BU[RBP>94E#KMY!J`&H`_W;V_W;\5FH*ZZ"0BUX(_T8(B@>8B_B#
+M_T%\"(/_6G\#@\<@B\<]:0!T5W]M+6(`=`9(2'1,ZR)J`&H`_W;V_W;\5FH"
+MBU[R_W<"_S>-1@90_W8$Z-D`@\04@T;R`NG'`)!J`&H`_W;V_W;\5FH0Z]20
+M:@!J`/]V]O]V_%9J".O$D/]V]/]V^NL$:@!J`/]V]O]V_%9J"NNLD"UO`'33
+M+08`=.8M`P!TN>NOD(M>\HL'B4;^BU[^_T;^B@>8B_@+_W1E@WX&`'0+BUX&
+MB`?_1@;KX9!7_W8$Z)7\@\0$Z]0M6`!U`^DO_RT*`'4#Z:[^2'4#Z1;^ZS`M
+M:0`]#P!W*-'@DR[_IQPH]B8\*#PH("<\*#PHYB8\*#PH/"C`)SPH$"<\*#PH
+MUB:#1O("Z:G\D,@F``!75HM&"(M6"HE&_(E6_@O2?0KW7OR#5OX`]U[^*_:+
+M1@R94E#_=O[_=OSHS3^+V`,>$`2*!XA"W$:+1@R94E"-1OQ0Z,X^@W[^`'_2
+M?`:#?OP`=<J#?@H`?0^#?A0`=0:#?A8`=`/_3@Z#?A(`=3&#?A``=2N+_NL-
+MD&H@_W8$Z+K[@\0$1SE^#GX5BUX&@S\`=.>+'\8'((M>!O\'Z^:0@WX6`'4&
+M@WX4`'08@WX*`'T2BUX&@S\`="J+'\8'+8M>!O\'@WX4`'0S@WX*`'PMBUX&
+M@S\`=!J+'\8'*XM>!O\'ZQEJ+?]V!.A0^X/$!.O3D&HK_W8$Z$+[@\0$@WX6
+M`'0>@WX4`'48@WX*`'P2BUX&@S\`=!N+'\8'((M>!O\'@WX2`'4_@WX0`'0Y
+MB_[K&Y!J(/]V!.@"^X/$!.OBD&HP_W8$Z/3Z@\0$1SE^#GX5BUX&@S\`=.>+
+M'\8',(M>!O\'Z^:0C43_B4;:ZQB+7MJ-1MP#V(H'F%#_=@3HO/J#Q`3_3MJ#
+M?MH`?""+7@:#/P!TVHM>VHU&W`/8B@>+7@:+'X@'BUX&_P?KUX-^$@!T*HO^
+MZPQJ(/]V!.A^^H/$!$<Y?@Y^%8M>!H,_`'3GBQ_&!R"+7@;_!^OFD"O`7E_)
+MPX,&-#D!@Q8V.0#K]`````````````#ZNB+_N`"`[[HH_[!M[NL`NF;_,\#O
+MZP`SP([8CM".P.H``/__B]R+5P**1P3NPXO<BU<",\#LPXO<BU<"BT<$[\.+
+MW(M7`NW#58OL5E<>!L1^",5V!(M.#(O&,\>I`0!U$_?&`0!T!@O)=`ND2='I
+M\Z6#T0#SI`<?7UY=PU6+[%<&Q'X$BTX(,\#1Z?.K<P&J!U]=PP#(`@``:@!J
+M9/\VV#G_-M8YZ/\]B4;^:`'@:&;_Z(/_@\0$_W;^:&+_Z'?_@\0$:@!J!&B.
+M:6H3Z)0#@\0(T6[^_W;^:&#_Z%G_@\0$ZPJ#!B0Y`8,6)CD`:&#_Z$[_@\0"
+M.T;^=^CK"H,&)#D!@Q8F.0!H8/_H-/^#Q`([1OYRZ&H(:"0YZ"`\*\#)PP``
+M````````````````'@93+HX><`&Z*O_M4(N'Q`WO^_\&R#G_ESHY^KHB_[@`
+M@.]8NBK_[X/$`@<?8<]@NP``Z\M@NP(`Z\5@NP0`Z[]@NP8`Z[E@NP@`Z[-@
+MNPH`ZZU@NPP`ZZ=@NPX`ZZ%@NQ``ZYM@NQ(`ZY5@NQ0`ZX]@NQ8`ZXE@NQ@`
+MZX-@NQH`Z7S_8+L<`.EU_V"['@#I;O]@NR``Z6?_8+LB`.E@_V"[)`#I6?]@
+MNR8`Z5+_8+LH`.E+_V"[*@#I1/]@NRP`Z3W_8+LN`.DV_V"[,`#I+_]@NS(`
+MZ2C_8+LT`.DA_V"[-@#I&O]@NS@`Z1/_8+LZ`.D,_V"[/`#I!?]@NSX`Z?[^
+M8+M``.GW_F"[0@#I\/Y@NT0`Z>G^8+M&`.GB_F"[2`#IV_Y@NTH`Z=3^8+M,
+M`.G-_F"[3@#IQOY@NU``Z;_^8+M2`.FX_F"[5`#IL?Y@NU8`Z:K^8+M8`.FC
+M_F"[6@#IG/Y@NUP`Z97^8+M>`.F._F"[8`#IA_Y@NV(`Z8#^8+MD`.EY_F"[
+M9@#I<OY@NV@`Z6O^8+MJ`.ED_F"[;`#I7?Y@NVX`Z5;^8+MP`.E/_F"[<@#I
+M2/Y@NW0`Z4'^8+MV`.DZ_F"[>`#I,_Y@NWH`Z2S^8+M\`.DE_F"[?@#I'O[Z
+MNBK_[8O8N`<`[_N+P\/ZNBK_[8O8N`8`[_N+P\/ZNBK_[8O8N`4`[_N+P\/Z
+MNBK_[8O8N`0`[_N+P\/ZNBK_[8O8N`,`[_N+P\/ZNBK_[8O8N`(`[_N+P\/Z
+MNBK_[8O8N`$`[_N+P\/ZNBK_[8O8N```[_N+P\/ZB]R+1P*Z*O_O^\.Z*O_M
+M]]`E!P##^L/[PYSZ6,.+W/]W`IW#S,,`PY##D,@(``!6:`"`:"+_Z'3\@\0$
+M:`"`:"+_Z&C\@\0$:/T`:"C_Z%S\@\0$:@!H*O_H4?R#Q`1J"&@X_^A&_(/$
+M!&H(:#K_Z#O\@\0$:@AH//_H,/R#Q`1J"&@^_^@E_(/$!"OVB][1X\>'.CGT
+M+8O>T>/'A\0-``!&@_Y`?.8KP(E&^HE&^"OVCD;ZB][1XXN'(`2+#@8`BU[X
+M)HD')HE/`D:#1O@$@_Y`?.`KP%[)PU6+[(-^!`!\$H-^!$!]#(-^"`!\!H-^
+M"`=^!;C__\G#N`@`*T8(B48(/0<`?@7'1@@'`(M&!HM>!-'CB8<Z.8M&"$B+
+M7@31XXF'Q`V+1@3K;Y#_=@AH,O_H>?N+Y>F(`(-^"@'U&\`E$``+1@A0:#C_
+MZ^60@WX*`?4;P"40``M&"%!H.O_KT9"#?@H!]1O`)1``"T8(4&@\_^N]D(-^
+M"@'U&\`E$``+1@A0:#[_ZZF0_W8(:#3_ZZ#_=@AH-O_KF"T(`#T+`'<@T>"3
+M+O^G="^,+XPO5"]<+P0O&"\L+T`OC"^,+XPO]BXKP,G#R`0``%:+1@2+5@:*
+MQ(K6*O8JY(OPT>:+'KHYBT`6B4;^BD8$*N2+\-'FBU[^BT`TB4;\B]CV1W>`
+M=`7'1OP``(M&_%[)PU6+[%=6BW8(@WX$_W4&@WX&_W03BT8$BU8&*M(]__]U
+M#8'Z`/]U!RO`7E_)PY#_=@90Z(7_@\0$B_@+_W4(N/__7E_)PY`+]G3TBP2)
+M173KUI!5B^Q75H-^!/]U$8-^!O]U"X`.O@T!*\!>7\G#_W8&_W8$Z$/_@\0$
+MB_`+]G4(N/__7E_)PY#V1'8!==K'!(`EQT0"``#'1`2`)<=$!@``QD0R`,9$
+M,`C&1#$`QT0(@!#'1`H``"O`B40.B40,B402B400B40:B408QT0@"@#'1"((
+M`,9$,Q/&1#01QD0U$\9$-A&A.#F)1"2A(#F)1":A,CF)1"BA(CF)1"JAW#B)
+M1"PK_XO?`][&1SD`1X/_"WSR*\")1%*)1%")1%:)1%2)1%J)1%B)1&*)1&")
+M1&:)1&2)1&J)1&B)A(X`BT04BU06)']24&H`:@!6Z&$/@\0*5NC`*X/$`E;H
+M-RR#Q`)6Z$XF@\0"@$QV`<1<<":#!P$F@U<"`.GN_E6+[%=6@WX$_W45@WX&
+M_W4/*\"CP`VCO@TKP%Y?R<.0_W8&_W8$Z!?^@\0$B_`+]G4(N/__7E_)PY#V
+M1'8!=-E6Z)0K@\0":@-6Z"LC@\0$5N@>+(/$`E;H;RR#Q`+H,_R+^,>$I@``
+M`,>$J````,>$G@```,>$H````,:$E@``QH27``#'1'8``,=$?@``5^@%_(/$
+M`NN!N/__P\@0``!75H-^!/]U(H-^!O]U'/]V#O]V#/]V"O]V"/]V!O]V!.C5
+M_X/$#%Y?R</_=@;_=@3H;?V#Q`2+\`OV=0BX__]>7\G#D,=&_@``BT8(Z2$#
+MD/]V#O]V#%;H/B:#Q`:)1O[I10.0_W8,5NAM(H/$!.DW`Y#_=@Q6Z)\HZ_"0
+MBT8,BU8.B4;XB5;Z5L1^^+DB`/.E7ND3`Y"+1@R+5@Z)1OB)5OK$7O@F]D<$
+M$'0AZ$7[B4;\@&1W]_]V_.@\^X/$`E;H.2J#Q`)6Z(PI@\0"Q%[X)O9'!"!T
+M!U;HURF#Q`+$7O@F]@<@=`EJ`%;H.16#Q`3$7O@F]@<0=`EJ`5;H)Q6#Q`3$
+M7O@F]D<$0'4#Z9D":@&+PXS"!0D`4E#_=@;_=@3H!0>#Q`KI?P*0*_^!?@@%
+M874%.7X*=`R!?@@'874(.7X*=0._`0"!?@@%874&@WX*`'0-@7X(!F%U"8-^
+M"@!U`X//`E=6Z&\A@\0$BT8,BU8.B4;XB5;ZBT08BU0:B4;TB5;VBT04BU06
+MB4;PB5;RBT;XBU;Z5AZ+_HOP'@>.VKDB`/.E'U[_=O+_=O#_=O;_=O16Z-\,
+MZ6G__W8._W8,5NA,)8/$!NG>`5;HRB.#Q`)6Z!DF@\0"BT8,BU8.B4;PB5;R
+M5HUT1,1^\+D&`/.E7HM>\";'1P0_`";'1P8``(!D17_IH0&0*_^!?@@*874%
+M.7X*=`R!?@@,874(.7X*=0._`0"!?@@*874&@WX*`'0-@7X("V%U"8-^"@!U
+M`X//`E=6Z)$@@\0$BT8,BU8.B4;PB5;RQ%[P)O9'!`1T&B:*1P@E!``]`0#U
+M&\`E!``,`5!6Z+LA@\0$Q%[P)O9'!`%U`^DA`2:*1P@E`0`]`0#U&\`E!``,
+M`5!6Z&0BZ<K]D(M&#(M6#HE&\(E6\E:-=%#$?O"Y"`#IU/V0BT8,BU8.B4;P
+MB5;R5AZ-?%"+\!X'CMJY"`#SI1]>_W06_W04_W0:_W08Z<K^D(M&#(M6#HE&
+M\(E6\HM$<(M4<E8>B_".VL1^\+DZ`/.E'UX&_W;P5NB@$^FM_I"+1'(+1'!U
+M`^F%`,1<<":+1W")1OQJ=/]T<O]T<.B5]8/$!HM&_)G$7'`FB4=P)HE7<O\V
+MK@2-A*(`4.AB*8/$!)G$7'`FB4<$)HE7!NM!D,=&_O__ZSF0+0%A/1,`=_#1
+MX),N_Z=P-50R8C)L,EPS&C,:,QHSNC,Z-/@S^#/X,Y(TK#18-88RVC2J,T`R
+M"#6+1OY>7\G#D,@$``!75HMV",=&_```_W8&_W8$Z-GY@\0$B_@+_W4(N/__
+M7E_)PY#H&?B)1OX+]G4*_S:L!(V%F@#K#8/^`741_S:N!(V%H@!0Z+LH@\0$
+MZQ.#_@)U!:&L!.L(@_X#=0>AK@1(B4;\_W;^Z-KW@\0"BT;\7E_)P\@&``!6
+MBW8$Z,'WB4;^@&1V?_]V_NBX]X/$`H-\=`!T"8M<=(-_#`!U!BO`7LG#D(M$
+M4(M44B%$8"%48HM$5(M45B%$9"%49HM$6(M46B%$:"%4:HM$8@M$8'40BT1F
+M"T1D=0B+1&H+1&ATOHU$8!Y0:@!H#V'_='S_='J+7'3_5PR#Q`PKP(E$8HE$
+M8(E$9HE$9(E$:HE$:.N/D,@B``!6Q%X,)HLWBUX$BX>B`(N7I`")1OR)5OZA
+MK@2)1N*+1PR+5PZ)1NB)5NJ*AYD`*N2)1N3'1O8``,=&[@``]D;I@'01]D;J
+M`74+B&;RQT;L!0#K&)#V1NF`=0;V1NH!=`;&1O)_Z^;'1NP#`,=&[@``ZPG_
+M3A#_1N3_1NZ+1@HY1NY\`^FN`L1>!O]&!B:*!XA&WCP@?#(\?G\N@WX0`'4#
+MZ9$"]D;H`70,/&%\"#QZ?P2`;MX@BD;>Q%[\)H@`1CEVXG>N*_;KJHM&Y(E&
+MYL=&X```QT;T``"+1NR)1OB*1O*(1OJ*1MZ8/0H`='=^`^F8`0O`=0/I?P$M
+M"`!T!DAT?^L:D/9&Z$!T"L=&X`H`QT;T`0"#?N0`?@/_3N3'1O```/9&^`%T
+M`_]&\/9&^`)T!(-&\`/V1O@$=`^`?OH`=0/19O2+1O0!1O"+1O`Y1A!\`^E1
+M`8M&YHE&Y,=&$```Z=$!D/9&Z!)T`^FK`/9&Z"!TJL=&X!0`QT;T`@#KGI#V
+M1ND"="**1N0E!P`M"`#WV-'XB4;@"\!^!K@"`.L#D"O`B4;TZS"0]D;I!'0,
+MQT;@%`#'1O0"`.L=]D;I"'07QT;X!`"*1N0E!P`M"`#WV(E&],9&^B"+1N0,
+M!T")1N3I.O^0]D;H@'4#Z3#_QT;@_P#'1O3__^DC__9&Z0'KZ/9&Z`1T`^ED
+M__9&Z`AT"8-^Y`!U`^E@_O9&Z1!T*HM&Y,'@`@4*`#T4`'T#N!0`B4;@BT;D
+M!3(`P>`%/0(`?0.X`@")1O3K(O9&Z2!T#,=&X!0`QT;T`@#K$/9&Z4!T"L=&
+MX!X`QT;T!`#'1N0``.FK_O]&Y.FE_L9&^@#'1O0!`,=&^`0`Z93^D"T+`'4#
+MZ5+_2'4#Z6+_2'4#Z6+_+0X`=,_I=_[V1O@!=!&*1M[$7OPFB`!&.7;B=P(K
+M]H-^X`!^-?9&^`)T+\1>_";&``!&.7;B=P(K]L1>_";&`()&.7;B=P(K]HI&
+MX,1>_":(`$8Y=N)W`BOV@W[T`'XL]D;X!'0FBT;T2`%&]NL$D/].](-^]`!^
+M$XI&^L1>_":(`$8Y=N)WZ"OVZ^2+1O`I1A#I1?V0BT;V`T;NF8M>!,1?<"8!
+M1P0F$5<&BD;DBUX$B(>9`,1>#":)-XM&[E[)PY#("```5U;_=@;_=@3H:?6#
+MQ`2+\`OV=0BX__]>7\G#D/9$=@%T\NBC\XE&_(N$I@")1OZ+A*@`B4;Z_W;\
+MZ)#S@\0"BT;^.4;Z=PN+?OHK^`,^K@3K!8M^^BOX3SE&^G4$@&1^@*&N!"O'
+M0#M$)G,$@&1^]XM$#(M4#B3Y"]!T'%>-1OX64/]V#/]V"O]V"%;H"?R#Q`Z)
+M1@SIN0"+1@P[QWX"B\>)1@P+P'\#Z:8`H:X$*T;^.T8,?@.+1@R)1O@+P'41
+M:#8%_S:J!/\VJ`#HNNF#Q`:AK@0Y1OAR$6@W!?\VJ@3_-J@`Z*'I@\0&_W;X
+MBX2B`(N4I``#1OY24/]V"O]V".AJ[X/$"HM&##E&^'T=*T;X4/^TI`#_M*(`
+MBT8(BU8*`T;X4E#H1>^#Q`J#?@P`?B"+1@R9Q%QP)@%'!"815P:+1@P!1OZA
+MK@0Y1OYR`RE&_J&N!#E&_G(1:$H%_S:J!/\VJ`#H(>F#Q`;H6_*)1OR+1OZ)
+MA*8`BX2H`(E&^HM&_CE&^G<(B_@K?OKK"I"+^"M^^@,^K@0Y?"1S!(!D?OM6
+MZ(8@@\0"_W;\Z!_R@\0"BT8,7E_)PY#("```5U;$7@PFBP>)1OJ+7@2+AYH`
+MBY><`(E&_(E6_HM&$"M&"AO)(\$#1@J)1A`K]NLYQ%[\BW[Z)HH!B$;X_T;Z
+MH:P$.4;Z<@7'1OH``(!^^$%\"H!^^%I_!(!&^""*1OC$7@;_1@8FB`=&.780
+M?\*+1OK$7@PFB0>+1A!>7\G#R`H``%=6_W8&_W8$Z"OS@\0$B_`+]G4(N/__
+M7E_)PY#V1'8!=/+H9?&)1OR+A)X`B4;^BX2@`(E&^O]V_.A2\8/$`HM&_CE&
+M^G<'B_@K?OKK"8L^K`0K?OH#^(E^^#E\*',$@&1_^_9$"`AT'5>-1OH64/]V
+M#/]V"O]V"%;H_OZ#Q`Z)1@SIM@"0BT8,.\=^`HO'B48,"\!_`^FB`*&L!"M&
+M^CM&#'X#BT8,B4;V"\!U$6C;!?\VJ@3_-J@`Z([G@\0&BT;V.0:L!'<1:-P%
+M_S:J!/\VJ`#H=.>#Q`;_=O;_=@K_=@B+A)H`BY2<``-&^E)0Z#WM@\0*BT;V
+M.48,?B"+1@PK1O90BT8(BU8*`T;V4E#_M)P`_[2:`.@5[8/$"H-^#`!^&"M^
+M#(M&#`%&^HM&^CD&K`1W!J&L!"E&^HM&^CD&K`1W$6CQ!?\VJ@3_-J@`Z/CF
+M@\0&Z#+PB4;\BT;ZB82@`(N$G@")1OXY1OIW"(OX*W[ZZPJ0BSZL!"M^^@/X
+M.4;Z=0J`9';W@&1_@.L,BT;X.48,=02`9'_^.7PJ=@2`9'_WQX2.````.7PH
+M<@=6Z(X)@\0"_W;\Z-/O@\0"BT8,"\!]`BO`F<1<<"8!1P@F$5<*BT8,7E_)
+MP\@*``!75HMV"/?&`0!T%XM>!,1?<":#1R0!)H-7)@"+7@2`3W@!]\8$`'07
+MBUX$Q%]P)H-'+`$F@U<N`(M>!(!/>`+WQ@(`=!>+7@3$7W`F@T<H`2:#5RH`
+MBUX$@$]X!/?&"`!T%XM>!,1?<":#1V0!)H-79@"+7@2`3W@(]\9P`'1TB\8D
+M<#P@=1"+7@3$7W`F@T=$`2:#5T8`B\8D<#P0=3^+7@3$7W`F@T=``2:#5T(`
+MBUX$]D=W"'0F@&=W]XM>!(!/=@)J!O]V!.A])(/$!"3]#`10:@;_=@3HA22#
+MQ`:+QB1P/'!U*(M>!/9'"@AT'X!/>!"+7@2*AY(`*N3WT"/PBH>3`"KDA<9T
+M&NL2D)"+7@3$7W`F@T<(`2:#5PH`*\!>7\G#BX>:`(N7G`")1OR)5OZ+OYX`
+MBX>@`(E&^CO'=PNAK`0KQP-&^NL#D"O'B4;X/0(`?2#$7W`F@T<P`2:#5S(`
+MBUX$Q%]P)H-'-`$F@U<V`.NID(J'E``JY(7&=0/ICP"#?O@%?,WVAY4``70B
+MQ%[\)L8!_T<Y/JP$=P(K_\1>_";&`0!'.3ZL!'=]*__K>8I'.\1>_":(`4<Y
+M/JP$=P(K_\1>_";&`0%'.3ZL!'<"*__$7OPFQ@$!]\8!`'0'Q%[\)H`)`O?&
+M!`!T!\1>_":`"03WQ@(`=`?$7OPF@`D(]\8(`'2?Q%[\)H`)$.N6D/?&#P!T
+M$_?&"`!T!HI'..L$D(I'-YB)1@:*1@;$7OPFB`%'.3ZL!'<"*_^+7@2)OYX`
+MBUX$@$]V".G0_L@P``!75HMV!(O^@[R```AR`^E6!HN<@`#1XXN'NCF)1N`+
+MP'4#Z4(&BYR"`-'C`]B+1Q:)1OX+P'4#Z2T&*L"(1MZ(1N*(1NJ(1NZ(1O"(
+M1M2(1M:(1OJ(1OR(1N:(1MCV10D0=`2`3M@']D4(@'0$@$[8",9&]@#V10D@
+M=`2`3O8']D4)`70$@$[V"/9%"@AT!X!.V'#K"Y#V10H"=`B`3O9P@$[J@,9&
+M\@#V10E`=`2`3O(']D4)`G0$@$[R"(-])`!T"*&N!#E%)'(&H3@YB44D@WTF
+M`'0(H:X$.44F<@:A(#F)12:#?2@`=`BAK`0Y12AV!J$R.8E%*(-]*@!T"*&L
+M!#E%*G(&H2(YB44J@WTL`'0(H:P$.44L<@:AW#B)12SV110!=`2`31@0]D44
+M`G0$@$T8(/9%%!!T!(!.[A#V110@=`2`3NX$BD4R*N1(=`Q(="E(="Q(="_K
+M!9"`3O#`@'TQ`G4H@$[P"(!],`5T.H!],`9U)(!.\`'K+H!.\$#KWH!.\*#K
+MV(!.\"#KTH!],0%UUH!.\`3KT(!],`=U!H!.\`+K!(!.\`/V110(=`:`91A?
+MZPKV11@@=`2`3NX"]D44!'0$@&48K_9%&`%T$H!.[D"`3NH0]D48!'0$@$[N
+M@/9%%$!U(O9$6`)T'/9%%`AT"H!._$"`3OI`ZPB`3OR`@$[Z@(!.UH#V110(
+M=1+V1%@(=`R`3OQ`@$[Z0(!.UH"+7N"#?P(4=0:#?PH&=0F+7O[V1P@(=2;V
+M1%@0=`R`3OP0@$[Z$(!.UH#V1%@@=!*`3OP@@$[Z((!.UH#K!(!E&?SV1M:`
+M=02`3M2`]D4(!'0$@$[>@/9%"$!T!(!.XH#V10@@=`2`3N)`]D4($'0$@$[B
+M(/9%#`)T!(!.W@+V10P$=`2`3MX!]D4(`G48@'[R`'02]D4*$'0&QD;L`NL*
+MQD;L`>L$QD;L`,9&W/^#?0(`=06#/0!T98-]`@-W7W(&@3T`A'=7_W4"_S6+
+M7O[_=PS_=PKH9"6)1M")5M+'1N@``.L$D/]&Z(-^Z`5]+XM>Z-'CBX>X!)E2
+M4(M>Z-'CBX?^.9D#1M`35M)24.@L)8E&VCT``7/.BD;HB$;<@'[<_W44*\")
+M10*)!8E%!HE%!,9&W`")1MIJ`&C0!_]U`O\UZ/<DB82,`(-](`!U((-](@!U
+M&FH`:@K_=0+_->C;)(E%(J'F.#E%(G8#B44B@WT@`'4%QT4@"@"*13J(1O0*
+MP'0$/`QV!,9&]`B*1O0JY#E%(G,,@WTB`'0&BD4BB$;TBD;T"$;J/`QS`_Y&
+M](%](/L$<@;&1N;_ZPV+12"Y!0`KTO?QB$;F]D4(`70'@$[4$.L%D(!.UA"+
+M10R+50XD^0O0=`2`3NX@Z$OIB4;HBH2&`"4#`%!J:%;H[AZ#Q`;_=@S_=@K_
+M=@C_=@96Z#D"@\0*:@96Z+H>@\0$"$;6BD;4]M`@1M9J`&H&5NB\'H/$!O9$
+M=D!T!(!.[B#&1N0`BD;P*N10:@A6Z+H>@\0&"\!T!(!.Y$**1NXJY%!J"5;H
+MHQZ#Q`8+P'0$@$[D1(I&ZBKD4&H*5NB,'H/$!@O`=`2`3N1(BD;B*N10:AY6
+MZ'4>@\0&BD;>*N10:A]6Z&8>@\0&:A56Z"H>@\0$)0\`"D;\4&H55N@R'H/$
+M!HI&^BKD4&H65N@^'H/$!HI&YBKD4&HA5N@O'H/$!HM%`@L%=#:*1MPJY%!J
+M=E;H&1Z#Q`;_=MIJ<E;H#1Z#Q`:*1MPJY%!J?%;H_AV#Q`;_=MIJ>%;H\AV#
+MQ`:*1O0JY(F$D`"*1MB(A)(`BD;VB(23`(I&\HB$E`"*1NR(A)4`BD4VF%!J
+M&E;HOQV#Q`:*13684&H;5NBQ'8/$!HI%-)A0:AQ6Z*,=@\0&BD4SF%!J'5;H
+ME1V#Q`;V10H(=!6*13F84&HB5NB!'8/$!HI%.9A0ZPUJ`&HB5NAO'8/$!FH`
+M:B-6Z&0=@\0&@'[D`'065NCR%X/$`HI&Y"KD4&H%5N@M'8/$!O9%&`%U&/9&
+M!@%T$E;HT!>#Q`)J&&H%5N@/'8/$!O9$=P%T'O9%"`%T&%;HLA>#Q`)J$6H%
+M5NCQ'(/$!H!D=_[K(O9$=P%U'/9%"`%U%E;HCA>#Q`)J$FH%5NC-'(/$!H!,
+M=P%6Z'@7@\0"BD;6*N10:@96Z,X<@\0&_W;HZ/KF@\0"7E_)PY!5B^Q6BW8$
+MBT08BU0:,48&,58(BT04BU06,48*,58,]D1V$'1%]D8&$'04]D08$'0%:@+K
+M`Y!J!E;H\`Z#Q`3V1@8"=!3V1!@"=`5J`.L#D&H!5N@"`8/$!/9&!D!T*?9$
+M&$!T&FH"ZQB0]D8&$'0):@96Z+8.@\0$]D8&0'0):@96Z'</@\0$]D8*@'03
+M]D04@'0$:@+K`FH&5NB.#H/$!%[)P\@"``!6BW8$]D1V$'1JBH2&`"4#`%!J
+M:%;HXAN#Q`;V1!@"=`EJ`5;HB0"#Q`3V1!@0=`EJ!E;H3@Z#Q`3V1!A`=`EJ
+M!E;H#P^#Q`2`9';OBH2&`"4#`%!J:%;HH!N#Q`9J!E;H?QN#Q`0,$"KD4&H&
+M5NB)&X/$!E[)PY#(`@``5U:+=@2+?@;HO^6)1OZ*A(8`)0,`4&IH5NAB&X/$
+M!E=6Z!``@\0$_W;^Z*'E@\0"7E_)PU6+[%:+=@16Z/45@\0"@WX&`'0::B-J
+M!5;H+AN#Q`;$7'`F@T<X`2:#5SH`ZQAJ)&H%5N@4&X/$!L1<<":#1SP!)H-7
+M/@#$7'`F@T<$`2:#5P8`7LG#58OL5HMV!`OV=0/IFP"+1@@+1@9U`^F0`%;H
+MQPZ#Q`)6Z!81@\0"BT1$BU1&Q%X&)HE';":)5VZ+1$R+5$[$7@8FB4=H)HE7
+M:O\VK@2-A*(`4.BO%8/$!)G$7@8FB4<,)HE7#O\VK`2-A)H`4.B4%8/$!)G$
+M7@8FB4<0)HE7$L1>!B:+1PPFBU<.)BE'!"895P;$7@8FBT<0)HM7$B8!1P@F
+M$5<*@&1%?U[)P\@B``!75HM>!(L'B4;LBUX&T>,#7@2+1Q:)1OZ+V(M'!L'H
+M`HE&\&H`:,@`BT<*BU<,BL2*XHK6*O;1ZM'84E#H51^(1OS'1O@``.MND$>!
+M_Q`G?2B+1NX%0`!0_W;ZZ+/@@\0$BT;Z!0@`4.BPX(/$`HA&]CQ`<M4\8'?1
+M@?\0)WT#Z:H`:&,):-H$_S;<`/\VX@#HO=J#Q`C_=OB+7O[_=P+_=NQHXP3H
+MJ-J#Q`AH%@7HG]J#Q`+_1OB+1O`Y1OA\`^F^`(M>^-'C`U[^BT<4B4;ZBU[X
+MT>.+A[`$B4;NBU[XT>,#7OZ)1R2+1NX%0`!0_W;ZZ!W@@\0$:@"+1OH%"`!0
+MZ`[@@\0$BT;N!04`4/]V^NC^WX/$!&B!`(M&^@4(`%#H[M^#Q`0K_^D@_Y"*
+M3OBX`0#3X(M>_@E'$HM&[@5+`%#_=OKHRM^#Q`1J`(M&^@4(`%#HN]^#Q`2+
+M1NX%?@!0_W;ZZ*O?@\0$BD;\*N10BT;Z!0@`4.B8WX/$!.DU_Y"+7OZ`3PX!
+MBU[^BT<&B4;RQT;>```+P'\#Z:H`H:P$]V;R!1``4.B5UX/$`HE&Z(E6ZJ&N
+M!/=F\@40`%#H?]>#Q`*)1N")5N*+1NH+1NAT!XO""T;@=07'1M[__XM&Z(M6
+MZH#D#XK$BN**UBKVT>K1V-'JT=C1ZM'8T>K1V`-&Z(/2``4/`(/2`"3PB4;H
+MB5;JBT;@BU;B@.0/BL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`T;@@](`!0\`
+M@](`)/")1N")5N(K_^E]`9!J=(O?P>,"`U[^_W=V_W=TZ`K?@\0&BT;LB82`
+M`(M&!HF$@@")O(8`BD8&*N29BO**U(K@*L"+3NR`S?^+V`O*B\<JY)D+V`O*
+MB5QZB4Q\B\?!^`.+7OX#1P2)A(0`QT1V``*+W\'C`@->_HM'=(M7=HE$<(E4
+M<HI&]BKDQ%QP)HE'<";'1W(``(O'F3/"*\+!^`(SPBO"B4;XB]C1XP->_HM'
+M)(F$B@"+7OC1XP->_HM'%(F$B`"+7OZ*3OBX`0#3X(5'$G4#Z0X!B\<E`P!0
+M:FA6Z"(7@\0&B\?!X`-0:AA6Z!,7@\0&@W[>`'T#Z><`BX2D``N$H@!U.XM&
+MX(M6XM'JT=C1ZM'8T>K1V-'JT=B*3N"#X0^)C*(`B82D`,>$I@```,>$J```
+M`*&N!"O2`4;@$5;BBX2<``N$F@!U.XM&Z(M6ZM'JT=C1ZM'8T>K1V-'JT=B*
+M3NB#X0^)C)H`B82<`,>$G@```,>$H````*&L!"O2`4;H$5;J1XM>_CE_!GY<
+M:388!:H`@<9<#O\&&`6+W]'C`U[^B7<TB]_!XP(#7OZ+1W8+1W1T`^E1_FIT
+MZ#[5@\0"B]_!XP(#7OZ)1W2)5W:+W\'C`@->_HM'=@M'='0#Z2C^QT1V`(#K
+MG)"AK`2Y!0`KTO?QHS(YH:P$P>`"*]+W\:,B.:&L!"L&(CD]$`!V"*&L!"T0
+M`.L%H:P$2$BCW#BA(CDY!MPX<P:AW#BC(CFAK`31Z*/F.*&N!"O2]_&C.#FA
+MK@3!X`(KTO?QHR`Y*\!>7\G#R!8``%=6H0`&_P8`!@O`=`<KP%Y?R<.0QT;T
+MZ#B+1O2CNCF+V,<'``"+7O3'1P(7`(M>],='#@``BU[TQT<0@`"+7O3'1Q(`
+M`(M>],='%```BU[TQT<(``#'1OR``,=&\`@"QT;R``#'1OX``,9&^``K_^GS
+M`(I&^B0'.D;X=`/I`@%I]_0`@<8:!L<$``"+1OZ)1`**1OHE?P")1`C'1`I`
+M>,=$#'T!BT;\B404BT;\B406BU[TBU\(T>,#7O2)=R:+7O3_1PCV1OH@=&"+
+M1OP%!@!0Z*W;@\0"BD[Z*NT[P74$@$P(@,=$!A``BT;\!1``B408BT;\!1``
+MB40:@T;\((M>](M?"-'C`U[TB7<FBU[T_T<(BE[X*O\#7O".1O**1`@FB`>`
+M1O@"ZQW'1`8(`(-&_!"*7O@J_P->\(Y&\HI$"":(!_Y&^(M>]/]'!(M>_M'C
+M`U[TB7<6BT0&BU[T`4<&_T;^1X/_"'T7BT;\!00`4.@-VX/$`HA&^CS_=`/I
+M\?['!JP$``#'!JX$``"+7O2#?P8`=%N+1P;1X&H`4*'B.(L6Y#AK3P0H*\&#
+MV@!K3P9X*\&#V@!24.@^&8E&ZHE6[`O2=04]``QV"<<&K`0`#.L8D`O2=0X]
+M0`!S"<<&K`1``.L&D"3PHZP$H:P$HZX$*__K`I!'BU[T.7\$=@Q74^A>^8/$
+M!`O`?>N+7O2`3PP#*_^+W]'CBX>X!)DKPM'XB]_1XXF'_CE'@_\%?.7_!KPY
+MBU[TBU\6BT<TB4;JB]C'!X`EQT<"``"+7NK&1S`(BU[JQD<R`(M>ZL9',0"+
+M7NHKP(E'&HE'&(M>ZO]W%O]W%%!04^@?\(/$"O]VZNA\#(/$`L=&ZC0`QT;L
+M``"A!@#$7NHFQP?:8R:)1P+I;/V0*\##D,@(``!75J&Z.8E&^@O`=08KP%Y?
+MR<.A&@6)1O['1O@``.MID$>+7OPY?P9^68O?T>,#7OR+=S0+]G3I]D1V`73C
+M]D1V!G0'5NCI`(/$`H-\>`!T!U;H?`"#Q`*`O)8``'0'5N@$`X/$`O9$=@AT
+M!U;H5P&#Q`+V1':`=*A6Z`3E@\0"ZY^0_T;^_T;XBT;XBU[Z.4<$?B6+1OXY
+M1P1_!<=&_@``BU[^T>,#7OJ+1Q:)1OP+P'30*__I:O^0@SX:!0A\"<<&&@4`
+M`.E$__\&&@7I/?^0R`0``%:+=@3H<=R)1OR+1'B)1O['1'@``(!,=H#_=OSH
+M7=R#Q`+V1OX!=`2`3&80]D;^`G0$@$QF(/9&_@1T!(!,9D#V1OX(=`B`3$6`
+M@$QE@/9&_A!T!(!,9H!>R<.0R`8``%=6BW8$Z!#<B4;\]D1V`G4E]D1V!'0,
+M@$QD`H!,=H"`9'Z`@&1V^_]V_.COVX/$`BO`7E_)PXN$I@")1OZ+A*@`B4;Z
+MBT;^.4;Z=PB+^"M^^NL*D(OX*W[Z`SZN!#E\)'(4]D1^!'6_@$Q^!(!,9`2`
+M3':`Z[$Y?"9WK/9$5`ATIO9$?@AUH(!,?@B`3&0(Z]^0R`@``%=6BW8$Z'K;
+MB4;\BX2>`(E&_HN$H`")1OJ+1OXY1OIW!XOX*W[ZZPF+^"M^^@,^K`2+QRN$
+MC@#V1'\!=1TYA(P`<PLY?")V!O9$9H!T#(!,?P&`3&4!@$QV@#E\*G<7]D15
+M"'0N]D1_"'4H@$Q_"(!,90CK&I`Y?"AR&/9$501T$O9$?P1U#(!,?P2`3&4$
+M@$QV@/]V_.CMVH/$`HF\C@`KP%Y?R</(#```5U:+=@3HT-J)1OJ+A)H`BY2<
+M`(E&_(E6_HN\G@"+A*``.\=W"2O'`P:L!.L#D"O'/0,`?1_$7'`F@T<P`2:#
+M5S(`Q%QP)H-'-`$F@U<V`%Y?R<.0BD0[Q%[\)H@!1SD^K`1W`BO_Q%[\)L8!
+M`T<Y/JP$=P(K_\9&]@#V1@:`=`2`3O8(]D8&0'0$@$[V`?9&!A!T!(!.]@+V
+M1@8@=`2`3O8$]D1,`G0$@$[V@/9$3`AT!(!.]A#V1$P0=`2`3O8@]D1,('0$
+M@$[V0(I&]L1>_":(`4<Y/JP$=P(K_XF\G@"`3'8(_W;ZZ/+9@\0"7E_)PY#(
+M!```5HMV!.C;V8E&_HJ$E@"(1OS&A)8``(!,=H#_=O[HQMF#Q`+V1OR`=!'$
+M7'`F@T=(`2:#5TH`@$QH`O9&_$!T*_9$%`AT%,1<<":#1T@!)H-72@"`3&@"
+MZQ*0Q%QP)H-'4`$F@U=2`(!,:`CV1OP0=!'$7'`F@T=8`2:#5UH`@$QH$/9&
+M_"!T$<1<<":#1UP!)H-77@"`3&@@5NCI`H/$`HM$3(M43HE$;(E4;O:$E0`"
+M=`V*1OPJY%!6Z$G^@\0$*\!>R<.0R`8``%=6BW8$BWX&]\<"`'1NQ%QP)H-'
+M'`$F@U<>`.CZV(E&^O\VK@2-A*(`4.BO"8/$!)G$7'`F*4<$)AE7!HN$I@")
+MA*@`@&1^P(J$A@`E`P!0:FA6Z'8.@\0&5N@E"8/$`FB"`&H%5NAC#H/$!E;H
+M$@F#Q`+_=OKHH]B#Q`+WQP$`=0/IS0#$7'`F@T<@`2:#5R(`Z(/8B4;ZBH2&
+M`"4#`%!J:%;H)@Z#Q`9J"E;H!0Z#Q`2(1OPE\0`,`5!J"E;H"PZ#Q`96Z+H(
+M@\0":DAJ!5;H^0V#Q`;_=OKH0-B#Q`+H-MB)1OJ*A(8`)0,`4&IH5NC9#8/$
+M!HI&_"KD4&H*5NC*#8/$!E;H>0B#Q`)J2&H%5NBX#8/$!O\VK`2-A)H`4.BY
+M"(/$!)G$7'`F`4<()A%7"HN$G@")A*``@&1_P(!D=O=6Z(CQ@\0"_W;ZZ,W7
+M@\0"7E_)P\@$``!75HMV!/9$%`1T`^FZ`/9&!@)U%NBGUXOXBH2&`"4#`%!J
+M:%;H2PV#Q`;V1@8!=`C'1OP``NL&D,=&_``$]D8&!'0+BT;\]]`A1';K!Y"+
+M1OP)1';V1'<&=1;V1!@0=!!J%5;H]`R#Q`0+A)``ZPR0:A56Z.0,@\0$)/!0
+M:A56Z/`,@\0&]D8&!'0/]D1W!G4)@$Q,!&H"ZP>0@&1,^VK]:FU6Z,P,@\0&
+M@(R6``+$7'`F@T=4`2:#5U8`]D8&`G4'5^C]UH/$`EY?R</(!```5U:+=@3V
+M1@8"=1;HX-:+^(J$A@`E`P!0:FA6Z(0,@\0&]D04!'0-QT;^;0#'1OP"`.L+
+MD,=&_FP`QT;\`0#V1@8$=!2`3$P!_W;\_W;^5NA0#(/$!NL5D(M&_/?04/]V
+M_E;H/0R#Q`:`9$S^BD;\"(26`,1<<":#1TP!)H-73@#V1@8"=0=7Z&C6@\0"
+M7E_)PY#("```5U:+=@2+G(``T>.+A[HYB4;ZZ$/6B4;\BH2&`"4#`%!J:%;H
+MY@N#Q`9J;%;HQ0N#Q`2(1OAJ;5;HN0N#Q`2(1O[_=OSH%=:#Q`**1$PE!`")
+M1$S'1$X``/9$%`AT$(!,3`CV1!1`=2OV1OA`ZR/V1OA`=`2`3$P(BU[Z@W\"
+M%'4&@W\*!G0,]D040'4&]D;X@'0$@$Q,`O9$%`1T!_9&_@+K%9#V1OX"=`:`
+M3$P$ZP2`9$S[]D;X`70$@$Q,`8M>^H-_`A1U!H-_"@9U#XN\@@#1YXM9%O9'
+M"`AU&/9&^!!T!(!,3!#V1O@@=`R`3$P@7E_)PX!,3!!>7\G#R`8``%:+=@3H
+M4=6)1OZ*A(8`)0,`4&IH5NCT"H/$!FH+5NC3"H/$!(A&^FIL5NC'"H/$!(A&
+M_/]V_N@CU8/$`L1>!BO`)HE'`B:)!_9&^@1T#O9&^@)U",1>!B:`3P(!]D08
+M('0.]D;\0'4(Q%X&)H!/`@1>R<.0R`0``%:+=@2AK@0%"P#$7@8FB4<$H:P$
+M2,1>!B:)1P;HP=2)1O[_-JX$C82B`%#H=@6#Q`3$7@8FB0?_-JP$C82:`%#H
+M806#Q`3$7@8FB4<"BH2&`"4#`%!J:%;H.0J#Q`9J"U;H&`J#Q`2(1OS_=O[H
+M=-2#Q`+$7@8KP":)1PHFB4<(]D1V`G0&N`$`ZP20N`(`*]+$7@8F"4<(]D1V
+M"'0&N``!ZP20N``"Q%X&)@E'"/9$=A!T!K@`0.L#D"O`Q%X&)@E'"/9&_`1T
+M#O9&_`)U",1>!B:`3PH!7LG#R`@``%=6BW8$C7Q$BF4!)0"`B07'10(``.CJ
+MTXE&_/\VK@2-A*(`4.B?!(/$!(E&^/\VK`2-A)H`4.B-!(/$!(E&^HJ$A@`E
+M`P!0:FA6Z&D)@\0&:@M6Z$@)@\0$B$;^_W;\Z*33@\0"]D1V`G0%N`$`ZP.X
+M`@`KT@D%]D1V0'0%N(``ZP(KP`D%]D;^!'0*]D;^`G4$@$T"`?9$&"!T"O9%
+M"`AU!(!-`@3V10('=`.`#4"+1O@Y1"1R`X`-!(M&^#E$)G<#@`T(H:X$*T;X
+M2'4#@`T0]D1V"'0%N``!ZP.X``()!?9$=A!T!;@`0.L"*\`)!8M&^CE$*'($
+M@$T!!(M&^CE$*G<$@$T!"(M&^CE$+'<$@$T!(*&L!"M&^DAU!(!-`1!>7\G#
+MD,@"``!75HMV!(M^!NC-THE&_H!,=B"#__]U#<:$EP`"QH28``#K+)"#__YU
+M!\:$EP`#Z^P+_WX;QH27``&Y!0"+QYGW^8OX/?\`?@.X_P"(A)@`5NCG`(/$
+M`O]V_NB`TH/$`H/__G0-Q%QP)H-'8`$F@U=B`%Y?R<.0R`(``%:+=@1J"5;H
+M\`>#Q`0,("KD4&H)5NCZ!X/$!E;HJ0*#Q`)J1&H%5NCH!X/$!E;HEP*#Q`*`
+M3'9`*\!>R<.0R`(``%:+=@2+1`R+5`XD^0O0=3!J"5;HH@>#Q`0DWRKD4&H)
+M5NBL!X/$!E;H6P*#Q`)J1&H%5NB:!X/$!E;H20*#Q`*`9':?*\!>R<.058OL
+M5U:+=@2+?@9H5Q!H'`7_-MP`_S;B`.AVR(/$"%=6Z&[(@\0$:"4%Z&7(@\0"
+M7E_)P\@"``!75HMV!/9$=PAU2>B,T8OX@$QV`HJ$A@`E`P!0:FA6Z"P'@\0&
+M:@96Z`L'@\0$)/T,!"KD4&H&5N@3!X/$!O9$%(!T"6H&5NB.^8/$!%?H3=&#
+MQ`)>7\G#R`(``%=6BW8$Z#;1B_B`9';]@$QW"(J$A@`E`P!0:FA6Z-(&@\0&
+M:@96Z+$&@\0$)/DJY%!J!E;HNP:#Q`97Z`31@\0"7E_)PY!5B^Q75HMV!.CM
+MT(OXBH2&`"4#`%!J:%;HD0:#Q`96Z$`!@\0":AAJ!5;H?P:#Q`97Z,C0@\0"
+M7E_)PY!5B^Q75HMV!.BQT(OXBH2&`"4#`%!J:%;H50:#Q`96Z`0!@\0":A1J
+M!5;H0P:#Q`96Z/(`@\0"5^B%T(/$`EY?R</(`@``5U:+=@3H;M"+^(J$A@`E
+M`P!0:FA6Z!(&@\0&5NC!`(/$`FH2:@56Z``&@\0&:@96Z-\%@\0$#!`JY%!J
+M!E;HZ06#Q`:`3'<!5^@NT(/$`EY?R<.0R`(``%=6BW8$Z!;0B_B*A(8`)0,`
+M4&IH5NBZ!8/$!FH&5NB9!8/$!"3O*N10:@96Z*,%@\0&5NA2`(/$`FH1:@56
+MZ)$%@\0&@&1W_E?HUL^#Q`)>7\G#D%6+[%=6BW8$Z+_/B_B*A(8`)0,`4&IH
+M5NAC!8/$!FH`:@96Z%@%@\0&5^BASX/$`EY?R<-5B^Q75HMV!"O_ZP%'@?_H
+M`WT1:@56Z!D%@\0$"\!U[%Y?R<-H91%H)P7_-MP`_S;B`.@AQH/$"/^TA@#_
+MM((`_[2``&@P!>@,QH/$"&AC!>@#QH/$`EY?R<-5B^Q75HM>!(MW!(M_!CO^
+M=PB+QBO'7E_)PXM&!BO'`\9>7\G#D,@$``!6BW8$BT08BU0:B4;\]D;\`G0R
+M:@56Z)4$@\0$"\!U1VHD:@56Z)X$@\0&Q%QP)H-'/`$F@U<^`,1<<":#1P0!
+M)H-7!@#V1OP0=`EJ`E;H__:#Q`3V1OQ`=`EJ`E;HP/>#Q`2`3'807LG#R!``
+M`%=6@SZ\.0!U!RO`7E_)PY"ANCF)1O8+P'4(N/__7E_)PY"+7O:+1Q:)1OX+
+MP'3K@WX$`'SEBT8$BU[^.4<&=MJ+V-'C`U[^BW<T"_9TS.A3SHE&^(J$A@`D
+M`XA&]&IH5NC=`X/$!(A&_(I&]"KD4&IH5NCC`X/$!FH+5NC"`X/$!(A&\O9&
+M\@AU!U;H'OV#Q`)J'5;HJ0.#Q`2(1O`K_^L"D$>!_Q`G?0UJ!5;HD0.#Q`0+
+MP'7L_W8&:AU6Z)D#@\0&:B1J!5;HC@.#Q`8K_^L"D$>!_Q`G?0UJ!5;H80.#
+MQ`0+P'7L@WX&"G4N:@UJ'5;H9`.#Q`9J)&H%5NA9`X/$!BO_ZP%'@?\0)WT-
+M:@56Z"T#@\0$"\!U[(I&\"KD4&H=5N@R`X/$!O9&\@AU!U;HM?R#Q`**1OPJ
+MY%!J:%;H%@.#Q`;_=OCH7<V#Q`+IN/Z0*\##D,@,``"#?@3_=2"#?@;_=1K_
+M=@[_=@S_=@K_=@C_=@;_=@3H[0"#Q`S)PX%^"`%D=0:#?@H`=`6X___)PXM&
+M#(M6#HE&_(E6_HI&!BKD"\!UYL=&](``QT;Z@`#$7OPFB@<E!P`FB0<FQT<"
+M``#$7OPKP":)1P8FB4<$Q%[\)O8'`70I_W;TZ+L`@\0""\!T"L1>_":`3P0!
+MZQ+$7OPFB@<E`0`FB0<FQT<"``#$7OPF]@<$="&+1O0%!@!0Z!K)@\0"QP9^
+M!0``:@&+1O1`0%#H_,B#Q`3$7OPF]@<"=!H&4_]V^O]V].C6`(/$"`O`=`C$
+M7OPF@$\$`L1>_";V!P1T#(,^?@4`=`4F@$\$!"O`R</(!@``QT;Z@`#_=OKH
+M(P"#Q`(+P'0$*\#)P[C__\G#D,<&?@4!`&B&`.B;R(/$`BO`P\@$``"+1@0%
+M!`!0Z(?(@\0"B$;^]M`JY%"+1@0%!`!0Z&C(@\0$:@3_=@3H7<B#Q`2+1@0%
+M"`!0Z%K(@\0"BT8$!00`4.A-R(/$`HA&_(I&_BKD4(M&!`4$`%#H+<B#Q`2*
+M1OPJY/?0*N2*3OXJ[3O!=06X`0#)PRO`R</(!@``5U;'1OP!`(MV!(O^`WX&
+MZP.01D8[_GY<:@!6Z.W'@\0$5NCPQX/$`HA&_@K`=..+QBM&!)DKPM'XQ%X(
+M)HA'",1>"";&1PD`B\8K1@29*\+1^,1>"":(1PJ*1O[$7@@FB$<+QT;\``"+
+M=@2+_@-^!NFG`)`K]NMSD)!'1SE^!GY1BT8$`\=0Z(O'@\0"B$;Z._YU"(I&
+M_CA&^G4*._YTVH!^^@!TU(O&F2O"T?C$7@@FB$<(BD;^Q%X()HA'"8O'F2O"
+MT?C$7@@FB$<*BD;ZZXN0T&;^@'[^`'4<:@"+1@0#QE#H)<>#Q`1&1CEV!G\#
+MZ77_QD;^`8I&_BKD4(M&!`/&4.@%QX/$!"O_Z6[_:/\`5NCVQH/$!$9&._Y_
+M\(M&_%Y?R<.0B]R+7P*+EX@`BX>*`(O<`T<$[H/"".S#B]R+7P*+EX@`BX>*
+M`(O<`T<$[HM'!H/"".[#B]R+7P*+EX@`BX>*`(O<`T<$[H/"".P[1P9T"(M'
+M!NZX`0##,\##8!X&+HX><`'H"P"Z(O^X`(#O!Q]ASS/;NH0`[*B`=#F#Z@2P
+MY^Z#P@CLBN"#Z@BP9^Z#P@CL"L1T'(D>\@6)%O0%T>.+OPXYJ`%U):@"=2>H
+M!'4@T>N#Z@2#PA!#.Q[P.'*XPXL>\@6+%O0%@^H$ZZKIG`'IOP*#P@0SP.R+
+MV('C^`#!ZP*+>30D!SP"=57W178@`'5<Q(6B`(N=I@"+M:@`.]YT<G<$BQZN
+M!(/J#(J%B@`$8^Z#P@BX#`#\B\LKSCO(<@*+R"O!\R9N._-S)8FUJ`"#Z@B*
+MA8H`!&#N@\(([NN`8%!HI07HWK^#Q`1AZ_#K8)"+G:8`._-T!C/V._-UNH/J
+M"(J%B@`$!NZ#P@CL)/D,`N[KN(N5B`"*A8H`!`;N@\(([*@$=`<D^0P"[NNB
+M)/GN@V5V_8--=@3V112`=0+KCU*X`@!05^CZ\(/$!%KK@(J%EP`\!'1M/`!U
+M"E?HAO:#Q`+I:O]7Z#KV@\0"BH67`#P!=`\\`G0X/`-T7<:%EP``Z]B+E8@`
+MBH6*``1C[H/""+``[K"![K``[K""[HJ%F`#NL`#NL(/NQH67``#I'_^+E8@`
+MBH6*``1C[H/""+``[K"![HN5B`"*A8H`!`;N@\((["3Y[NGV_HN5B`"*A8H`
+M!&/N@\((L`#NL(/NQH67``#IVOX\!W4#Z1,!8%!H@`7HP;Z#Q`1AZ:,`@\("
+M,\#LB]B!X_@`P>L"BWDT)`<\`W72,\F+E8@`BH6*``0.[H/"".R*R`K)=0/K
+M8Y"+M9X`BY6@`#OR<@>+QBO"ZPB0*]:AK`0KP@/!.T4J<U/$A9H`BY6(`(J%
+MB@`$8NZ#P@B*I94`BYV@`(/[`'4$BQZL!$OL._-T,2:(!$8[-JP$=",*Y'5"
+MXNN)M9X`@TUV"(N5B`"*A8H`!&#N@\(([NFN_>M)D#/VZ]E6!L1U<":#1#`!
+M)H-4,@`F@T0T`2:#5#8`!U[BG^O`]L0"=`HZ13MUM+``ZP60//]UJSOS=,HF
+MB`1&.S:L!'6=,_;KF?=%=A``=0E15^CG]X/$`EGI3_^+E8@`BH6*``1B[H/"
+M".PSVXK8,\#L4U!7Z"K7@\0&Z6[_@\(&,\#LB]B!X_@`P>L"BWDT)`<\`70/
+M8%!HR@7H=+V#Q`1AZR.0BY6(`(J%B@`$3.Z#P@CL"(66`(N5B`"*A8H`!&#N
+M@\(([NGB_%6+[(M>!/]V"/]V!O]W`O\WZ"X`BUX$B5<"B0>+Y5W"!@!5B^R+
+M7@2+!XM7`HM.!NCS`8M>!(D'B5<"B^5=P@0``%6+[%=64S/_BT8&"\!]$O?7
+MBU8$]]CWVAT``(E&!HE6!(M&"@O`?1+WUXM6"/?8]]H=``")1@J)5@@+P'46
+MBTX(BT8&,]+W\8O8BT8$]_&+T^L\D(O8BTX(BU8&BT8$T>O1V='JT=@+VW7T
+M]_&+\#/24E#_=@K_=@CH-`$[5@9W!W((.T8$=@.#[@$STHO&"_]T!_?:]]B#
+MV@!;7E^+Y5W""```58OL4U<S_XM&!@O`?1+WUXM6!/?8]]H=``")1@:)5@2+
+M1@H+P'T0BU8(]]CWVAT``(E&"HE6"`O`=1J+3@B+1@8STO?QBT8$]_&+PC/2
+M"_]U1>M*D(O8BTX(BU8&BT8$T>O1V='JT=@+VW7T]_$STE)0_W8*_W8(Z)$`
+M.U8&=P=R"SM&!'8&*T8(&U8**T8$&U8&"_]U!_?:]]B#V@!?6XOE7<((`%6+
+M[%-6BT8*"\!U%HM."(M&!C/2]_&+V(M&!/?QB]/K/)"+R(M>"(M6!HM&!-'I
+MT=O1ZM'8"\EU]/?SB_`STE)0_W8*_W8(Z!P`.U8&=P=R"#M&!'8#@^X!,]*+
+MQEY;B^5=P@@``%6+[(M&!HM>"@O8BUX(=0N+1@3WXXOE7<((`/?CB\B+1@3W
+M9@H#R(M&!/?C`]&+Y5W""``R[>,&T>#1TN+ZPP"#!A8&`8,6&`8`_P8>.8,^
+M'CED<P/I@0"#!B@Y`8,6*CD`QP8>.0``H28Y"P8D.70H_S8F.?\V)#EJ`&ID
+MH20YBQ8F.2L&-#D;%C8Y4E#H;_]24.@$_Z/\.8,^_#D`?`>#/OPY9'X&QP;\
+M.0``@SX,.@-\#?\V_#EH]@7HF+J#Q`2#/@PZ`GP):/D%Z(BZ@\0"*\"C-CFC
++-#GIZJ`````````V
+`
+end
diff --git a/usr.sbin/stallion/bootcode/stl.4 b/usr.sbin/stallion/bootcode/stl.4
new file mode 100644
index 0000000..81177ec
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/stl.4
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 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.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STL 4 i386
+.Sh NAME
+.Nm stl ,
+.Nm stli
+.Nd "drivers for Stallion Technologies multiport serial controllers"
+.Sh SYNOPSIS
+.Cd "stl0 at isa? port <addr> tty irq <irq>"
+.Cd "stli0 at isa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type> "
+.Cd "stli0 at eisa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type> "
+.Sh DESCRIPTION
+This is a kernel driver for Stallion Technologies multiport serial boards.
+There are two drivers, each supporting a different class of boards.
+The
+.Nm stl
+driver supports the EasyIO and EasyConnection 8/32
+boards, while the
+.Nm stli
+driver supports all other types, including
+ONboard, Brumby and EasyConnection 8/64.
+.Sh CONFIGURATION
+Each board installed in the system needs a configuration entry in the
+kernel configuration file.
+Slightly different options and parameters are required for each of the
+different board types.
+Depending on the type of board one of the
+.Nm stl
+or
+.Nm stli
+drivers will be used. The
+.Nm stl
+and
+.Nm stli
+drivers can support up to 8 boards.
+.Pp
+Configuration of the hardware - DIP switches, jumpers, etc - varies
+from board to board.
+Consult documentation supplied with the board for hardware
+configuration details.
+Alternatively the board documentation is available on Stallion
+Technologies WWW site at http://www.stallion.com.
+.Pp
+The EasyIO and EasyConnection 8/32 families of boards use the
+.Nm stl
+driver.
+ISA board configuration entries for the
+.Nm stl
+driver take the general form of:
+.Pp
+.Cd "stlX at isa? port <io-addr> tty irq <irq>"
+.Pp
+.Ar X
+is the unit number assigned to the board.
+Any unique value between 0 and 7 is valid.
+.Pp
+The I/O address used by the board is specified by
+.Ar <io-addr> .
+Each of the EasyIO and EasyConnection 8/32-AT boards can use
+an I/O address in the range from 0 to 0x400.
+.Pp
+All EasyIO and EasyConnection 8/32 boards require an interrupt,
+and this interrupt is specified by
+.Ar <irq> .
+Legal IRQ values for the ISA boards are 3, 4, 5, 7, 10, 11, 12 and 15.
+Interrupts are software programmed on all boards except the EasyIO-8M.
+.Pp
+The EasyConnection 8/32-AT board uses a secondary I/O address region,
+and this is fixed at address 0x280 in the driver code.
+All EasyConnection 8/32-AT boards may share the same secondary address
+region.
+.Pp
+EasyConnection 8/32 PCI boards are detected automatically by the
+system on boot up.
+No configuration information is required in advance for these
+board types.
+During boot up the
+.Nm stl
+driver will issue messages to indicate that a EasyConnection 8/32
+PCI board was found, and some information about it.
+.Pp
+Following are some examples of configuration entries for each of the ISA
+boards supported by the
+.Nm stl
+driver.
+Each example also describes some important details about each of the
+board types.
+.Pp
+Each EasyIO board requires 8 bytes of I/O address space and 1 IRQ line.
+A configuration entry for an EasyIO board would look like:
+.Pp
+.Cd "stl0 at isa? port 0x2a8 tty irq 15"
+.Pp
+This entry specifies an EasyIO board at I/O address 0x2a8 using IRQ 15.
+The I/O and IRQ values can be modified as required.
+.Pp
+Each EasyConnection 8/32-AT board requires 2 sets of I/O addresses
+and 1 IRQ line.
+The primary I/O address range is 2 bytes in size, and must be unique
+to each EasyConnection 8/32-AT board in the system.
+The secondary I/O address range is 32 bytes in size, but can be shared
+by multiple EasyConnection 8/32-AT boards.
+This secondary I/O address is set at 0x280 in the driver code.
+A configuration entry would look like:
+.Pp
+.Cd "stl0 at isa? port 0x2a0 irq 10 tty"
+.Pp
+This specifies an EasyConnection 8/32-AT with primary I/O address 0x2a0,
+secondary I/O address of 0x280 and IRQ 10.
+.Pp
+The ONboard, Brumby and EasyConnection 8/64 families of boards use the
+.Nm stli
+driver. The
+.Nm stli
+driver supports the ISA and EISA members of these families.
+.Pp
+ISA board configuration entries for the
+.Nm stli
+driver take the general form of:
+.Pp
+.Cd "stliX at isa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type>"
+.Pp
+.Ar X
+is the unit number assigned to the board.
+Any unique value between 0 and 7 is valid.
+.Pp
+The I/O address used by the board is specified by
+.Ar <io-addr> .
+Each of the different supported board types has restrictions on valid
+I/O addresses and also the amount of I/O space required varies between
+the boards.
+.Pp
+All boards using the
+.Nm stli
+driver require a shared memory region to operate.
+Depending on the board type the region required varies in size
+from 4 kbytes to 64 kbytes. The size of the board region is specified
+by field
+.Ar <size>
+of the configuration entry, and the address of the region is specified by
+.Ar <mem-addr> .
+.Pp
+The flags field specifies the particular board type that this entry
+applies to.
+Not all board types are distinguishable by the driver at runtime,
+so this field is required by the driver.
+Valid board types are:
+.Bd -literal -offset indent
+BOARD NAME TYPE I/O SIZE
+
+Brumby 2 0x4000
+ONboard 4 0x10000
+ONboard/E 7 0x10000
+EasyConnection 8/64-AT 23 0x1000
+EasyConnection 8/64-EISA 24 0x10000
+.Ed
+.Pp
+Following are some examples of configuration entries for each of the
+boards supported by the
+.Nm stli
+driver. Each example also describes some important details about
+each of the board types.
+.Pp
+The EasyConnection 8/64-AT board requires 4 bytes of I/O address space and
+4 kbytes of memory space.
+A configuration entry would look like:
+.Pp
+.Cd "stli0 at isa? port 0x2a0 tty iomem 0xcc000 iosiz 0x1000 flags 23"
+.Pp
+The flags field of this entry specifies that this is an
+EasyConnection 8/64-AT board.
+It is set to I/O address 0x2a0 and memory address 0xcc000.
+The
+.Ar iosiz
+parameter specifies a memory region size
+of 4 kbytes.
+.Pp
+The EasyConnection 8/64-EISA board requires a 64 kbyte region of
+memory space.
+This region can be anywhere in the 32 bit memory address space.
+A configuration entry would be like:
+.Pp
+.Cd "stli0 at eisa? port 0x2000 tty iomem 0x80000000 iosiz 0x10000 flags 24"
+.Pp
+The flags field is used to specify that this is an EasyConnection 8/64-EISA
+board.
+The I/O (port) address resource is derived from the EISA slot that
+the board is in.
+Each EISA slot is allocated a section of the I/O address space by the
+hardware of the system.
+That address being 0xX000 where X is the slot number.
+The example board is at memory address 0x80000000 which is 2 Gbyte.
+The
+.Ar iosiz
+parameter specifies the size of the memory region,
+in this case 64 kbytes.
+.Pp
+Each ONboard ISA board requires 16 bytes of I/O space and a 64 kbyte
+section of memory address space.
+Valid ONboard I/O addresses are in the range 0x200 to 0x300.
+A configuration entry for an ONboard ISA would look like:
+.Pp
+.Cd "stli0 at isa? port 0x240 tty iomem 0xd0000 iosiz 0x10000 flags 4"
+.Pp
+This entry specifies an ONboard ISA by setting flags to 4.
+It uses I/O address 0x240 and a memory region of 64 kbytes at memory
+address 0xd0000.
+.Pp
+Each ONboard/E board requires a 64 kbyte memory region, and this
+can be anywhere in the 32 bit address space (that is from 0 to 4 Gbyte).
+A configuration entry would look like:
+.Pp
+.Cd "stli0 at eisa? port 0x3000 tty iomem 0xc0000000 iosiz 0x10000 flags 7"
+.Pp
+The specifies an ONboard/E in slot 3 using a shared memory address
+of 0xc0000000 (3 Gbyte).
+.Pp
+Each Brumby board requires 16 bytes of I/O address space and a 4 kbyte
+region of shared memory space.
+The valid Brumby I/O addresses are in the range 0x300 to 0x400.
+The shared memory region of the Brumby must be in the 0xc0000 to
+0xdc000 region of the memory address space.
+A configuration entry for a Brumby would be like:
+.Pp
+.Cd "stli0 at isa? port 0x360 tty iomem 0xc8000 iosiz 0x4000 flags 2"
+.Pp
+This specifies a Brumby board at I/O address 0x360 using a shared memory
+region at address 0xc8000.
+.Sh NOTES
+When building the device nodes for the ports be sure to use the correct
+driver name,
+.Nm stl
+or
+.Nm stli.
+Each driver has a separate major number allocated,
+so even though the port device names are the same for each driver,
+the major number of the device node is different.
+Use the
+.Xr MAKEDEV 8
+script to create the devices.
+Use the ttyE and cue tag for the
+.Nm stl
+driver, and
+the ttyEi and cuei tags for the
+.Nm stli
+driver.
+.Pp
+The intelligent board types (ONboard, Brumby and EasyConnection 8/64)
+require a firmware download before the ports will be operational.
+This is achieved by using the
+.Nm stlload
+command.
+See its manual page for details on usage.
+.Sh FILES
+.Bl -tag -width "/dev/staliomem?" -compact
+.It Pa /dev/ttyE?
+standard callin devices
+.It Pa /dev/ttyiE?
+initial-state callin devices
+.It Pa /dev/ttylE?
+lock-state callin devices
+.It Pa /dev/cue?
+standard callout devices
+.It Pa /dev/cuie?
+initial-state callout devices
+.It Pa /dev/cule?
+lock-state callout devices
+.It Pa /dev/staliomem?
+board control device
+.El
+.sp
+Note that the port numbers start at 0 for port 0 of board 0.
+Each board has 64 port slots allocated for it.
+So the second boards ports start at 64 and go through 127.
+Use the
+.Xr MAKEDEV 8
+script to create the devices.
+Use the ttyE and cue tag for the
+.Nm stl
+driver, and
+the ttyEi and cuei tags for the
+.Nm stli
+driver.
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8 ,
+.Xr MAKEDEV 8 ,
+.Xr stlload 8 ,
+.Xr stlstats 8
+.Sh HISTORY
+This driver was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlload/Makefile b/usr.sbin/stallion/stlload/Makefile
new file mode 100644
index 0000000..46048a5
--- /dev/null
+++ b/usr.sbin/stallion/stlload/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile,v 1.3 1997/02/22 16:13:45 peter Exp $
+
+PROG= stlload
+MAN8= stlload.8
+
+CFLAGS+=-DBOOTDIR=\"${BOOTDIR}\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/stallion/stlload/stlload.8 b/usr.sbin/stallion/stlload/stlload.8
new file mode 100644
index 0000000..51e1197
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.8
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 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.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STLLOAD 8 i386
+.Sh NAME
+.Nm stlload
+.Nd "Stallion Technologies multiport serial board down loader"
+.Sh SYNOPSIS
+.Nm stlload
+.Op Fl vhVR
+.Op Fl i Ar image-file
+.Op Fl c Ar control-device
+.Op Fl r Ar rx-buf-size
+.Op Fl t Ar tx-buf-size
+.Op Fl B Ar boot-banner
+.Op Fl b Ar unit-number
+.Sh DESCRIPTION
+.Nm Stlload
+is used to download the firmware code to Stallion Technologies intelligent
+multiport serial boards.
+A firmware download is only required for those boards that use the Stallion
+.Nm stli
+driver.
+This includes the EasyConnection 8/64, ONboard and Brumby families of boards.
+.Pp
+Different board types require different firmware images.
+If the wrong firmware is loaded into a board it will fail to operate.
+.Pp
+The download process is achieved through the Stallion
+.Nm stli
+driver control device,
+.Pa /dev/staliomem? .
+This device implements a file type device that can read and write into the
+boards shared memory region.
+It also implements a number of special
+.Em ioctls
+that reset and restart the board.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl v
+Verbose output generation.
+Trace is generated at each phase of the download and startup process.
+.It Fl h
+Output usage information.
+.It Fl V
+Output version information.
+.It Fl R
+Reset the board only.
+Does not proceed to download firmware to the board.
+.It Fl i Ar image-file
+Specify the firmware image file to download.
+The default firmware image is
+.Pa /usr/libdata/stallion/cdk.sys .
+.It Fl c Ar control-device
+Specify the board control device through which to download the firmware
+and start up the board.
+The default is
+.Pa /dev/staliomem0 .
+.It Fl r Ar rx-buf-size
+Specify the size of the boards shared memory Receive Data buffer.
+By default the buffer is dynamically sized to use the maximum
+available shared memory.
+.It Fl t Ar tx-buf-size
+Specify the size of the boards shared memory Transmit Data buffer.
+By default the buffer is dynamically sized to use the maximum
+available shared memory.
+.It Fl B Ar boot-banner
+Enable the slave debug trace flag during download.
+This enables debug trace output from the firmware code.
+This trace is output on port 0 of the board,
+and the port is set to 9600 baud, 8 data bits, no parity and 1 stop bit.
+.It Fl b Ar unit-number
+Specify the unit (board) number to be downloaded. The default is to
+download board 0.
+.El
+.Pp
+.Nm Stlload
+would typically be run from
+.Pa /etc/rc.serial .
+.Sh FILES
+.Bl -tag -width /usr/libdata/stallion/2681.sys
+.It Pa /usr/libdata/stallion/cdk.sys
+firmware code to EasyConnection 8/64 class boards
+.It Pa /usr/libdata/stallion/2681.sys
+firmware code to ONboard and Brumby class boards
+.It Pa /dev/staliomem?
+driver board control device
+.Sh SEE ALSO
+.Xr stl 4 ,
+.Xr stli 4 ,
+.Xr stlstats 8
+.Sh HISTORY
+This program was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlload/stlload.c b/usr.sbin/stallion/stlload/stlload.c
new file mode 100644
index 0000000..283dcd7
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.c
@@ -0,0 +1,502 @@
+/*****************************************************************************/
+
+/*
+ * stlload.c -- stallion intelligent multiport down loader.
+ *
+ * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Greg Ungerer.
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: stlload.c,v 1.7 1997/10/20 12:51:29 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <machine/cdk.h>
+
+/*****************************************************************************/
+
+char *version = "1.0.0";
+char *defdevice = "/dev/staliomem%d";
+char *image = BOOTDIR "/cdk.sys";
+char *oldimage = BOOTDIR "/2681.sys";
+
+char *memdevice;
+char devstr[128];
+int brdnr = 0;
+int verbose = 0;
+int reset = 0;
+
+/*
+ * Define a local buffer for copying the image into the shared memory.
+ */
+#define BUFSIZE 4096
+
+char buf[BUFSIZE];
+
+/*
+ * Define the timeout length when waiting for slave to start up.
+ * The quantity is measured in seconds.
+ */
+#define TIMEOUT 5
+
+/*
+ * Set up a default feature area structure.
+ */
+cdkfeature_t feature = { 0, 0, ETYP_CDK, 0, 0, 0, 0, 0 };
+
+/*
+ * Have local copies of the board signatures ready.
+ */
+cdkecpsig_t ecpsig;
+cdkonbsig_t onbsig;
+
+/*****************************************************************************/
+
+/*
+ * Declare internal function prototypes here.
+ */
+static void usage(void);
+int ecpfindports(cdkecpsig_t *sigp);
+int onbfindports(cdkonbsig_t *sigp);
+int download(void);
+
+/*****************************************************************************/
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: stlload [-vhVR] [-i image-file] [-c control-device] [-r rx-buf-size]",
+" [-t tx-buf-size] [-B boot-banner] [-b unit-number]");
+ exit(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Given a boards signature determine how many ports it has. We need to
+ * know this to setup the slave feature arguments. This function is for
+ * ECP boards only.
+ */
+
+int ecpfindports(cdkecpsig_t *sigp)
+{
+ unsigned int id;
+ int bank, nrports;
+
+ nrports = 0;
+ for (bank = 0; (bank < 8); bank++) {
+ id = (unsigned int) sigp->panelid[bank];
+ if (id == 0xff)
+ break;
+ if ((id & 0x07) != bank)
+ break;
+ if (id & 0x20) {
+ nrports += 16;
+ bank++;
+ } else {
+ nrports += 8;
+ }
+ }
+
+ return(nrports);
+}
+
+/*****************************************************************************/
+
+/*
+ * Given a boards signature determine how many ports it has. We need to
+ * know this to setup the slave feature arguments. This function is for
+ * ONboards and Brumbys.
+ */
+
+int onbfindports(cdkonbsig_t *sigp)
+{
+ int i, nrports;
+
+ if (sigp->amask1) {
+ nrports = 32;
+ } else {
+ for (i = 0; (i < 16); i++) {
+ if (((sigp->amask0 << i) & 0x8000) == 0)
+ break;
+ }
+ nrports = i;
+ }
+
+ return(nrports);
+}
+
+/*****************************************************************************/
+
+/*
+ * Download an image to the slave board. There is a long sequence of
+ * things to do to get the slave running, but it is basically a simple
+ * process. Main things to do are: copy slave image into shared memory,
+ * start slave running and then read shared memory map.
+ */
+
+int download()
+{
+ unsigned char alivemarker;
+ time_t strttime;
+ int memfd, ifd;
+ int nrdevs, sigok, n;
+
+ if (verbose)
+ printf("Opening shared memory device %s\n", memdevice);
+ if ((memfd = open(memdevice, O_RDWR)) < 0) {
+ warn("failed to open memory device %s", memdevice);
+ return(-1);
+ }
+
+/*
+ * Before starting the download must tell driver that we are about to
+ * stop its slave. This is only important if it is already running.
+ * Once we have told the driver its stopped then do a hardware reset
+ * on it, to get it into a known state.
+ */
+ if (verbose)
+ printf("Stoping any current slave\n");
+ if (ioctl(memfd, STL_BSTOP, 0) < 0) {
+ warn("ioctl(STL_BSTOP)");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Reseting the board\n");
+ if (ioctl(memfd, STL_BRESET, 0) < 0) {
+ warn("ioctl(STL_BRESET)");
+ return(-1);
+ }
+ if (reset)
+ return(0);
+
+/*
+ * After reseting the board we need to send an interrupt to the older
+ * board types to get them to become active. Do that now.
+ */
+ if (verbose)
+ printf("Interrupting board to activate shared memory\n");
+ if (ioctl(memfd, STL_BINTR, 0) < 0) {
+ warn("ioctl(STL_BINTR)");
+ return(-1);
+ }
+ /*sleep(1);*/
+
+ if (verbose)
+ printf("Opening slave image file %s\n", image);
+ if ((ifd = open(image, O_RDONLY)) < 0) {
+ warn("failed to open image file %s", image);
+ return(-1);
+ }
+
+/*
+ * At this point get the signature of the board from the shared memory.
+ * Do a double check that it is a board we know about. We will also need
+ * to calculate the number of ports on this board (to use later).
+ */
+ sigok = 0;
+ if (verbose)
+ printf("Reading ROM signature from board\n");
+
+ if (lseek(memfd, CDK_SIGADDR, SEEK_SET) != CDK_SIGADDR) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (read(memfd, &ecpsig, sizeof(cdkecpsig_t)) < 0) {
+ warn("read of ROM signature failed");
+ return(-1);
+ }
+ if (ecpsig.magic == ECP_MAGIC) {
+ nrdevs = ecpfindports(&ecpsig);
+ if (nrdevs < 0)
+ return(-1);
+ sigok++;
+ }
+
+ if (lseek(memfd, CDK_SIGADDR, SEEK_SET) != CDK_SIGADDR) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (read(memfd, &onbsig, sizeof(cdkonbsig_t)) < 0) {
+ warn("read of ROM signature failed");
+ return(-1);
+ }
+ if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) &&
+ (onbsig.magic2 == ONB_MAGIC2) &&
+ (onbsig.magic3 == ONB_MAGIC3)) {
+ nrdevs = onbfindports(&onbsig);
+ if (nrdevs < 0)
+ return(-1);
+ sigok++;
+ }
+
+ if (! sigok) {
+ warnx("unknown signature from board");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Board signature reports %d ports\n", nrdevs);
+
+/*
+ * Start to copy the image file into shared memory. The first thing to
+ * do is copy the vector region in from shared memory address 0. We will
+ * then skip over the signature and feature area and start copying the
+ * actual image data and code from 4k upwards.
+ */
+ if (verbose)
+ printf("Copying vector table into shared memory\n");
+ if ((n = read(ifd, buf, CDK_SIGADDR)) < 0) {
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (lseek(memfd, 0, SEEK_SET) != 0) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (write(memfd, buf, n) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+ if (lseek(ifd, 0x1000, SEEK_SET) != 0x1000) {
+ warn("lseek(%x) failed on image file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (lseek(memfd, 0x1000, SEEK_SET) != 0x1000) {
+ warn("lseek(%x) failed on memory device", CDK_FEATADDR);
+ return(-1);
+ }
+
+/*
+ * Copy buffer size chunks of data from the image file into shared memory.
+ */
+ do {
+ if ((n = read(ifd, buf, BUFSIZE)) < 0) {
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (write(memfd, buf, n) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+ } while (n == BUFSIZE);
+
+ close(ifd);
+
+/*
+ * We need to down load the start up parameters for the slave. This is
+ * done via the feature area of shared memory. Think of the feature area
+ * as a way of passing "command line" arguments to the slave.
+ * FIX: should do something here to load "brdspec" as well...
+ */
+ feature.nrdevs = nrdevs;
+ if (verbose)
+ printf("Loading features into shared memory\n");
+ if (lseek(memfd, CDK_FEATADDR, SEEK_SET) != CDK_FEATADDR) {
+ warn("lseek(%x) failed on memory device", CDK_FEATADDR);
+ return(-1);
+ }
+ if (write(memfd, &feature, sizeof(cdkfeature_t)) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+/*
+ * Wait for board alive marker to be set. The slave image will set the
+ * byte at address CDK_RDYADDR to 0x13 after it has successfully started.
+ * If this doesn't happen we timeout and fail.
+ */
+ if (verbose)
+ printf("Setting alive marker to 0\n");
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ alivemarker = 0;
+ if (write(memfd, &alivemarker, 1) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+/*
+ * At this point the entire image is loaded into shared memory. To start
+ * it executiong we poke the board with an interrupt.
+ */
+ if (verbose)
+ printf("Interrupting board to start slave image\n");
+ if (ioctl(memfd, STL_BINTR, 0) < 0) {
+ warn("ioctl(STL_BINTR) failed");
+ return(-1);
+ }
+
+ strttime = time((time_t *) NULL);
+ if (verbose)
+ printf("Waiting for slave alive marker, time=%x timeout=%d\n",
+ strttime, TIMEOUT);
+ while (time((time_t *) NULL) < (strttime + TIMEOUT)) {
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ if (read(memfd, &alivemarker, 1) < 0){
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (alivemarker == CDK_ALIVEMARKER)
+ break;
+ }
+
+ if (alivemarker != CDK_ALIVEMARKER) {
+ warnx("slave image failed to start");
+ return(-1);
+ }
+
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ alivemarker = 0;
+ if (write(memfd, &alivemarker, 1) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Slave image started successfully\n");
+
+/*
+ * The last thing to do now is to get the driver started. Now that the
+ * slave is operational it must read in the memory map and gets its
+ * internal tables initialized.
+ */
+ if (verbose)
+ printf("Driver initializing host shared memory interface\n");
+ if (ioctl(memfd, STL_BSTART, 0) < 0) {
+ warn("ioctl(STL_BSTART) failed");
+ return(-1);
+ }
+
+ close(memfd);
+ return(0);
+}
+
+/*****************************************************************************/
+
+void main(int argc, char *argv[])
+{
+ struct stat statinfo;
+ int c;
+
+ while ((c = getopt(argc, argv, "hvVRB:i:b:c:t:r:")) != -1) {
+ switch (c) {
+ case 'V':
+ printf("stlload version %s\n", version);
+ exit(0);
+ break;
+ case 'B':
+ feature.banner = atol(optarg);
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'i':
+ image = optarg;
+ break;
+ case 'R':
+ reset++;
+ break;
+ case 'b':
+ brdnr = atoi(optarg);
+ break;
+ case 'c':
+ memdevice = optarg;
+ break;
+ case 't':
+ feature.txrqsize = atol(optarg);
+ break;
+ case 'r':
+ feature.rxrqsize = atol(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if (memdevice == (char *) NULL) {
+ if ((brdnr < 0) || (brdnr >= 8))
+ errx(1, "invalid board number %d specified", brdnr);
+ sprintf(devstr, defdevice, brdnr);
+ memdevice = &devstr[0];
+ if (verbose)
+ printf("Using shared memory device %s\n", memdevice);
+ }
+
+ if (verbose)
+ printf("Downloading image %s to board %d\n", image, brdnr);
+
+/*
+ * Check that the shared memory device exits and is a character device.
+ */
+ if (stat(memdevice, &statinfo) < 0)
+ errx(1, "memory device %s does not exist", memdevice);
+ if ((statinfo.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "memory device %s is not a char device", memdevice);
+
+ if (stat(image, &statinfo) < 0)
+ errx(1, "image file %s does not exist", image);
+
+/*
+ * All argument checking is now done. So lets get this show on the road.
+ */
+ if (download() < 0)
+ exit(1);
+ exit(0);
+}
+
+/*****************************************************************************/
diff --git a/usr.sbin/stallion/stlstats/Makefile b/usr.sbin/stallion/stlstats/Makefile
new file mode 100644
index 0000000..0789c07
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.4 1997/03/13 04:21:38 davidn Exp $
+
+PROG= stlstats
+MAN8= stlstats.8
+
+DPADD= ${LIBNCURSES} ${LIBMYTINFO}
+LDADD= -lncurses -lmytinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/stallion/stlstats/stlstats.8 b/usr.sbin/stallion/stlstats/stlstats.8
new file mode 100644
index 0000000..05dec3c
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/stlstats.8
@@ -0,0 +1,135 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 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.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STLSTATS 8 i386
+.Sh NAME
+.Nm stlstats
+.Nd "Stallion Technologies multiport serial statistics display"
+.Sh SYNOPSIS
+.Nm stlstats
+.Op Fl hVi
+.Op Fl c Ar control-device
+.Op Fl b Ar board-number
+.Op Fl p Ar port-number
+.Op Fl d Ar port-device
+.Sh DESCRIPTION
+.Nm Stlstats
+is used to display statistical information about the ports on Stallion
+Technologies multiport serial boards.
+.Pp
+.Nm Stlstats
+normally runs as a full screen menu driven application.
+A help line is displayed at the bottom of each screen with the valid
+input keys for this screen.
+.Pp
+Generally the digit keys ('0' through '9') specify the number of the
+device to display statistics for.
+Where digits alone are not enough to access all possible devices
+(for example on 16 port panels) then the first letters of the alphabet
+are used to access the remaining devices.
+The letters 'a' through 'f' are used to access devices 10 through 15.
+.Pp
+The 'q' key is always used to move back to the previous level screen.
+The escape key can also be used to move back to the previous screen.
+.Pp
+The first screen is a display of all ports on panel 0 of board 0.
+Values displayed on this screen are a summary of the information for
+each port. The statistics displayed are: driver and TTY state flags,
+termios flags (cflags, iflags, oflags, lflags), RS-232 signal values
+(as per TIOCM signal defines), total transmit and receive character
+counts.
+.Pp
+From this screen you can look at summary information
+about each panel and board installed in the system.
+Each board is accessed by the digit keys ('0' through '7'),
+while panels of each board can be cycled through using the 'n' key.
+.Pp
+The per port screen displays some detailed information about a
+particular port.
+This is accessed from the board screen using the 'p' key.
+The first port displayed will be port 0.
+To display other ports use the digit and alphabetic keys
+('0' through '9' and 'a' through 'f').
+This screen displays: driver and TTY state flags, hardware ID,
+termios flags (cflags, iflags, oflags, lflags),
+total transmitted and received character counts,
+current transmit and receive characters buffered,
+receiver error counts (overruns, parity, framing, lost),
+software flow control characters transmitted and received,
+hardware flow control actions taken,
+count of transmitted and received breaks,
+modem signal transitions and
+current RS-232 signal states.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl h
+Output usage information.
+.It Fl V
+Output version information.
+.It Fl i
+Output only the board type information.
+This output is useful for scripts or other programs that need to know
+a little bit about the board (for example an automated download script).
+.Nm Stlstats
+will not enter full screen interactive mode.
+.It Fl c Ar control-device
+Specify the board control device through which to gather port statistics.
+The default is
+.Pa /dev/staliomem0 .
+.It Fl b Ar board-number
+Specify the board number to display first.
+The default is to display board 0.
+.It Fl p Ar port-number
+Specify the port number to display first.
+.Nm Stlstats
+will go straight into the port display screen (bypassing board display)
+when this option is used.
+.It Fl d Ar port-device
+Specify the port special device file (the
+.Pa /dev/ttyXXX
+file) to
+display first.
+The board screen is bypassed and the port statistics screen is shown
+immediately on start up.
+.El
+.Sh FILES
+.Bl -tag -width /dev/staliomem0
+.It Pa /dev/staliomem0
+driver control device used for statistics collection
+.Sh SEE ALSO
+.Xr stl 4 ,
+.Xr stli 4 ,
+.Xr stlload 8
+.Sh HISTORY
+This program was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlstats/stlstats.c b/usr.sbin/stallion/stlstats/stlstats.c
new file mode 100644
index 0000000..b856df4
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/stlstats.c
@@ -0,0 +1,580 @@
+/*****************************************************************************/
+
+/*
+ * stlstats.c -- stallion intelligent multiport stats display.
+ *
+ * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Greg Ungerer.
+ * 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.
+ */
+
+/*****************************************************************************/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: stlstats.c,v 1.6 1997/10/20 12:51:34 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <machine/cdk.h>
+#include <machine/comstats.h>
+
+/*****************************************************************************/
+
+char *version = "1.0.0";
+char *defdevice = "/dev/staliomem0";
+
+char *ctrldevice;
+int ctrlfd;
+int displaybrdnr = 0;
+int displaypanelnr = 0;
+int displayportnr = 0;
+int displayportbank = 0;
+
+#define MAXBRDS 8
+#define MAXPORTS 32
+
+combrd_t brdstats;
+comstats_t stats[MAXPORTS];
+
+char *line = " ";
+
+/*****************************************************************************/
+
+/*
+ * Declare internal function prototypes here.
+ */
+static void usage(void);
+void useportdevice(char *devname);
+void localexit(int nr);
+void menuport();
+void displayport();
+void menuallports();
+void displayallports();
+void getallstats();
+void getbrdstats();
+void clearportstats();
+void clearallstats();
+
+/*****************************************************************************/
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: stlstats [-hVi] [-c control-device] [-b board-number]",
+ " [-p port-number] [-d port-device]");
+ exit(0);
+}
+
+/*****************************************************************************/
+
+void useportdevice(char *devname)
+{
+ struct stat statinfo;
+ int portnr, portcnt;
+ int i;
+
+ if (stat(devname, &statinfo) < 0)
+ errx(1, "port device %s does not exist", devname);
+ if ((statinfo.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "port device %s is not a char device", devname);
+
+ displaybrdnr = (statinfo.st_rdev & 0x00700000) >> 20;
+ portnr = (statinfo.st_rdev & 0x1f) |
+ ((statinfo.st_rdev & 0x00010000) >> 11);
+ getbrdstats();
+ if (brdstats.ioaddr == 0)
+ errx(1, "device %s does not exist", devname);
+
+ for (portcnt = 0, i = 0; (i < brdstats.nrpanels); i++) {
+ if ((portnr >= portcnt) &&
+ (portnr < (portcnt + brdstats.panels[i].nrports)))
+ break;
+ portcnt += brdstats.panels[i].nrports;
+ }
+ if (i >= brdstats.nrpanels)
+ errx(1, "device %s does not exist", devname);
+ displaypanelnr = i;
+ displayportnr = portnr - portcnt;
+ if (displayportnr >= 16)
+ displayportbank = 16;
+}
+
+/*****************************************************************************/
+
+/*
+ * Get the board stats for the current display board.
+ */
+
+void getbrdstats()
+{
+ brdstats.brd = displaybrdnr;
+ if (ioctl(ctrlfd, COM_GETBRDSTATS, &brdstats) < 0)
+ memset((combrd_t *) &brdstats, 0, sizeof(combrd_t));
+}
+
+/*****************************************************************************/
+
+/*
+ * Zero out stats for the current display port.
+ */
+
+void clearportstats()
+{
+ stats[displayportnr].brd = displaybrdnr;
+ stats[displayportnr].panel = displaypanelnr;
+ stats[displayportnr].port = displayportnr;
+ ioctl(ctrlfd, COM_CLRPORTSTATS, &stats[displayportnr]);
+}
+
+/*****************************************************************************/
+
+/*
+ * Zero out all stats for all ports on all boards.
+ */
+
+void clearallstats()
+{
+ int brdnr, panelnr, portnr;
+
+ for (brdnr = 0; (brdnr < MAXBRDS); brdnr++) {
+ for (panelnr = 0; (panelnr < COM_MAXPANELS); panelnr++) {
+ for (portnr = 0; (portnr < MAXPORTS); portnr++) {
+ stats[0].brd = brdnr;
+ stats[0].panel = panelnr;
+ stats[0].port = portnr;
+ ioctl(ctrlfd, COM_CLRPORTSTATS, &stats[0]);
+ }
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Get the stats for the current display board/panel.
+ */
+
+void getallstats()
+{
+ int i;
+
+ for (i = 0; (i < brdstats.panels[displaypanelnr].nrports); i++) {
+ stats[i].brd = displaybrdnr;
+ stats[i].panel = displaypanelnr;
+ stats[i].port = i;
+ if (ioctl(ctrlfd, COM_GETPORTSTATS, &stats[i]) < 0) {
+ warn("ioctl(COM_GETPORTSTATS) failed");
+ localexit(1);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Display the per ports stats screen.
+ */
+
+void displayport()
+{
+ mvprintw(0, 0, "STALLION SERIAL PORT STATISTICS");
+ mvprintw(2, 0,
+ "Board=%d Type=%d HwID=%02x State=%06x TotalPorts=%d",
+ displaybrdnr, brdstats.type, brdstats.hwid, brdstats.state,
+ brdstats.nrports);
+ mvprintw(3, 0, "Panel=%d HwID=%02x Ports=%d", displaypanelnr,
+ brdstats.panels[displaypanelnr].hwid,
+ brdstats.panels[displaypanelnr].nrports);
+
+ attron(A_REVERSE);
+ mvprintw(5, 0, line);
+ mvprintw(5, 0, "Port=%d ", displayportnr);
+ attroff(A_REVERSE);
+
+ mvprintw(7, 0, "STATE: State=%08x", stats[displayportnr].state);
+ mvprintw(7, 29, "Tty=%08x", stats[displayportnr].ttystate);
+ mvprintw(7, 47, "Flags=%08x", stats[displayportnr].flags);
+ mvprintw(7, 65, "HwID=%02x", stats[displayportnr].hwid);
+
+ mvprintw(8, 0, "CONFIG: Cflag=%08x", stats[displayportnr].cflags);
+ mvprintw(8, 29, "Iflag=%08x", stats[displayportnr].iflags);
+ mvprintw(8, 47, "Oflag=%08x", stats[displayportnr].oflags);
+ mvprintw(8, 65, "Lflag=%08x", stats[displayportnr].lflags);
+
+ mvprintw(10, 0, "TX DATA: Total=%d", stats[displayportnr].txtotal);
+ mvprintw(10, 29, "Buffered=%d ", stats[displayportnr].txbuffered);
+ mvprintw(11, 0, "RX DATA: Total=%d", stats[displayportnr].rxtotal);
+ mvprintw(11, 29, "Buffered=%d ", stats[displayportnr].rxbuffered);
+ mvprintw(12, 0, "RX ERRORS: Parity=%d", stats[displayportnr].rxparity);
+ mvprintw(12, 29, "Framing=%d", stats[displayportnr].rxframing);
+ mvprintw(12, 47, "Overrun=%d", stats[displayportnr].rxoverrun);
+ mvprintw(12, 65, "Lost=%d", stats[displayportnr].rxlost);
+
+ mvprintw(14, 0, "FLOW TX: Xoff=%d", stats[displayportnr].txxoff);
+ mvprintw(14, 29, "Xon=%d", stats[displayportnr].txxon);
+#if 0
+ mvprintw(14, 47, "CTSoff=%d", stats[displayportnr].txctsoff);
+ mvprintw(14, 65, "CTSon=%d", stats[displayportnr].txctson);
+#endif
+ mvprintw(15, 0, "FLOW RX: Xoff=%d", stats[displayportnr].rxxoff);
+ mvprintw(15, 29, "Xon=%d", stats[displayportnr].rxxon);
+ mvprintw(15, 47, "RTSoff=%d", stats[displayportnr].rxrtsoff);
+ mvprintw(15, 65, "RTSon=%d", stats[displayportnr].rxrtson);
+
+ mvprintw(17, 0, "OTHER: TXbreaks=%d",
+ stats[displayportnr].txbreaks);
+ mvprintw(17, 29, "RXbreaks=%d", stats[displayportnr].rxbreaks);
+ mvprintw(17, 47, "Modem=%d", stats[displayportnr].modem);
+
+ mvprintw(19, 0, "SIGNALS: DCD=%d DTR=%d CTS=%d RTS=%d "
+ "DSR=%d RI=%d",
+ (stats[displayportnr].signals & TIOCM_CD) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_DTR) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_CTS) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_RTS) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_DSR) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_RI) ? 1 : 0);
+
+ attron(A_REVERSE);
+ mvprintw(22, 0, line);
+ attroff(A_REVERSE);
+
+ mvprintw(24, 19, "(q=Quit,0123456789abcdef=Port,Z=ZeroStats)");
+ refresh();
+}
+
+/*****************************************************************************/
+
+/*
+ * Continuously update and display the per ports stats screen.
+ * Also checks for keyboard input, and processes it as appropriate.
+ */
+
+void menuport()
+{
+ int ch, done;
+
+ clear();
+ done = 0;
+
+ while ((ch = getch()) != 27) {
+ switch (ch) {
+ case ERR:
+ break;
+ case ' ':
+ refresh();
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ ch = (ch - 'a' + '0' + 10);
+ /* fall thru */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ch -= '0';
+ if (ch >= brdstats.panels[displaypanelnr].nrports) {
+ beep();
+ } else {
+ displayportnr = displayportbank + ch;
+ clear();
+ }
+ break;
+ case 'Z':
+ clearportstats();
+ clear();
+ break;
+ case 'q':
+ done = 1;
+ break;
+ default:
+ beep();
+ break;
+ }
+
+ if (done)
+ break;
+
+ getallstats();
+ displayport();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Display the all ports stats screen.
+ */
+
+void displayallports()
+{
+ int i, nrports, portnr;;
+
+ nrports = brdstats.panels[displaypanelnr].nrports;
+
+ mvprintw(0, 0, "STALLION SERIAL PORT STATISTICS");
+ mvprintw(2, 0, "Board=%d Type=%d HwID=%02x State=%06x TotalPorts=%d",
+ displaybrdnr, brdstats.type, brdstats.hwid, brdstats.state,
+ brdstats.nrports);
+ mvprintw(3, 0, "Panel=%d HwID=%02x Ports=%d", displaypanelnr,
+ brdstats.panels[displaypanelnr].hwid, nrports);
+
+ attron(A_REVERSE);
+ mvprintw(5, 0, "Port State Tty Flags Cflag Iflag Oflag Lflag "
+ "Sigs TX Total RX Total ");
+ attroff(A_REVERSE);
+
+ if (nrports > 0) {
+ if (nrports > 16)
+ nrports = 16;
+ portnr = displayportbank;
+ for (i = 0; (i < nrports); i++, portnr++) {
+ mvprintw((6 + i), 1, "%2d", portnr);
+ mvprintw((6 + i), 5, "%06x", stats[portnr].state);
+ mvprintw((6 + i), 12, "%06x", stats[portnr].ttystate);
+ mvprintw((6 + i), 19, "%08x", stats[portnr].flags);
+ mvprintw((6 + i), 28, "%05x", stats[portnr].cflags);
+ mvprintw((6 + i), 34, "%05x", stats[portnr].iflags);
+ mvprintw((6 + i), 40, "%05x", stats[portnr].oflags);
+ mvprintw((6 + i), 46, "%05x", stats[portnr].lflags);
+ mvprintw((6 + i), 52, "%04x", stats[portnr].signals);
+ mvprintw((6 + i), 58, "%10d", stats[portnr].txtotal);
+ mvprintw((6 + i), 69, "%10d", stats[portnr].rxtotal);
+ }
+ } else {
+ mvprintw(12, 32, "NO BOARD %d FOUND", displaybrdnr);
+ i = 16;
+ }
+
+ attron(A_REVERSE);
+ mvprintw((6 + i), 0, line);
+ attroff(A_REVERSE);
+
+ mvprintw(24, 14,
+ "(q=Quit,01234567=Board,n=Panels,p=Ports,Z=ZeroStats)");
+ refresh();
+}
+
+/*****************************************************************************/
+
+/*
+ * Continuously update and display the all ports stats screen.
+ * Also checks for keyboard input, and processes it as appropriate.
+ */
+
+void menuallports()
+{
+ int ch, done;
+
+ clear();
+ getbrdstats();
+
+ done = 0;
+ while ((ch = getch()) != 27) {
+ switch (ch) {
+ case ERR:
+ break;
+ case ' ':
+ refresh();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ displaybrdnr = ch - '0';
+ displaypanelnr = 0;
+ getbrdstats();
+ if (brdstats.state == 0)
+ beep();
+ clear();
+ break;
+ case 'n':
+ if (brdstats.panels[displaypanelnr].nrports > 16) {
+ if (displayportbank == 0) {
+ displayportbank = 16;
+ clear();
+ break;
+ }
+ }
+ displayportbank = 0;
+ displaypanelnr++;
+ if (displaypanelnr >= brdstats.nrpanels)
+ displaypanelnr = 0;
+ clear();
+ break;
+ case 'p':
+ if (brdstats.panels[displaypanelnr].nrports > 0) {
+ displayportnr = displayportbank;
+ menuport();
+ clear();
+ } else {
+ beep();
+ }
+ break;
+ case 'Z':
+ clearallstats();
+ clear();
+ break;
+ case 'q':
+ done = 1;
+ break;
+ default:
+ beep();
+ break;
+ }
+
+ if (done)
+ break;
+
+ getallstats();
+ displayallports();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * A local exit routine - shuts down curses before exiting.
+ */
+
+void localexit(int nr)
+{
+ refresh();
+ endwin();
+ exit(nr);
+}
+
+/*****************************************************************************/
+
+void main(int argc, char *argv[])
+{
+ struct stat statinfo;
+ int c, useport;
+ char *portdev;
+
+ ctrldevice = defdevice;
+ useport = 0;
+
+ while ((c = getopt(argc, argv, "hvVb:p:d:c:")) != -1) {
+ switch (c) {
+ case 'V':
+ printf("stlstats version %s\n", version);
+ exit(0);
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'b':
+ displaybrdnr = atoi(optarg);
+ break;
+ case 'p':
+ displaypanelnr = atoi(optarg);
+ break;
+ case 'd':
+ useport++;
+ portdev = optarg;
+ break;
+ case 'c':
+ ctrldevice = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+/*
+ * Check that the control device exits and is a character device.
+ */
+ if (stat(ctrldevice, &statinfo) < 0)
+ errx(1, "control device %s does not exist", ctrldevice);
+ if ((statinfo.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "control device %s is not a char device", ctrldevice);
+ if ((ctrlfd = open(ctrldevice, O_RDWR)) < 0)
+ errx(1, "open of %s failed", ctrldevice);
+
+/*
+ * Validate the panel number supplied by user. We do this now since we
+ * need to have parsed the entire command line first.
+ */
+ getbrdstats();
+ if (displaypanelnr >= brdstats.nrpanels)
+ displaypanelnr = 0;
+
+ if (useport)
+ useportdevice(portdev);
+
+/*
+ * Everything is now ready, lets go!
+ */
+ initscr();
+ cbreak();
+ halfdelay(5);
+ noecho();
+ clear();
+ if (useport) {
+ menuport();
+ clear();
+ }
+ menuallports();
+ refresh();
+ endwin();
+
+ close(ctrlfd);
+ printf("\n");
+ exit(0);
+}
+
+/*****************************************************************************/
diff --git a/usr.sbin/sysctl/Makefile b/usr.sbin/sysctl/Makefile
new file mode 100644
index 0000000..e0c6a22
--- /dev/null
+++ b/usr.sbin/sysctl/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= sysctl
+MAN8= sysctl.8
+
+BINDIR= /sbin
+NOSHARED?= yes
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sysctl/pathconf.c b/usr.sbin/sysctl/pathconf.c
new file mode 100644
index 0000000..07f786d
--- /dev/null
+++ b/usr.sbin/sysctl/pathconf.c
@@ -0,0 +1,240 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pathconf.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PC_NAMES { \
+ { 0, 0 }, \
+ { "link_max", CTLTYPE_INT }, \
+ { "max_canon", CTLTYPE_INT }, \
+ { "max_input", CTLTYPE_INT }, \
+ { "name_max", CTLTYPE_INT }, \
+ { "path_max", CTLTYPE_INT }, \
+ { "pipe_buf", CTLTYPE_INT }, \
+ { "chown_restricted", CTLTYPE_INT }, \
+ { "no_trunc", CTLTYPE_INT }, \
+ { "vdisable", CTLTYPE_INT }, \
+}
+#define PC_MAXID 10
+
+struct ctlname pcnames[] = PC_NAMES;
+char names[BUFSIZ];
+
+struct list {
+ struct ctlname *list;
+ int size;
+};
+struct list pclist = { pcnames, PC_MAXID };
+
+int Aflag, aflag, nflag, wflag, stdinflag;
+
+int findname __P((char *, char *, char**, struct list *));
+void listall __P((char *, struct list *));
+void parse __P((char *, char *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *path;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "Aan")) != -1) {
+ switch (ch) {
+
+ case 'A':
+ Aflag = 1;
+ break;
+
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'n':
+ nflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+ path = *argv++;
+ if (strcmp(path, "-") == 0)
+ stdinflag = 1;
+ argc--;
+ if (Aflag || aflag) {
+ listall(path, &pclist);
+ exit(0);
+ }
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(path, *argv, 1);
+ exit(0);
+}
+
+/*
+ * List all variables known to the system.
+ */
+void
+listall(path, lp)
+ char *path;
+ struct list *lp;
+{
+ int lvl2;
+
+ if (lp->list == 0)
+ return;
+ for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
+ if (lp->list[lvl2].ctl_name == 0)
+ continue;
+ parse(path, lp->list[lvl2].ctl_name, Aflag);
+ }
+}
+
+/*
+ * Parse a name into an index.
+ * Lookup and print out the attribute if it exists.
+ */
+void
+parse(pathname, string, flags)
+ char *pathname;
+ char *string;
+ int flags;
+{
+ int indx, value;
+ char *bufp, buf[BUFSIZ];
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((indx = findname(string, "top", &bufp, &pclist)) == -1)
+ return;
+ if (bufp) {
+ warnx("name %s in %s is unknown", *bufp, string);
+ return;
+ }
+ if (stdinflag)
+ value = fpathconf(0, indx);
+ else
+ value = pathconf(pathname, indx);
+ if (value == -1) {
+ if (flags == 0)
+ return;
+ switch (errno) {
+ case EOPNOTSUPP:
+ warnx("%s: value is not available", string);
+ return;
+ case ENOTDIR:
+ warnx("%s: specification is incomplete", string);
+ return;
+ case ENOMEM:
+ warnx("%s: type is unknown to this program", string);
+ return;
+ default:
+ warn("%s", string);
+ return;
+ }
+ }
+ if (!nflag)
+ fprintf(stdout, "%s = ", string);
+ fprintf(stdout, "%d\n", value);
+}
+
+/*
+ * Scan a list of names searching for a particular name.
+ */
+int
+findname(string, level, bufp, namelist)
+ char *string;
+ char *level;
+ char **bufp;
+ struct list *namelist;
+{
+ char *name;
+ int i;
+
+ if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
+ warnx("%s: incomplete specification", string);
+ return (-1);
+ }
+ for (i = 0; i < namelist->size; i++)
+ if (namelist->list[i].ctl_name != NULL &&
+ strcmp(name, namelist->list[i].ctl_name) == 0)
+ break;
+ if (i == namelist->size) {
+ warnx("%s level name %s in %s is invalid", level, name, string);
+ return (-1);
+ }
+ return (i);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: pathname [-n] variable ...",
+ " pathname [-n] -a",
+ " pathname [-n] -A");
+ exit(1);
+}
diff --git a/usr.sbin/sysctl/sysctl.8 b/usr.sbin/sysctl/sysctl.8
new file mode 100644
index 0000000..e959ecd
--- /dev/null
+++ b/usr.sbin/sysctl/sysctl.8
@@ -0,0 +1,243 @@
+.\" 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.
+.\"
+.\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93
+.\" $Id: sysctl.8,v 1.17 1998/09/29 02:01:06 jkoshy Exp $
+.\"
+.Dd September 23, 1994
+.Dt SYSCTL 8
+.Os
+.Sh NAME
+.Nm sysctl
+.Nd get or set kernel state
+.Sh SYNOPSIS
+.Nm sysctl
+.Op Fl bdn
+.Ar name ...
+.Nm sysctl
+.Op Fl bn
+.Fl w
+.Ar name=value ...
+.Nm sysctl
+.Op Fl bdn
+.Fl aAX
+.Sh DESCRIPTION
+The
+.Nm
+utility retrieves kernel state and allows processes with
+appropriate privilege to set kernel state.
+The state to be retrieved or set is described using a
+``Management Information Base'' (``MIB'') style name,
+described as a dotted set of components.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+List all the currently available string or integer values.
+.It Fl A
+List all the known MIB names including opaques.
+Those with string or integer values will be printed as with the
+.Fl a
+flag; for the opaque values,
+information about the format and the length is printed in addition the first
+few bytes is dumped in hex.
+.It Fl X
+Same as
+.Fl A
+except the entire value of opaque variables is hexdumped.
+.It Fl n
+Specify that the printing of the field name should be
+suppressed and that only its value should be output.
+This flag is useful for setting shell variables.
+For example, to save the pagesize in variable psize, use:
+.Bd -literal -offset indent -compact
+set psize=`sysctl -n hw.pagesize`
+.Ed
+.It Fl b
+Force the value of the variable(s) to be output in raw, binary
+format. No names are printed and no terminating newlines are output.
+This is mostly useful with a single variable.
+.It Fl d
+Display the description rather than the value of the requested
+variable(s).
+.It Fl w Ar name=value ...
+Set the MIB
+.Ar name
+to the new
+.Ar value .
+If just a MIB style
+.Ar name
+is given,
+the corresponding value is retrieved.
+.El
+.Pp
+The information available from
+.Nm
+consists of integers, strings, and opaques.
+.Nm Sysctl
+only knows about a couple of opaque types, and will resort to hexdumps
+for the rest.
+The opaque information is much more useful if retrieved by special
+purpose programs such as
+.Nm ps ,
+.Nm systat ,
+and
+.Nm netstat .
+.Pp
+The string and integer information is summarized below.
+For a detailed description of these variable see
+.Xr sysctl 3 .
+.Pp
+The changeable column indicates whether a process with appropriate
+privilege can change the value.
+.Bl -column net.inet.ip.forwardingxxxxxx integerxxx
+.It Sy Name Type Changeable
+.It kern.ostype string no
+.It kern.osrelease string no
+.It kern.osrevision integer no
+.It kern.version string no
+.It kern.maxvnodes integer yes
+.It kern.maxproc integer no
+.It kern.maxprocperuid integer yes
+.It kern.maxfiles integer yes
+.It kern.maxfilesperproc integer yes
+.It kern.argmax integer no
+.It kern.securelevel integer raise only
+.It kern.hostname string yes
+.It kern.hostid integer yes
+.It kern.clockrate struct no
+.It kern.posix1version integer no
+.It kern.ngroups integer no
+.It kern.job_control integer no
+.It kern.saved_ids integer no
+.It kern.boottime struct no
+.It kern.domainname string yes
+.It kern.update integer yes
+.It kern.osreldate string no
+.It kern.bootfile string yes
+.It kern.corefile string yes
+.It kern.logsigexit integer yes
+.It vm.loadavg struct no
+.It hw.machine string no
+.It hw.model string no
+.It hw.ncpu integer no
+.It hw.byteorder integer no
+.It hw.physmem integer no
+.It hw.usermem integer no
+.It hw.pagesize integer no
+.It hw.floatingpoint integer no
+.It hw.machine_arch string no
+.It machdep.console_device dev_t no
+.It machdep.adjkerntz integer yes
+.It machdep.disable_rtc_set integer yes
+.It user.cs_path string no
+.It user.bc_base_max integer no
+.It user.bc_dim_max integer no
+.It user.bc_scale_max integer no
+.It user.bc_string_max integer no
+.It user.coll_weights_max integer no
+.It user.expr_nest_max integer no
+.It user.line_max integer no
+.It user.re_dup_max integer no
+.It user.posix2_version integer no
+.It user.posix2_c_bind integer no
+.It user.posix2_c_dev integer no
+.It user.posix2_char_term integer no
+.It user.posix2_fort_dev integer no
+.It user.posix2_fort_run integer no
+.It user.posix2_localedef integer no
+.It user.posix2_sw_dev integer no
+.It user.posix2_upe integer no
+.It user.stream_max integer no
+.It user.tzname_max integer no
+.El
+.Sh EXAMPLES
+For example, to retrieve the maximum number of processes allowed
+in the system, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl kern.maxproc
+.Ed
+.Pp
+To set the maximum number of processes allowed
+per uid to 1000, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl -w kern.maxprocperuid=1000
+.Ed
+.Pp
+Information about the system clock rate may be obtained with:
+.Bd -literal -offset indent -compact
+sysctl kern.clockrate
+.Ed
+.Pp
+Information about the load average history may be obtained with:
+.Bd -literal -offset indent -compact
+sysctl vm.loadavg
+.Ed
+.Pp
+More variables than these exist, and the best and likely only place
+to search for their deeper meaning is undoubtedly the source where
+they are defined.
+.Sh FILES
+.Bl -tag -width <netinet/icmpXvar.h> -compact
+.It Pa <sys/sysctl.h>
+definitions for top level identifiers, second level kernel and hardware
+identifiers, and user level identifiers
+.It Pa <sys/socket.h>
+definitions for second level network identifiers
+.It Pa <sys/gmon.h>
+definitions for third level profiling identifiers
+.It Pa <vm/vm_param.h>
+definitions for second level virtual memory identifiers
+.It Pa <netinet/in.h>
+definitions for third level Internet identifiers and
+fourth level IP identifiers
+.It Pa <netinet/icmp_var.h>
+definitions for fourth level ICMP identifiers
+.It Pa <netinet/udp_var.h>
+definitions for fourth level UDP identifiers
+.El
+.Sh SEE ALSO
+.Xr sysctl 3
+.Sh BUGS
+.Nm Sysctl
+presently exploits an undocumented interface to the kernel
+sysctl facility to traverse the sysctl tree and to retrieve format
+and name information.
+This correct interface is being thought about for the time being.
+.Sh HISTORY
+.Nm Sysctl
+first appeared in
+.Bx 4.4 .
+.Pp
+In
+.Fx 2.2 ,
+.Nm
+was significantly remodeled.
diff --git a/usr.sbin/sysctl/sysctl.c b/usr.sbin/sysctl/sysctl.c
new file mode 100644
index 0000000..f264409
--- /dev/null
+++ b/usr.sbin/sysctl/sysctl.c
@@ -0,0 +1,502 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: sysctl.c,v 1.19 1998/11/08 19:27:43 phk Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int Aflag, aflag, bflag, dflag, nflag, wflag, Xflag;
+
+static int oidfmt(int *, int, char *, u_int *);
+static void parse(char *);
+static int show_var(int *, int);
+static int sysctl_all (int *oid, int len);
+static int name2oid(char *, int *);
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n",
+ "usage: sysctl [-bdn] variable ...",
+ " sysctl [-bn] -w variable=value ...",
+ " sysctl [-bdn] -a",
+ " sysctl [-bdn] -A",
+ " sysctl [-bdn] -X");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ setbuf(stdout,0);
+ setbuf(stderr,0);
+
+ while ((ch = getopt(argc, argv, "AabdnwX")) != -1) {
+ switch (ch) {
+ case 'A': Aflag = 1; break;
+ case 'a': aflag = 1; break;
+ case 'b': bflag = 1; break;
+ case 'd': dflag = 1; break;
+ case 'n': nflag = 1; break;
+ case 'w': wflag = 1; break;
+ case 'X': Xflag = Aflag = 1; break;
+ default: usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (wflag && (Aflag || aflag || dflag))
+ usage();
+ if (Aflag || aflag)
+ exit (sysctl_all(0, 0));
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(*argv++);
+ exit(0);
+}
+
+/*
+ * Parse a name into a MIB entry.
+ * Lookup and print out the MIB entry if it exists.
+ * Set a new value if requested.
+ */
+static void
+parse(char *string)
+{
+ int len, i, j;
+ void *newval = 0;
+ int intval, newsize = 0;
+ quad_t quadval;
+ int mib[CTL_MAXNAME];
+ char *cp, *bufp, buf[BUFSIZ];
+ u_int kind;
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((cp = strchr(string, '=')) != NULL) {
+ if (!wflag)
+ errx(2, "must specify -w to set variables");
+ *strchr(buf, '=') = '\0';
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ newval = cp;
+ newsize = strlen(cp);
+ } else {
+ if (wflag)
+ usage();
+ }
+ len = name2oid(bufp, mib);
+
+ if (len < 0)
+ errx(1, "unknown oid '%s'", bufp);
+
+ if (oidfmt(mib, len, 0, &kind))
+ err(1, "couldn't find format of oid '%s'", bufp);
+
+ if (!wflag) {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE) {
+ sysctl_all(mib, len);
+ } else {
+ i = show_var(mib, len);
+ if (!i && !bflag)
+ putchar('\n');
+ }
+ } else {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE)
+ errx(1, "oid '%s' isn't a leaf node", bufp);
+
+ if (!(kind&CTLFLAG_WR))
+ errx(1, "oid '%s' is read only", bufp);
+
+ switch (kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ intval = atoi(newval);
+ newval = &intval;
+ newsize = sizeof intval;
+ break;
+ break;
+ case CTLTYPE_STRING:
+ break;
+ case CTLTYPE_QUAD:
+ break;
+ sscanf(newval, "%qd", &quadval);
+ newval = &quadval;
+ newsize = sizeof quadval;
+ break;
+ default:
+ errx(1, "oid '%s' is type %d,"
+ " cannot set that", bufp,
+ kind & CTLTYPE);
+ }
+
+ i = show_var(mib, len);
+ if (sysctl(mib, len, 0, 0, newval, newsize) == -1) {
+ if (!i && !bflag)
+ putchar('\n');
+ switch (errno) {
+ case EOPNOTSUPP:
+ errx(1, "%s: value is not available",
+ string);
+ case ENOTDIR:
+ errx(1, "%s: specification is incomplete",
+ string);
+ case ENOMEM:
+ errx(1, "%s: type is unknown to this program",
+ string);
+ default:
+ warn("%s", string);
+ return;
+ }
+ }
+ if (!bflag)
+ printf(" -> ");
+ i = nflag;
+ nflag = 1;
+ j = show_var(mib, len);
+ if (!j && !bflag)
+ putchar('\n');
+ nflag = i;
+ }
+}
+
+/* These functions will dump out various interesting structures. */
+
+static int
+S_clockinfo(int l2, void *p)
+{
+ struct clockinfo *ci = (struct clockinfo*)p;
+ if (l2 != sizeof *ci)
+ err(1, "S_clockinfo %d != %d", l2, sizeof *ci);
+ printf("{ hz = %d, tick = %d, tickadj = %d, profhz = %d, stathz = %d }",
+ ci->hz, ci->tick, ci->tickadj, ci->profhz, ci->stathz);
+ return (0);
+}
+
+static int
+S_loadavg(int l2, void *p)
+{
+ struct loadavg *tv = (struct loadavg*)p;
+
+ if (l2 != sizeof *tv)
+ err(1, "S_loadavg %d != %d", l2, sizeof *tv);
+
+ printf("{ %.2f %.2f %.2f }",
+ (double)tv->ldavg[0]/(double)tv->fscale,
+ (double)tv->ldavg[1]/(double)tv->fscale,
+ (double)tv->ldavg[2]/(double)tv->fscale);
+ return (0);
+}
+
+static int
+S_timeval(int l2, void *p)
+{
+ struct timeval *tv = (struct timeval*)p;
+ time_t tv_sec;
+ char *p1, *p2;
+
+ if (l2 != sizeof *tv)
+ err(1, "S_timeval %d != %d", l2, sizeof *tv);
+ printf("{ sec = %ld, usec = %ld } ",
+ tv->tv_sec, tv->tv_usec);
+ tv_sec = tv->tv_sec;
+ p1 = strdup(ctime(&tv_sec));
+ for (p2=p1; *p2 ; p2++)
+ if (*p2 == '\n')
+ *p2 = '\0';
+ fputs(p1, stdout);
+ return (0);
+}
+
+static int
+T_dev_t(int l2, void *p)
+{
+ dev_t *d = (dev_t *)p;
+ if (l2 != sizeof *d)
+ err(1, "T_dev_T %d != %d", l2, sizeof *d);
+ printf("{ major = %d, minor = %d }",
+ major(*d), minor(*d));
+ return (0);
+}
+
+/*
+ * These functions uses a presently undocumented interface to the kernel
+ * to walk the tree and get the type so it can print the value.
+ * This interface is under work and consideration, and should probably
+ * be killed with a big axe by the first person who can find the time.
+ * (be aware though, that the proper interface isn't as obvious as it
+ * may seem, there are various conflicting requirements.
+ */
+
+static int
+name2oid(char *name, int *oidp)
+{
+ int oid[2];
+ int i;
+ size_t j;
+
+ oid[0] = 0;
+ oid[1] = 3;
+
+ j = CTL_MAXNAME * sizeof (int);
+ i = sysctl(oid, 2, oidp, &j, name, strlen(name));
+ if (i < 0)
+ return i;
+ j /= sizeof (int);
+ return (j);
+}
+
+static int
+oidfmt(int *oid, int len, char *fmt, u_int *kind)
+{
+ int qoid[CTL_MAXNAME+2];
+ u_char buf[BUFSIZ];
+ int i;
+ size_t j;
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, len * sizeof(int));
+
+ j = sizeof buf;
+ i = sysctl(qoid, len + 2, buf, &j, 0, 0);
+ if (i)
+ err(1, "sysctl fmt %d %d %d", i, j, errno);
+
+ if (kind)
+ *kind = *(u_int *)buf;
+
+ if (fmt)
+ strcpy(fmt, (char *)(buf + sizeof(u_int)));
+ return 0;
+}
+
+/*
+ * This formats and outputs the value of one variable
+ *
+ * Returns zero if anything was actually output.
+ * Returns one if didn't know what to do with this.
+ * Return minus one if we had errors.
+ */
+
+static int
+show_var(int *oid, int nlen)
+{
+ u_char buf[BUFSIZ], *val, *p;
+ char name[BUFSIZ], descr[BUFSIZ], *fmt;
+ int qoid[CTL_MAXNAME+2];
+ int i;
+ size_t j, len;
+ u_int kind;
+ int (*func)(int, void *) = 0;
+
+ qoid[0] = 0;
+ memcpy(qoid + 2, oid, nlen * sizeof(int));
+
+ qoid[1] = 1;
+ j = sizeof name;
+ i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl name %d %d %d", i, j, errno);
+
+ if (dflag) {
+ qoid[1] = 5;
+ j = sizeof descr;
+ i = sysctl(qoid, nlen + 2, descr, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl name %d %d %d", i, j, errno);
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%s", descr[0] ? descr : "[no description]");
+ return (0);
+ }
+
+ /* find an estimate of how much we need for this var */
+ j = 0;
+ i = sysctl(oid, nlen, 0, &j, 0, 0);
+ j += j; /* we want to be sure :-) */
+
+ val = alloca(j);
+ len = j;
+ i = sysctl(oid, nlen, val, &len, 0, 0);
+ if (i || !len)
+ return (1);
+
+ if (bflag) {
+ fwrite(val, 1, len, stdout);
+ return (0);
+ }
+
+ qoid[1] = 4;
+ j = sizeof buf;
+ i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl fmt %d %d %d", i, j, errno);
+
+ kind = *(u_int *)buf;
+
+ fmt = (char *)(buf + sizeof(u_int));
+
+ p = val;
+ switch (*fmt) {
+ case 'A':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%s", p);
+ return (0);
+
+ case 'I':
+ if (!nflag)
+ printf("%s: ", name);
+ val = "";
+ while (len >= sizeof(int)) {
+ printf("%s%d", val, *(int *)p);
+ val = " ";
+ len -= sizeof (int);
+ p += sizeof (int);
+ }
+ return (0);
+
+ case 'L':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%ld", *(long *)p);
+ return (0);
+
+ case 'P':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%p", *(void **)p);
+ return (0);
+
+ case 'T':
+ case 'S':
+ i = 0;
+ if (!strcmp(fmt, "S,clockinfo")) func = S_clockinfo;
+ else if (!strcmp(fmt, "S,timeval")) func = S_timeval;
+ else if (!strcmp(fmt, "S,loadavg")) func = S_loadavg;
+ else if (!strcmp(fmt, "T,dev_t")) func = T_dev_t;
+ if (func) {
+ if (!nflag)
+ printf("%s: ", name);
+ return ((*func)(len, p));
+ }
+ /* FALL THROUGH */
+ default:
+ if (!Aflag)
+ return (1);
+ if (!nflag)
+ printf("%s: ", name);
+ printf("Format:%s Length:%d Dump:0x", fmt, len);
+ while (len--) {
+ printf("%02x", *p++);
+ if (Xflag || p < val+16)
+ continue;
+ printf("...");
+ break;
+ }
+ return (0);
+ }
+ return (1);
+}
+
+static int
+sysctl_all (int *oid, int len)
+{
+ int name1[22], name2[22];
+ int i, j;
+ size_t l1, l2;
+
+ name1[0] = 0;
+ name1[1] = 2;
+ l1 = 2;
+ if (len) {
+ memcpy(name1+2, oid, len*sizeof (int));
+ l1 += len;
+ } else {
+ name1[2] = 1;
+ l1++;
+ }
+ while (1) {
+ l2 = sizeof name2;
+ j = sysctl(name1, l1, name2, &l2, 0, 0);
+ if (j < 0)
+ if (errno == ENOENT)
+ return 0;
+ else
+ err(1, "sysctl(getnext) %d %d", j, l2);
+
+ l2 /= sizeof (int);
+
+ if (l2 < len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (name2[i] != oid[i])
+ return 0;
+
+ i = show_var(name2, l2);
+ if (!i && !bflag)
+ putchar('\n');
+
+ memcpy(name1+2, name2, l2*sizeof (int));
+ l1 = 2 + l2;
+ }
+}
diff --git a/usr.sbin/sysinstall/Makefile b/usr.sbin/sysinstall/Makefile
new file mode 100644
index 0000000..0be2955
--- /dev/null
+++ b/usr.sbin/sysinstall/Makefile
@@ -0,0 +1,85 @@
+PROG= sysinstall
+MAN8= sysinstall.8
+
+BINDIR=/stand
+NOSHARED=YES
+
+CLEANFILES+= makedevs.c rtermcap rtermcap.tmp dumpnlist
+CLEANFILES+= keymap.tmp keymap.h
+
+.PATH: ${.CURDIR}/../disklabel ${.CURDIR}/../../usr.bin/cksum
+
+SRCS= anonFTP.c cdrom.c command.c config.c devices.c kget.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c floppy.c \
+ ftp.c globals.c index.c install.c installUpgrade.c keymap.c \
+ label.c lndir.c main.c makedevs.c media.c menus.c misc.c mouse.c \
+ msg.c network.c nfs.c options.c package.c register.c system.c \
+ tape.c tcpip.c termcap.c ufs.c user.c variable.c wizard.c \
+ keymap.h
+
+CFLAGS+= -Wall -I${.CURDIR}/../../gnu/lib/libdialog -I${.OBJDIR}
+CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -DUC_PRIVATE -DKERN_NO_SYMBOLS
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lmytinfo -lutil -ldisk -lftpio
+
+makedevs.c: Makefile rtermcap keymap.h
+ rm -f makedevs.tmp
+ echo '#include <sys/types.h>' > makedevs.tmp
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.tmp
+.if ${MACHINE_ARCH} != "alpha"
+ file2c 'u_char boot0[] = {' '};' < /boot/boot0 >> makedevs.tmp
+.endif
+ mv makedevs.tmp makedevs.c
+
+rtermcap: ${.CURDIR}/rtermcap.c
+ ${CC} -o rtermcap ${.CURDIR}/rtermcap.c -ltermcap
+
+
+KEYMAPS= be.iso br275.iso danish.iso finnish.iso fr.iso \
+ german.iso hr.iso hu.iso2.101keys it.iso icelandic.iso jp.106 \
+ norwegian.iso pl_PL.ISO_8859-2 pt.iso ru.koi8-r si.iso \
+ spanish.iso swedish.iso swissfrench.iso swissgerman.iso uk.iso \
+ us.dvorak us.iso
+
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ 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..59d4859
--- /dev/null
+++ b/usr.sbin/sysinstall/anonFTP.c
@@ -0,0 +1,316 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: anonFTP.c,v 1.23 1997/04/02 12:07:18 jkh Exp $
+ *
+ * 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 },
+ { NULL },
+};
+
+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, pwline);
+ fclose(fptr);
+ msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
+ vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
+ return DITEM_SUCCESS;
+}
+
+/* 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];
+
+ /* 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!!");
+ 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);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (cancel)
+ return DITEM_FAILURE;
+ return DITEM_SUCCESS;
+}
+
+int
+configAnonFTP(dialogMenuItem *self)
+{
+ int i;
+
+ /* Be optimistic */
+ i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ i = anonftpOpenDialog();
+ if (DITEM_STATUS(i) != DITEM_SUCCESS) {
+ msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
+ return i | DITEM_RESTORE;
+ }
+
+ /*** 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("cp /etc/pwd.db %s/etc && chmod 444 %s/etc/pwd.db", tconf.homedir, tconf.homedir);
+ vsystem("cp /etc/passwd %s/etc && chmod 444 %s/etc/passwd", tconf.homedir, tconf.homedir);
+ vsystem("cp /etc/group %s/etc && 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];
+ dialog_clear();
+ 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..0115202
--- /dev/null
+++ b/usr.sbin/sysinstall/cdrom.c
@@ -0,0 +1,164 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: cdrom.c,v 1.44 1998/12/22 12:31:24 jkh Exp $
+ *
+ * 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 char mountpoint[] = "/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 CD in your drive looks more like an Audio CD than a FreeBSD release.");
+ return FALSE;
+ }
+ else if (errno != EBUSY) {
+ 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 CD currently in the drive is either not a FreeBSD\n"
+ "CD 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 CD anyway?") != 0) {
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ return FALSE;
+ }
+ else {
+ readInfo = FALSE;
+ bogusCDOK = TRUE;
+ }
+ }
+
+ if (readInfo &&
+ (!(cd_attr = read_props(string_concat(mountpoint, "/cdrom.inf"))) ||
+ !(cp = property_find(cd_attr, "CD_VERSION")) || (strcmp(cp, variable_get(VAR_RELNAME)) && strcmp("none", variable_get(VAR_RELNAME))))) {
+ if (!cp) {
+ msgConfirm("Unable to find a %s/cdrom.inf file.\n"
+ "Either this is not a FreeBSD CDROM, 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 (!bogusCDOK) {
+ msgConfirm("Warning: The version of the FreeBSD CD 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 CD before selecting it as your\n"
+ "installation media.", cp, variable_get(VAR_RELNAME));
+
+ if (msgYesNo("Would you like to try and use this CDROM anyway?") != 0) {
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ properties_free(cd_attr);
+ return FALSE;
+ }
+ else
+ bogusCDOK = TRUE;
+ }
+ }
+ 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 (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the CDROM 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..b14f913
--- /dev/null
+++ b/usr.sbin/sysinstall/command.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * 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", commandStack[i]->cmds[j].ptr);
+ ret = vsystem((char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n", 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("%x: Execute(%s, %s)", func, commandStack[i]->key, commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %x 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..74cd515
--- /dev/null
+++ b/usr.sbin/sysinstall/config.c
@@ -0,0 +1,741 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: config.c,v 1.123 1999/02/14 07:35:27 jkh Exp $
+ *
+ * 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>
+
+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)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat)
+ return "msdos";
+ 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";
+ }
+ 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->private_data)
+ 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\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+
+ /* And finally, a /proc. */
+ fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
+ Mkdir("/proc");
+
+ 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 = (Boolean)variable_get(VAR_NAMESERVER);
+
+ 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;
+}
+
+void
+configRC_conf(void)
+{
+ FILE *rcSite;
+ Variable *v;
+ int write_header;
+ static int did_marker = 0;
+
+ write_header = !file_readable("/etc/rc.conf");
+ rcSite = fopen("/etc/rc.conf", "a");
+ if (!rcSite)
+ return;
+ if (write_header) {
+ fprintf(rcSite, "# This file now contains just the overrides from /etc/defaults/rc.conf\n");
+ fprintf(rcSite, "# please make all changes to this file.\n\n");
+ }
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ if (v->dirty) {
+ if (!did_marker) {
+ fprintf(rcSite, "# -- sysinstall generated deltas -- #\n");
+ did_marker = 1;
+ }
+ fprintf(rcSite, "%s=\"%s\"\n", v->name, v->value);
+ v->dirty = 0;
+ }
+ }
+ fclose(rcSite);
+}
+
+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) | DITEM_RESTORE;
+}
+
+int
+configRegister(dialogMenuItem *self)
+{
+ return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
+}
+
+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 | DITEM_RESTORE;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ dialog_clear();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+configXEnvironment(dialogMenuItem *self)
+{
+ char *config, *execfile;
+ char *moused;
+
+tryagain:
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ if (file_readable("/var/run/ld.so.hints"))
+ systemExecute("/sbin/ldconfig -m /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ else
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ config = variable_get(VAR_XF86_CONFIG);
+ if (!config)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ execfile = string_concat("/usr/X11R6/bin/", config);
+ if (file_executable(execfile)) {
+ dialog_clear_norefresh();
+ 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;
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ dialog_clear();
+ 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.");
+ dialog_clear();
+ systemExecute(execfile);
+ if (!file_readable("/etc/XF86Config") && !msgYesNo("The XFree86 configuration process seems to have\nfailed. Would you like to try again?"))
+ goto tryagain;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("The XFree86 setup utility you chose does not appear to be installed!\n"
+ "Please install this before attempting to configure XFree86.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+}
+
+int
+configResolv(dialogMenuItem *ditem)
+{
+ FILE *fp;
+ char *cp, *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);
+ 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, "127.0.0.1\t\tlocalhost.%s localhost\n", dp);
+ else
+ fprintf(fp, "127.0.0.1\t\tlocalhost\n");
+ /* Now the host entries, if applicable */
+ if (cp && cp[0] != '0' && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\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(variable_get(VAR_GATED_PKG)) != 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);
+ }
+ }
+ return ret | DITEM_RESTORE;
+}
+
+int
+configPackages(dialogMenuItem *self)
+{
+ static PkgNode top, plist;
+ static Boolean index_initted = FALSE;
+ PkgNodePtr tmp;
+ FILE *fp;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ if (!index_initted) {
+ msgNotify("Attempting to fetch packages/INDEX file from selected media.");
+ fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
+ if (!fp) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n"
+ "This may be because the packages collection is not available at\n"
+ "on the distribution media you've chosen (most likely an FTP site\n"
+ "without the packages collection mirrored). Please verify media\n"
+ "(or path to media) and try again. If your local site does not\n"
+ "carry the packages collection, then we recommend either a CD\n"
+ "distribution or the master distribution on ftp.freebsd.org.");
+ mediaDevice->shutdown(mediaDevice);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ 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);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ fclose(fp);
+ index_sort(&top);
+ index_initted = TRUE;
+ }
+ 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) {
+ index_extract(mediaDevice, &top, &plist);
+ break;
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ 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 | DITEM_RESTORE;
+}
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (variable_get(VAR_PCNFSD))
+ variable_unset(VAR_PCNFSD);
+ else {
+ ret = package_add(variable_get(VAR_PCNFSD_PKG));
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES", 0);
+ variable_set2("mountd_flags", "-n", 1);
+ }
+ }
+ return ret;
+}
+
+int
+configNFSServer(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ /* 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 '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ restorescr(w);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES", 1);
+ }
+ 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;
+}
diff --git a/usr.sbin/sysinstall/dev2c.sh b/usr.sbin/sysinstall/dev2c.sh
new file mode 100644
index 0000000..929e9e7
--- /dev/null
+++ b/usr.sbin/sysinstall/dev2c.sh
@@ -0,0 +1,80 @@
+:
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> 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$
+#
+# During installation, we suffer badly of we have to run MAKEDEV. MAKEDEV
+# need sh, ln, chown, mknod, awk, rm, test and probably emacs too when
+# we come down to it. So instead this script will make a C-procedure which
+# makes all the B & C nodes of a specified directory.
+#
+# Poul-Henning
+
+(cd $1; ls -li ) | sed 's/,//' | awk '
+BEGIN {
+ while (getline < "/etc/passwd") {
+ split($0,a,":")
+ uid[a[1]] = a[3]
+ }
+ while (getline < "/etc/group") {
+ split($0,a,":")
+ gid[a[1]] = a[3]
+ }
+ printf("/*\n");
+ printf(" * This file is generated from the contents of /dev\n");
+ printf(" */\n");
+ printf("#define CHK(foo) {i = foo;}\n");
+ printf("#include <unistd.h>\n");
+ printf("#include <sys/types.h>\n");
+ printf("#include <sys/stat.h>\n");
+ printf("int makedevs()\n{\n\tint i=0;\n");
+ }
+ {
+ printf ("/* %s */\n",$0)
+ $4 = uid[$4]
+ $5 = gid[$5]
+ if (substr($2,1,1) == "b") {
+ k="S_IFBLK"
+ } else if (substr($2,1,1) == "c") {
+ k="S_IFCHR"
+ } else if (substr($2,1,1) == "d") {
+ next
+ } else if (substr($2,1,1) == "-") {
+ next
+ } else {
+ next
+ }
+ m = 0;
+ if (substr($2,2,1) == "r") m += 400;
+ if (substr($2,3,1) == "w") m += 200;
+ if (substr($2,4,1) == "x") m += 100;
+ if (substr($2,5,1) == "r") m += 40;
+ if (substr($2,6,1) == "w") m += 20;
+ if (substr($2,7,1) == "x") m += 10;
+ if (substr($2,8,1) == "r") m += 4;
+ if (substr($2,9,1) == "w") m += 2;
+ if (substr($2,10,1) == "x") m += 1;
+
+ if (a[$1] != 0) {
+ printf ("\tCHK(link(\"%s\",\"%s\"));\n", \
+ a[$1],$11)
+ } else {
+ printf ("\tCHK(mknod(\"%s\",%s,makedev(%d,%d)));\n", \
+ $11, k, $6, $7)
+ printf ("\tCHK(chmod(\"%s\",0%d));\n", \
+ $11, m)
+ printf ("\tCHK(chown(\"%s\",%d,%d));\n", \
+ $11, $4,$5)
+ a[$1] = $11
+ }
+ }
+END {
+ printf("\treturn i;\n}\n");
+ }
+'
diff --git a/usr.sbin/sysinstall/devices.c b/usr.sbin/sysinstall/devices.c
new file mode 100644
index 0000000..bf4af72
--- /dev/null
+++ b/usr.sbin/sysinstall/devices.c
@@ -0,0 +1,531 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: devices.c,v 1.89 1999/04/06 08:25:52 jkh Exp $
+ *
+ * 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;
+ char dev_type;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd%dc", "SCSI CDROM drive", 6, 2, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "mcd%da", "Mitsumi (old model) CDROM drive", 7, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "scd%da", "Sony CDROM drive - CDU31/33A type", 16, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "matcd%da", "Matsushita CDROM ('sound blaster' type)", 17, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_CDROM, "wcd%dc", "ATAPI IDE CDROM", 19, 2, 8, 4, 'b' },
+ { DEVICE_TYPE_TAPE, "rsa%d", "SCSI tape drive", 14, 0, 16, 4, 'c' },
+ { DEVICE_TYPE_TAPE, "rwt%d", "Wangtek tape drive", 10, 0, 1, 4, 'c' },
+ { DEVICE_TYPE_DISK, "da%d", "SCSI disk device", 4, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rda%d", "SCSI disk device", 13, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "wd%d", "IDE/ESDI/MFM/ST506 disk device", 0, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rwd%d", "IDE/ESDI/MFM/ST506 disk device", 3, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "fla%d", "DiskOnChip2000 Flash device", 28, 65538, 8, 16, 'b' },
+ { DEVICE_TYPE_DISK, "rfla%d", "DiskOnChip2000 Flash devicee", 101, 65538, 8, 16, 'c' },
+ { DEVICE_TYPE_DISK, "wfd%d", "ATAPI floppy device", 1, 65538, 8, 4, 'b' },
+ { DEVICE_TYPE_DISK, "rwfd%d", "ATAPI floppy device", 87, 65538, 8, 4, 'c' },
+ { DEVICE_TYPE_FLOPPY, "fd%d", "floppy drive unit A", 2, 0, 64, 4, 'b' },
+ { DEVICE_TYPE_FLOPPY, "wfd%d", "ATAPI floppy drive unit A", 1, 0, 8, 4, 'b' },
+ { DEVICE_TYPE_FLOPPY, "worm%d", "SCSI optical disk / CDR", 23, 0, 1, 4, 'b' },
+ { DEVICE_TYPE_NETWORK, "ax", "ASIX AX88140A PCI ethernet card" },
+ { 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, "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", "WD/SMC 80xx; Novell NE1000/2000; 3Com 3C503 card" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 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, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "mx", "Macronix 98713/98715/98725 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "pn", "Lite-On 82168/82169 PNIC PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "rl", "RealTek 8129/8139 PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "tx", "SMC 9432TX 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, "vx", "3COM 3c590 / 3c595 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "wb", "Winbond W89C840F PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "xl", "3COM 3c90x / 3c90xB PCI ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ze", "IBM/National Semiconductor PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "zp", "3Com Etherlink III PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "cuaa%d", "%s on device %s (COM%d)", 28, 128, 1, 16, 'c' },
+ { DEVICE_TYPE_NETWORK, "lp", "Parallel Port IP (PLIP) peer connection" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { 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);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0)
+ return fd;
+ m = 0640;
+ if (dev.dev_type == 'c')
+ m |= S_IFCHR;
+ else
+ m |= S_IFBLK;
+ d = makedev(dev.major, dev.minor + (i * dev.delta));
+ fail = mknod(try, m, d);
+ fd = open(try, O_RDONLY);
+ if (fd >= 0)
+ 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);
+ 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++) {
+ Devices[i]->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) {
+ msgConfirm("ifconfig: socket");
+ goto skipif; /* Jump over network iface probing */
+ }
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ msgConfirm("ifconfig (SIOCGIFCONF)");
+ goto skipif; /* Jump over network iface probing */
+ }
+ 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)
+ continue;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo", 2))
+ continue;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ continue;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ continue;
+ }
+ /* 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);
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ msgConfirm("ifconfig: socket");
+ continue;
+ }
+ 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));
+ }
+
+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);
+ 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);
+ 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;
+ if (device_names[i].dev_type == 'c')
+ m |= S_IFCHR;
+ else
+ m |= S_IFBLK;
+ 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);
+ 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);
+ 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;
+
+ d = Open_Disk(names[i]);
+ if (!d)
+ msgFatal("Unable to open disk %s", names[i]);
+
+ deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE,
+ dummyInit, dummyGet, dummyShutdown, d);
+ 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 == 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;
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+}
+
+/* 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/disks.c b/usr.sbin/sysinstall/disks.c
new file mode 100644
index 0000000..11b6470a
--- /dev/null
+++ b/usr.sbin/sysinstall/disks.c
@@ -0,0 +1,814 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: disks.c,v 1.108 1999/01/08 00:14:21 jkh Exp $
+ *
+ * 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 <sys/stat.h>
+#include <sys/disklabel.h>
+
+/* 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 void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ int 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 int Total;
+
+static void
+print_chunks(Disk *d)
+{
+ int row;
+ int i;
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %d/%d/%d 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 = %lu sectors",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect);
+ mvprintw(3, 0, "%10s %10s %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, chunk_info[i]->size,
+ 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 B = Bad Block Scan C = Create Slice");
+ mvprintw(17, 0, "D = Delete Slice G = Set Drive Geometry S = Set Bootable");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 48, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static u_char *
+getBootMgr(char *dname)
+{
+#ifndef __alpha__ /* only meaningful on x86 */
+ extern u_char mbr[], boot0[];
+ 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:
+ return boot0;
+
+ case 1:
+ return mbr;
+
+ case 2:
+ default:
+ break;
+ }
+ }
+#endif
+ return NULL;
+}
+
+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;
+}
+
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ Boolean chunking;
+ char *msg = NULL;
+ u_char *mbrContents;
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+
+ 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);
+ 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':
+#ifdef __alpha__
+ rv = 1;
+#else /* The rest is only relevant on x86 */
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ 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 'B':
+ if (chunk_info[current_chunk]->type != freebsd)
+ msg = "Can only scan for bad blocks in FreeBSD slice.";
+ else if (strncmp(d->name, "sd", 2) ||
+ strncmp(d->name, "da", 2) ||
+ !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
+ "Are you sure you want to do this on a SCSI disk?")) {
+ if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
+ chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
+ else
+ chunk_info[current_chunk]->flags |= CHUNK_BAD144;
+ }
+ 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], *cp;
+ int size, subtype;
+ chunk_e partitiontype;
+
+ snprintf(tmp, 20, "%lu", 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 = strtol(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). You can choose other types, 6 for a\n"
+ "DOS partition or 131 for a Linux partition, for example.\n\n"
+ "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 use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+#ifdef __alpha__
+ if (partitiontype == freebsd && size == chunk_info[current_chunk]->size)
+ All_FreeBSD(d, 1);
+ else
+#endif
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
+ 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;
+ WINDOW *save = savescr();
+
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). Other popular values are 6 for\n"
+ "DOS FAT partition, 131 for a Linux ext2fs partition or\n"
+ "130 for a Linux swap partition.\n\n"
+ "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.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ restorescr(save);
+ }
+ 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 ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgYesNo("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 (!msgYesNo("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);
+
+ /* 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.
+ */
+#if 0
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#else
+ /*
+ * 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)) &&
+ (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#endif
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /* 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.
+ */
+#if 0
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#else
+ /*
+ * 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)) &&
+ (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+#endif
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ dialog_clear_norefresh();
+ 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);
+}
+
+static u_char *
+bootalloc(char *name)
+{
+ 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 %d bytes from %s\n", sb.st_size, buf);
+ return NULL;
+ }
+ close(fd);
+ return cp;
+ }
+ msgDebug("bootalloc: couldn't open %s\n", buf);
+ }
+ else
+ msgDebug("bootalloc: can't stat %s\n", buf);
+ return NULL;
+}
+
+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 | DITEM_RESTORE;
+}
+
+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))
+ 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))
+ 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 | DITEM_RESTORE;
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+ char *cp;
+
+ 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));
+ cp = variable_get(DISK_PARTITIONED);
+ if (cp && !strcmp(cp, "written"))
+ return DITEM_SUCCESS;
+
+ for (i = 0; devs[i]; i++) {
+ Chunk *c1;
+ Disk *d = (Disk *)devs[i]->private;
+ static u_char *boot1;
+#ifndef __alpha__
+ static u_char *boot2;
+#endif
+
+ if (!devs[i]->enabled)
+ continue;
+
+#ifdef __alpha__
+ if (!boot1) boot1 = bootalloc("boot1");
+ Set_Boot_Blocks(d, boot1, NULL);
+#else
+ if (!boot1) boot1 = bootalloc("boot1");
+ if (!boot2) boot2 = bootalloc("boot2");
+ Set_Boot_Blocks(d, boot1, boot2);
+#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;
+ }
+
+ /* If we've been through here before, we don't need to do the rest */
+ if (cp && !strcmp(cp, "written"))
+ return DITEM_SUCCESS;
+
+ /* Now scan for bad blocks, if necessary */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->flags & CHUNK_BAD144) {
+ int ret;
+
+ msgNotify("Running bad block scan on slice %s", c1->name);
+ if (!Fake) {
+ ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
+ if (ret)
+ msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
+ ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
+ if (ret)
+ msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
+ }
+ }
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written", 0);
+ return DITEM_SUCCESS;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+ u_char *mbrContents;
+ 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));
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ 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 = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ 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));
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find %d free blocks on this disk!", 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]) {
+ dialog_clear();
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ dialog_clear();
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+ mbrContents = getBootMgr(d->name);
+ Set_Boot_Mgr(d, mbrContents);
+ }
+ variable_set2(DISK_PARTITIONED, "yes", 0);
+ }
+}
diff --git a/usr.sbin/sysinstall/dispatch.c b/usr.sbin/sysinstall/dispatch.c
new file mode 100644
index 0000000..8d289e4
--- /dev/null
+++ b/usr.sbin/sysinstall/dispatch.c
@@ -0,0 +1,436 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dispatch.c,v 1.26 1998/11/15 09:06:19 jkh Exp $
+ *
+ * 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 struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configRegister", configRegister },
+ { "configUsers", configUsers },
+ { "configXEnvironment", configXEnvironment },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetDES", distSetDES },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installNovice", installNovice },
+ { "installUpgrade", installUpgrade },
+ { "installFixupBin", installFixupBin },
+ { "installFixupXFree", installFixupXFree },
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "loadFloppyConfig", dispatch_load_floppy },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "msgConfirm", dispatch_msgConfirm },
+ { "optionsEditor", optionsEditor },
+ { "register", configRegister }, /* Alias */
+ { "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(msg);
+ return DITEM_SUCCESS;
+ }
+
+ msgDebug("_msgConfirm: No message passed in `command' variable.\n");
+ return DITEM_FAILURE;
+}
+
+static int
+call_possible_resword(char *name, dialogMenuItem *value, int *status)
+{
+ int i, rval;
+
+ rval = 0;
+ for (i = 0; resWords[i].name; i++) {
+ if (!strcmp(name, resWords[i].name)) {
+ *status = resWords[i].handler(value);
+ rval = 1;
+ break;
+ }
+ }
+ return rval;
+}
+
+/* For a given string, call it or spit out an undefined command diagnostic */
+int
+dispatchCommand(char *str)
+{
+ int i;
+ char *cp;
+
+ if (!str || !*str) {
+ msgConfirm("Null or zero-length string passed to dispatchCommand");
+ return DITEM_FAILURE;
+ }
+ /* If it's got a newline, trim it */
+ if ((cp = index(str, '\n')) != NULL)
+ *cp = '\0';
+
+ /* If it's got a `=' sign in there, assume it's a variable setting */
+ if (index(str, '=')) {
+ if (isDebug())
+ msgDebug("dispatch: setting variable `%s'\n", str);
+ variable_set(str, 0);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ }
+ 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) {
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (variable_get(VAR_NO_ERROR))
+ variable_unset(VAR_NO_ERROR);
+ else {
+ 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_RESTORE | DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ dialog_clear_norefresh();
+
+ 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 (!mediaDevice->init(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = mediaDevice->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..31451a8
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.c
@@ -0,0 +1,821 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dist.c,v 1.136 1999/02/14 18:53:17 jkh Exp $
+ *
+ * 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/time.h>
+#include <signal.h>
+#include <libutil.h>
+
+unsigned int Dists;
+unsigned int DESDists;
+unsigned int SrcDists;
+unsigned int XF86Dists;
+unsigned int XF86ServerDists;
+unsigned int XF86FontDists;
+
+typedef struct _dist {
+ char *my_name;
+ char *my_dir;
+ unsigned int *my_mask;
+ unsigned int my_bit;
+ struct _dist *my_dist;
+} Distribution;
+
+extern Distribution DistTable[];
+extern Distribution DESDistTable[];
+extern Distribution SrcDistTable[];
+extern Distribution XF86DistTable[];
+extern Distribution XF86FontDistTable[];
+extern Distribution XF86ServerDistTable[];
+
+/* The top-level distribution categories */
+static Distribution DistTable[] = {
+{ "bin", "/", &Dists, DIST_BIN, NULL },
+{ "doc", "/", &Dists, DIST_DOC, NULL },
+{ "games", "/", &Dists, DIST_GAMES, NULL },
+{ "manpages", "/", &Dists, DIST_MANPAGES, NULL },
+{ "catpages", "/", &Dists, DIST_CATPAGES, NULL },
+{ "proflibs", "/", &Dists, DIST_PROFLIBS, NULL },
+{ "dict", "/", &Dists, DIST_DICT, NULL },
+{ "info", "/", &Dists, DIST_INFO, NULL },
+{ "src", "/", &Dists, DIST_SRC, SrcDistTable },
+{ "des", "/", &Dists, DIST_DES, DESDistTable },
+{ "compat1x", "/", &Dists, DIST_COMPAT1X, NULL },
+{ "compat20", "/", &Dists, DIST_COMPAT20, NULL },
+{ "compat21", "/", &Dists, DIST_COMPAT21, NULL },
+{ "compat22", "/", &Dists, DIST_COMPAT22, NULL },
+{ "ports", "/usr", &Dists, DIST_PORTS, NULL },
+{ "XF86333", "/usr", &Dists, DIST_XF86, XF86DistTable },
+{ NULL },
+};
+
+/* The DES distribution (not for export!) */
+static Distribution DESDistTable[] = {
+{ "des", "/", &DESDists, DIST_DES_DES, NULL },
+{ "krb", "/", &DESDists, DIST_DES_KERBEROS, NULL },
+{ "ssecure", "/usr/src", &DESDists, DIST_DES_SSECURE, NULL },
+{ "scrypto", "/usr/src", &DESDists, DIST_DES_SCRYPTO, NULL },
+{ "skerbero", "/usr/src", &DESDists, DIST_DES_SKERBEROS, NULL },
+{ NULL },
+};
+
+/* The /usr/src distribution */
+static Distribution SrcDistTable[] = {
+{ "sbase", "/usr/src", &SrcDists, DIST_SRC_BASE, NULL },
+{ "scontrib", "/usr/src", &SrcDists, DIST_SRC_CONTRIB, NULL },
+{ "sgnu", "/usr/src", &SrcDists, DIST_SRC_GNU, NULL },
+{ "setc", "/usr/src", &SrcDists, DIST_SRC_ETC, NULL },
+{ "sgames", "/usr/src", &SrcDists, DIST_SRC_GAMES, NULL },
+{ "sinclude", "/usr/src", &SrcDists, DIST_SRC_INCLUDE, NULL },
+{ "slib", "/usr/src", &SrcDists, DIST_SRC_LIB, NULL },
+{ "slibexec", "/usr/src", &SrcDists, DIST_SRC_LIBEXEC, NULL },
+{ "srelease", "/usr/src", &SrcDists, DIST_SRC_RELEASE, NULL },
+{ "sbin", "/usr/src", &SrcDists, DIST_SRC_BIN, NULL },
+{ "ssbin", "/usr/src", &SrcDists, DIST_SRC_SBIN, NULL },
+{ "sshare", "/usr/src", &SrcDists, DIST_SRC_SHARE, NULL },
+{ "ssys", "/usr/src", &SrcDists, DIST_SRC_SYS, NULL },
+{ "subin", "/usr/src", &SrcDists, DIST_SRC_UBIN, NULL },
+{ "susbin", "/usr/src", &SrcDists, DIST_SRC_USBIN, NULL },
+{ NULL },
+};
+
+/* The XFree86 distribution */
+static Distribution XF86DistTable[] = {
+{ "XF86333", "/usr/X11R6", &XF86Dists, DIST_XF86_FONTS, XF86FontDistTable },
+{ "XF86333", "/usr/X11R6", &XF86Dists, DIST_XF86_SERVER, XF86ServerDistTable },
+{ "Xsrc1", "/usr/X11R6/src", &XF86Dists, DIST_XF86_SRC, NULL },
+{ "Xsrcctrb", "/usr/X11R6/src", &XF86Dists, DIST_XF86_CSRC, NULL },
+{ "Xbin", "/usr/X11R6", &XF86Dists, DIST_XF86_BIN, NULL },
+{ "Xcfg", "/usr/X11R6", &XF86Dists, DIST_XF86_CFG, NULL },
+{ "Xdoc", "/usr/X11R6", &XF86Dists, DIST_XF86_DOC, NULL },
+{ "Xhtml", "/usr/X11R6", &XF86Dists, DIST_XF86_HTML, NULL },
+{ "Xlib", "/usr/X11R6", &XF86Dists, DIST_XF86_LIB, NULL },
+{ "Xlk98", "/usr/X11R6", &XF86Dists, DIST_XF86_LKIT98, NULL },
+{ "Xlkit", "/usr/X11R6", &XF86Dists, DIST_XF86_LKIT, NULL },
+{ "Xman", "/usr/X11R6", &XF86Dists, DIST_XF86_MAN, NULL },
+{ "Xprog", "/usr/X11R6", &XF86Dists, DIST_XF86_PROG, NULL },
+{ "Xps", "/usr/X11R6", &XF86Dists, DIST_XF86_PS, NULL },
+{ "Xset", "/usr/X11R6", &XF86Dists, DIST_XF86_SET, NULL },
+{ "X9set", "/usr/X11R6", &XF86Dists, DIST_XF86_9SET, NULL },
+{ NULL },
+};
+
+/* The XFree86 server distribution */
+static Distribution XF86ServerDistTable[] = {
+{ "PC98-Servers/X9480", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9480, NULL },
+{ "PC98-Servers/X9EGC", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9EGC, NULL },
+{ "PC98-Servers/X9GA9", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9GA9, NULL },
+{ "PC98-Servers/X9GAN", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9GAN, NULL },
+{ "PC98-Servers/X9LPW", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9LPW, NULL },
+{ "PC98-Servers/X9MGA", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9MGA, NULL },
+{ "PC98-Servers/X9NKV", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9NKV, NULL },
+{ "PC98-Servers/X9NS3", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9NS3, NULL },
+{ "PC98-Servers/X9SPW", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9SPW, NULL },
+{ "PC98-Servers/X9SVG", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9SVG, NULL },
+{ "PC98-Servers/X9TGU", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9TGU, NULL },
+{ "PC98-Servers/X9WEP", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WEP, NULL },
+{ "PC98-Servers/X9WS", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WS, NULL },
+{ "PC98-Servers/X9WSN", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WSN, NULL },
+{ "Servers/X8514", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_8514, NULL },
+{ "Servers/XAGX", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_AGX, NULL },
+{ "Servers/XI128", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_I128, NULL },
+{ "Servers/XMa8", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH8, NULL },
+{ "Servers/XMa32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH32,NULL },
+{ "Servers/XMa64", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH64,NULL },
+{ "Servers/XMono", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MONO, NULL },
+{ "Servers/XP9K", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_P9000, NULL },
+{ "Servers/XS3", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_S3, NULL },
+{ "Servers/XS3V", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_S3V, NULL },
+{ "Servers/XSVGA", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_SVGA, NULL },
+{ "Servers/XVG16", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_VGA16, NULL },
+{ "Servers/XW32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_W32, NULL },
+{ "Xnest", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_NEST, NULL },
+{ NULL },
+};
+
+/* The XFree86 font distribution */
+static Distribution XF86FontDistTable[] = {
+{ "Xfnts", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_MISC, NULL },
+{ "Xf100", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_100, NULL },
+{ "Xfcyr", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_CYR, NULL },
+{ "Xfscl", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_SCALE, NULL },
+{ "Xfnon", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_NON, NULL },
+{ "Xfsrv", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_SERVER, NULL },
+{ NULL },
+};
+
+static int distMaybeSetDES(dialogMenuItem *self);
+static int distMaybeSetPorts(dialogMenuItem *self);
+
+
+static void
+distVerifyFlags(void)
+{
+ if (SrcDists)
+ Dists |= DIST_SRC;
+ if (DESDists) {
+ if (DESDists & DIST_DES_KERBEROS)
+ DESDists |= DIST_DES_DES;
+ Dists |= DIST_DES;
+ }
+ if (XF86Dists & DIST_XF86_SET)
+ XF86ServerDists |= DIST_XF86_SERVER_VGA16;
+ if (XF86ServerDists)
+ XF86Dists |= DIST_XF86_SERVER;
+ if (XF86FontDists)
+ XF86Dists |= DIST_XF86_FONTS;
+ if (XF86Dists || XF86ServerDists || XF86FontDists)
+ Dists |= DIST_XF86;
+ if (Dists & DIST_XF86)
+ Dists |= DIST_COMPAT22;
+ if (isDebug())
+ msgDebug("Dist Masks: Dists: %0x, DES: %0x, Srcs: %0x\nXServer: %0x, XFonts: %0x, XDists: %0x\n",
+ Dists, DESDists, SrcDists, XF86ServerDists, XF86FontDists, XF86Dists);
+}
+
+int
+distReset(dialogMenuItem *self)
+{
+ Dists = 0;
+ DESDists = 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_DES)) != NULL)
+ DESDists = 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_BIN | DIST_XF86_SET | DIST_XF86_CFG | DIST_XF86_LIB | DIST_XF86_PROG | DIST_XF86_MAN | DIST_XF86_SERVER | DIST_XF86_FONTS;
+ XF86ServerDists = DIST_XF86_SERVER_SVGA | DIST_XF86_SERVER_VGA16;
+ XF86FontDists = DIST_XF86_FONTS_MISC;
+ return distSetXF86(NULL);
+}
+
+int
+distSetDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+ i = distMaybeSetDES(self) | 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;
+ i = distMaybeSetDES(self) | 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;
+ i = distMaybeSetDES(self) | 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_BIN;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetEverything(dialogMenuItem *self)
+{
+ int i;
+
+ Dists = DIST_ALL | DIST_XF86;
+ SrcDists = DIST_SRC_ALL;
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ i = distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetDES(dialogMenuItem *self)
+{
+ int i;
+
+ if (!dmenuOpenSimple(&MenuDESDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static int
+distMaybeSetDES(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do wish to install DES cryptographic software?\n\n"
+ "If you choose No, FreeBSD will use an MD5 based password scheme which,\n"
+ "while perhaps more secure, is not interoperable with the traditional\n"
+ "UNIX DES passwords on other non-FreeBSD systems.\n\n"
+ "Please do NOT choose Yes at this point if you are outside the\n"
+ "United States and Canada yet are installing from a U.S. FTP server.\n"
+ "This will violate U.S. export restrictions and possibly get the\n"
+ "server site into trouble! In such cases, install everything but the\n"
+ "DES distribution from the U.S. server then switch your media type to\n"
+ "point to an international FTP server, using the Custom installation\n"
+ "option to select and extract the DES distribution in a second pass.")) {
+ if (!dmenuOpenSimple(&MenuDESDistributions, FALSE))
+ i = DITEM_FAILURE;
+ }
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+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 2000 ported software packages,\n"
+ "though at a cost of around 50MB of disk space when \"clean\" and possibly\n"
+ "much more than that if a lot of the distribution tarballs are loaded\n"
+ "(unless you have the extra CDs available from a FreeBSD CDROM 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, if you have at least\n"
+ "100MB to spare in your /usr partition, well worth having around.\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++) {
+ /* This is shorthand for "dist currently disabled" */
+ if (!dist[i].my_dir)
+ continue;
+ if (!strcmp(dist[i].my_name, name)) {
+ *(dist[i].my_mask) |= dist[i].my_bit;
+ status = TRUE;
+ }
+ if (dist[i].my_dist) {
+ if (distSetByName(dist[i].my_dist, name)) {
+ status = TRUE;
+ }
+ }
+ }
+ distVerifyFlags();
+ 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", 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;
+}
+
+int
+distSetSrc(dialogMenuItem *self)
+{
+ int i;
+
+ if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+int
+distSetXF86(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+ 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;
+}
+
+static Boolean
+distExtract(char *parent, Distribution *me)
+{
+ int i, status, total, intr;
+ int cpid, zpid, fd2, chunk, numchunks;
+ char *path, *dist, buf[BUFSIZ];
+ const char *tmp;
+ FILE *fp;
+ WINDOW *w = savescr();
+ struct timeval start, stop;
+ struct sigaction old, new;
+
+ status = TRUE;
+ dialog_clear_norefresh();
+ 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;
+ new.sa_mask = 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;
+
+ /* This is shorthand for "dist currently disabled" */
+ if (!me[i].my_dir) {
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ continue;
+ }
+
+ /* Recurse if we actually have a sub-distribution */
+ if (me[i].my_dist) {
+ if ((status = distExtract(dist, me[i].my_dist)) == TRUE)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ goto done;
+ }
+
+ /*
+ * Try to get distribution as multiple pieces, locating and parsing an
+ * info file which tells us how many we need for this distribution.
+ */
+ numchunks = 0;
+ snprintf(buf, sizeof buf, "%s/%s.inf", path, dist);
+
+ getinfo:
+ fp = mediaDevice->get(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr) { /* Hard error, can't continue */
+ if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
+ buf, !intr ? "I/O error." : "User interrupt.")) {
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getinfo;
+ }
+ else {
+ status = FALSE;
+ goto done;
+ }
+ }
+ else if (fp > 0) {
+ properties dist_attr;
+
+ 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);
+ properties_free(dist_attr);
+ if (!numchunks)
+ continue;
+ }
+ else {
+ /* Try to get the distribution as a single file */
+ snprintf(buf, sizeof buf, "%s/%s.tgz", path, dist);
+ /*
+ * Passing TRUE as 3rd parm to get routine makes this a "probing" get, for which errors
+ * are not considered too significant.
+ */
+ getsingle:
+ fp = mediaDevice->get(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr) { /* Hard error, can't continue */
+ if (intr) /* result of an interrupt */
+ msgConfirm("Unable to open %s: User interrupt", buf);
+ else
+ msgConfirm("Unable to open %s: I/O error", buf);
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getsingle;
+ }
+ else if (fp > 0) {
+ char *dir = root_bias(me[i].my_dir);
+
+ msgNotify("Extracting %s into %s directory...", dist, dir);
+ status = mediaExtractDist(dir, dist, fp);
+ fclose(fp);
+ goto done;
+ }
+ else
+ numchunks = 0;
+ }
+
+ /* Fall through from "we got the attribute file, now get the pieces" step */
+ if (!numchunks)
+ continue;
+
+ if (isDebug())
+ msgDebug("Attempting to extract distribution from %u chunks.\n", numchunks);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ /* We have one or more chunks, initialize unpackers... */
+ mediaExtractDistBegin(root_bias(me[i].my_dir), &fd2, &zpid, &cpid);
+
+ /* And go for all the chunks */
+ for (chunk = 0; chunk < numchunks; chunk++) {
+ int n, retval, last_msg;
+ char prompt[80];
+
+ last_msg = 0;
+
+ getchunk:
+ snprintf(buf, sizeof buf, "%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, buf);
+ fp = mediaDevice->get(mediaDevice, buf, FALSE);
+ intr = check_for_interrupt();
+ if (fp <= (FILE *)0 || intr) {
+ if (fp == (FILE *)0)
+ msgConfirm("Failed to find %s on this media. Reinitializing media.", buf);
+ else
+ msgConfirm("failed to retreive piece file %s.\n"
+ "%s: Reinitializing media.", buf, !intr ? "I/O error" : "User interrupt");
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice))
+ goto punt;
+ else
+ goto getchunk;
+ }
+
+ snprintf(prompt, sizeof prompt, "Extracting %s into %s directory...", dist, root_bias(me[i].my_dir));
+ dialog_gauge("Progress", prompt, 8, 15, 6, 50, (int)((float)(chunk + 1) / numchunks * 100));
+
+ while (1) {
+ int seconds;
+
+ n = fread(buf, 1, BUFSIZ, fp);
+ if (check_for_interrupt()) {
+ msgConfirm("Media read error: User interrupt.");
+ fclose(fp);
+ goto punt;
+ }
+ else if (n <= 0)
+ break;
+ total += 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 KB/sec.",
+ total, dist, chunk + 1, numchunks, (total / seconds) / 1024.0);
+ }
+ retval = write(fd2, buf, n);
+ if (retval != n) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, n);
+ goto punt;
+ }
+ }
+ fclose(fp);
+ }
+ close(fd2);
+ status = mediaExtractDistEnd(zpid, cpid);
+ goto done;
+
+ punt:
+ close(fd2);
+ mediaExtractDistEnd(zpid, cpid);
+ status = FALSE;
+
+ done:
+ if (!status) {
+ if (me[i].my_dist) {
+ msgConfirm("Unable to transfer all components of the %s distribution.\n"
+ "If this is a CDROM install, it may be because export restrictions prohibit\n"
+ "DES code from being shipped from the U.S. Try to get this code from a\n"
+ "local FTP site instead!", me[i].my_name);
+ }
+ else {
+ 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;
+ dialog_clear();
+ }
+ }
+ /* If extract was successful, remove ourselves from further consideration */
+ if (status)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ else
+ continue;
+ }
+ 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;
+
+ /* This is shorthand for "dist currently disabled" */
+ if (!me[i].my_dir)
+ 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_dist)
+ printSelected(buf, *(me[i].my_mask), me[i].my_dist, col);
+ }
+}
+
+int
+distExtractAll(dialogMenuItem *self)
+{
+ int old_dists, retries = 0, status = DITEM_SUCCESS;
+ char buf[512];
+
+ /* paranoia */
+ if (!Dists) {
+ if (!dmenuOpenSimple(&MenuSubDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (!mediaVerify() || !mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ old_dists = Dists;
+ distVerifyFlags();
+
+ dialog_clear_norefresh();
+ msgNotify("Attempting to install all selected distributions..");
+
+ /* Try for 3 times around the loop, then give up. */
+ while (Dists && ++retries < 3)
+ distExtract(NULL, DistTable);
+
+ /* Only do bin fixup if bin dist was successfully extracted */
+ if ((old_dists & DIST_BIN) && !(Dists & DIST_BIN))
+ status |= installFixupBin(self);
+ if (old_dists & DIST_XF86)
+ status |= installFixupXFree(self);
+
+ if (Dists) {
+ int col = 0;
+
+ buf[0] = '\0';
+ printSelected(buf, Dists, DistTable, &col);
+ dialog_clear_norefresh();
+ 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);
+ status |= DITEM_RESTORE;
+ }
+ return status;
+}
diff --git a/usr.sbin/sysinstall/dist.h b/usr.sbin/sysinstall/dist.h
new file mode 100644
index 0000000..ae38850
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.h
@@ -0,0 +1,112 @@
+#ifndef _DIST_H_INCLUDE
+#define _DIST_H_INCLUDE
+
+/* Bitfields for distributions - hope we never have more than 32! :-) */
+#define DIST_BIN 0x0001
+#define DIST_GAMES 0x0002
+#define DIST_MANPAGES 0x0004
+#define DIST_PROFLIBS 0x0008
+#define DIST_DICT 0x0010
+#define DIST_SRC 0x0020
+#define DIST_DOC 0x0040
+#define DIST_INFO 0x0080
+#define DIST_COMPAT1X 0x0100
+#define DIST_COMPAT20 0x0200
+#define DIST_COMPAT21 0x0400
+#define DIST_COMPAT22 0x0800
+#define DIST_XF86 0x1000
+#define DIST_DES 0x2000
+#define DIST_CATPAGES 0x4000
+#define DIST_PORTS 0x8000
+#define DIST_ALL 0xFFFF
+
+/* Canned distribution sets */
+#define _DIST_DEVELOPER \
+ (DIST_BIN | DIST_DOC | DIST_MANPAGES | DIST_DICT | DIST_PROFLIBS | DIST_INFO | DIST_SRC | DIST_COMPAT22)
+
+#define _DIST_USER \
+ (DIST_BIN | DIST_DOC | DIST_MANPAGES | DIST_DICT | DIST_COMPAT22)
+
+/* Subtypes for DES distribution */
+#define DIST_DES_DES 0x0001
+#define DIST_DES_SCRYPTO 0x0002
+#define DIST_DES_SSECURE 0x0004
+#define DIST_DES_KERBEROS 0x0008
+#define DIST_DES_SKERBEROS 0x0010
+
+/* 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
+/* 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 0xFFFF
+
+/* Subtypes for XFree86 distribution */
+#define DIST_XF86_BIN 0x0001
+#define DIST_XF86_CFG 0x0002
+#define DIST_XF86_DOC 0x0004
+#define DIST_XF86_HTML 0x0008
+#define DIST_XF86_LIB 0x0010
+#define DIST_XF86_LKIT98 0x0020
+#define DIST_XF86_LKIT 0x0040
+#define DIST_XF86_MAN 0x0080
+#define DIST_XF86_PROG 0x0100
+#define DIST_XF86_PS 0x0200
+#define DIST_XF86_SET 0x0400
+#define DIST_XF86_9SET 0x0800
+#define DIST_XF86_SRC 0x1000
+#define DIST_XF86_CSRC 0x2000
+#define DIST_XF86_MISC_ALL 0x3FFF
+#define DIST_XF86_SERVER 0x8000
+#define DIST_XF86_SERVER_9MGA 0x0000001
+#define DIST_XF86_SERVER_9480 0x0000002
+#define DIST_XF86_SERVER_9EGC 0x0000004
+#define DIST_XF86_SERVER_9GA9 0x0000008
+#define DIST_XF86_SERVER_9GAN 0x0000010
+#define DIST_XF86_SERVER_9LPW 0x0000020
+#define DIST_XF86_SERVER_9NKV 0x0000040
+#define DIST_XF86_SERVER_9NS3 0x0000080
+#define DIST_XF86_SERVER_9SVG 0x0000100
+#define DIST_XF86_SERVER_9SPW 0x0000200
+#define DIST_XF86_SERVER_9TGU 0x0000400
+#define DIST_XF86_SERVER_9WEP 0x0000800
+#define DIST_XF86_SERVER_9WS 0x0001000
+#define DIST_XF86_SERVER_9WSN 0x0002000
+#define DIST_XF86_SERVER_8514 0x0004000
+#define DIST_XF86_SERVER_AGX 0x0008000
+#define DIST_XF86_SERVER_I128 0x0010000
+#define DIST_XF86_SERVER_MACH8 0x0020000
+#define DIST_XF86_SERVER_MACH32 0x0040000
+#define DIST_XF86_SERVER_MACH64 0x0080000
+#define DIST_XF86_SERVER_MONO 0x0100000
+#define DIST_XF86_SERVER_P9000 0x0200000
+#define DIST_XF86_SERVER_S3 0x0400000
+#define DIST_XF86_SERVER_S3V 0x0800000
+#define DIST_XF86_SERVER_SVGA 0x1000000
+#define DIST_XF86_SERVER_VGA16 0x2000000
+#define DIST_XF86_SERVER_W32 0x4000000
+#define DIST_XF86_SERVER_NEST 0x8000000
+#define DIST_XF86_SERVER_ALL 0xFFFFFFF
+#define DIST_XF86_FONTS 0x10000
+#define DIST_XF86_FONTS_MISC 0x0001
+#define DIST_XF86_FONTS_100 0x0002
+#define DIST_XF86_FONTS_CYR 0x0004
+#define DIST_XF86_FONTS_SCALE 0x0008
+#define DIST_XF86_FONTS_NON 0x0010
+#define DIST_XF86_FONTS_SERVER 0x0020
+#define DIST_XF86_FONTS_ALL 0x003F
+#define DIST_XF86_ALL 0x1FFFF
+
+#endif /* _DIST_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/dmenu.c b/usr.sbin/sysinstall/dmenu.c
new file mode 100644
index 0000000..f83fada
--- /dev/null
+++ b/usr.sbin/sysinstall/dmenu.c
@@ -0,0 +1,308 @@
+/*
+ * 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.
+ *
+ * $Id: dmenu.c,v 1.39 1999/02/05 22:25:13 jkh Exp $
+ *
+ * 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 | DITEM_RESTORE;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE) |
+ DITEM_RESTORE;
+}
+
+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)
+{
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+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, 1);
+ 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;
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ if (!variable_check(var))
+ variable_set(var, *var != '_');
+ else
+ variable_unset(var);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ w = savescr();
+ ans = msgGetInput(variable_get(var), tmp->title, 1);
+ restorescr(w);
+ 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];
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+
+ /* Pop up that dialog! */
+ dialog_clear_norefresh();
+ 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 *)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 *)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 *)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ clearok(stdscr, TRUE);
+ if (exited) {
+ exited = FALSE;
+ return TRUE;
+ }
+ else if (rval)
+ return FALSE;
+ else if (menu->type & DMENU_SELECTION_RETURNS)
+ return TRUE;
+ }
+}
diff --git a/usr.sbin/sysinstall/doc.c b/usr.sbin/sysinstall/doc.c
new file mode 100644
index 0000000..9d82f26
--- /dev/null
+++ b/usr.sbin/sysinstall/doc.c
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ *
+ * $Id: doc.c,v 1.22 1997/02/07 04:25:53 jkh Exp $
+ *
+ * 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"
+
+/*
+ * 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_exists(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 | DITEM_RESTORE;
+ }
+
+ /* Run browser on the appropriate doc */
+ if (dmenuOpenSimple(&MenuHTMLDoc, FALSE))
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+}
+
+/* Try to show one of the documents requested from the HTML doc menu */
+int
+docShowDocument(dialogMenuItem *self)
+{
+ char tmp[512], target[512];
+ char *where = NULL;
+ char *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;
+ }
+ if (!strcmp(str, "Home"))
+ where = "http://www.freebsd.org";
+ else if (!strcmp(str, "Other"))
+ where = msgGetInput("http://www.freebsd.org", "Please enter the URL of the location you wish to visit.");
+ else if (!strcmp(str, "FAQ")) {
+ strcpy(target, "/usr/share/doc/FAQ/FAQ.html");
+ if (!file_readable(target))
+ strcpy(target, "http://www.freebsd.org/FAQ");
+ where = target;
+ }
+ else if (!strcmp(str, "Handbook")) {
+ strcpy(target, "/usr/share/doc/handbook/handbook.html");
+ if (!file_readable(target))
+ strcpy(target, "http://www.freebsd.org/handbook");
+ where = target;
+ }
+ if (where) {
+ sprintf(tmp, "%s %s", browser, where);
+ dialog_clear();
+ systemExecute(tmp);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ msgConfirm("Hmmmmm! I can't seem to access the documentation you selected!\n"
+ "Have you loaded the bin 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..a11122a
--- /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.
+ *
+ * $Id: dos.c,v 1.21 1998/10/28 02:18:08 jkh Exp $
+ *
+ * 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 <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("msdos", 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..ca958ed
--- /dev/null
+++ b/usr.sbin/sysinstall/floppy.c
@@ -0,0 +1,155 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: floppy.c,v 1.32 1999/02/15 00:49:33 jkh Exp $
+ *
+ * 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 <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+#include <ufs/ufs/ufsmount.h>
+static Boolean floppyMounted;
+
+char *distWanted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitFloppy(Device *dev)
+{
+ struct msdosfs_args dosargs;
+ struct ufs_args u_args;
+ char *mp;
+
+ if (floppyMounted)
+ return TRUE;
+
+ mp = dev->private ? (char *)dev->private : mountpoint;
+ if (Mkdir(mp)) {
+ msgConfirm("Unable to make %s directory mountpoint for %s!", mp, dev->devname);
+ return FALSE;
+ }
+
+ msgDebug("Init floppy called for %s distribution.\n", distWanted ? distWanted : "some");
+
+ if (!variable_get(VAR_NONINTERACTIVE)) {
+ if (!distWanted)
+ msgConfirm("Please insert floppy in %s", dev->description);
+ else
+ msgConfirm("Please insert floppy containing %s in %s",
+ distWanted, dev->description);
+ }
+
+ memset(&dosargs, 0, sizeof dosargs);
+ dosargs.fspec = dev->devname;
+ dosargs.uid = dosargs.gid = 0;
+ dosargs.mask = 0777;
+
+ memset(&u_args, 0, sizeof(u_args));
+ u_args.fspec = dev->devname;
+
+ if (mount("msdos", mp, MNT_RDONLY, (caddr_t)&dosargs) == -1) {
+ if (mount("ufs", mp, MNT_RDONLY, (caddr_t)&u_args) == -1) {
+ msgConfirm("Error mounting floppy %s (%s) on %s : %s",
+ dev->name, dev->devname, mp, strerror(errno));
+ return FALSE;
+ }
+ }
+ 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..4f1e542
--- /dev/null
+++ b/usr.sbin/sysinstall/ftp.c
@@ -0,0 +1,253 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: ftp.c,v 1.35 1998/01/28 04:42:38 jkh Exp $
+ *
+ * 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;
+
+/* 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 netdev->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)
+ netdev->shutdown(netdev);
+}
+
+Boolean
+mediaInitFTP(Device *dev)
+{
+ int i, code;
+ 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));
+ }
+ msgNotify("Logging in to %s@%s..", login_name, hostname);
+ if ((OpenConn = ftpLogin(hostname, 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;
+ }
+ }
+
+ /* Give it a shot - can't hurt to try and zoom in if we can, unless the release is set to
+ __RELEASE or "none" which signifies that it's not set */
+ rel = variable_get(VAR_RELNAME);
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "none"))
+ i = ftpChdir(OpenConn, rel);
+ else
+ i = 0;
+ if (i) {
+ if (!msgYesNo("Warning: Can't CD to `%s' distribution on this\n"
+ "FTP server. You may need to visit a different server for\n"
+ "the release you're 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 \"none\").\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 punt;
+ else
+ goto try;
+ }
+ else
+ goto punt;
+ }
+ 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 */
+ dev->shutdown(dev);
+ if (!dev->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..72aecb1
--- /dev/null
+++ b/usr.sbin/sysinstall/globals.c
@@ -0,0 +1,71 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: globals.c,v 1.18 1997/02/22 14:11:43 peter Exp $
+ *
+ * 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? */
+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;
+ 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..e0c91dc
--- /dev/null
+++ b/usr.sbin/sysinstall/help/distributions.hlp
@@ -0,0 +1,63 @@
+DISTRIBUTION INFORMATION
+------------------------
+
+An ``X-'' prefixed before a distribution set means that the XFree86
+3.3.3.1 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 and 2.2.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.
+
+---
+
+ENCRYPTION SOFTWARE:
+--------------------
+You may notice that certain distributions, like "des" and "krb",
+are marked "NOT FOR EXPORT!" This is because it's illegal to export
+them from the United States (or any other country which considers
+encryption technology to be on its restricted export list). Since
+breaking this law only gets the _originating_ site (US!) in trouble,
+please do not load these distributions from U.S. servers! We don't
+like these restrictions any more than you do, but can't do much about
+it (write your U.S. congress person!).
+
+A number of "foreign" servers do exist for the benefit of
+non-U.S. sites, the official site being:
+
+ ftp://ftp.internat.freebsd.org/pub/FreeBSD
+
+Please get all such export restricted software from there
+if you are outside the U.S., thanks!
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..f63dd30
--- /dev/null
+++ b/usr.sbin/sysinstall/help/html.hlp
@@ -0,0 +1,19 @@
+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 lynx (a text based
+browser), 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..7fccd8e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/media.hlp
@@ -0,0 +1,53 @@
+You can install from the following types of media:
+
+ CDROM requires one of the following supported CDROM drives:
+ Sony CDU 31/33A
+ Matushita/Panasonic "Sound Blaster" CDROM.
+ Mitsumi FX-001{A-D} (older non-IDE drives).
+ IDE CDROM
+ 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
+ there are also two ways of invoking FTP in either
+ "Active" and "Passive" mode.
+
+ 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. If you chose "other" in the FTP menu, please
+ also 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..3519c0f
--- /dev/null
+++ b/usr.sbin/sysinstall/help/network_device.hlp
@@ -0,0 +1,56 @@
+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, at the minimum, your service provider's IP address
+and possibly your own (though you can also leave it blank and allow
+PPP to negotiate it with your ISP if your ISP supports such dynamic
+negotiation). 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 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..dced216
--- /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 lynx 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..6004d75
--- /dev/null
+++ b/usr.sbin/sysinstall/help/partition.hlp
@@ -0,0 +1,118 @@
+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 20MB 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 100MB. 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).
+
+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..3f360c3
--- /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 http://ftp.freebsd.org/pub/FreeBSD/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/shortcuts.hlp b/usr.sbin/sysinstall/help/shortcuts.hlp
new file mode 100644
index 0000000..503a2a7
--- /dev/null
+++ b/usr.sbin/sysinstall/help/shortcuts.hlp
@@ -0,0 +1,116 @@
+/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: lynx)
+browserPackage Which package to get browser from (default: lynx)
+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
+configApache Configure and install the Apache WEB server
+configGated Configure and install gated
+configNFSServer Configure host as an NFS server
+configSamba Configure and install Samba
+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..33280a4
--- /dev/null
+++ b/usr.sbin/sysinstall/help/slice.hlp
@@ -0,0 +1,59 @@
+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'.
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..4d94520
--- /dev/null
+++ b/usr.sbin/sysinstall/help/usage.hlp
@@ -0,0 +1,68 @@
+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. Note also that "checkbox" menus use SPACE to select
+their items, not ENTER! Pressing ENTER will leave the menu with either
+an OK or Cancel status, depending on which button at the bottom is
+selected, and is probably not what you wanted to do if still selecting
+options. Remember the spacebar!
+
+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 3
+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/index.c b/usr.sbin/sysinstall/index.c
new file mode 100644
index 0000000..c02d4ad
--- /dev/null
+++ b/usr.sbin/sysinstall/index.c
@@ -0,0 +1,705 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: index.c,v 1.63 1999/02/15 04:57:07 jkh Exp $
+ *
+ * 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 <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 int index_extract_one(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended);
+static void index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie);
+
+/* 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"
+ "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.",
+ "afterstep", "Ports to support the AfterStep window manager.",
+ "applications", "User application software.",
+ "astro", "Applications related to astronomy.",
+ "archivers", "Utilities for archiving and unarchiving data.",
+ "audio", "Audio utilities - most require a supported sound card.",
+ "biology", "Software related to biology.",
+ "benchmarks", "Utilities for measuring system performance.",
+ "cad", "Computer Aided Design utilities.",
+ "chinese", "Ported software for the Chinese market.",
+ "comms", "Communications utilities.",
+ "converters", "Format conversion utilities.",
+ "databases", "Database software.",
+ "devel", "Software development utilities and libraries.",
+ "deskutils", "Various Desktop utilities.",
+ "documentation", "Document preparation utilities.",
+ "editors", "Common text editors.",
+ "elisp", "Things related to Emacs Lisp.",
+ "elisp", "Emacs lisp ports.",
+ "emulators", "Utilities for emulating other OS types.",
+ "games", "Various and sundry amusements.",
+ "german", "Ported software for Germanic countries.",
+ "graphics", "Graphics libraries and utilities.",
+ "gnome", "Components of the Gnome Desktop environment.",
+ "japanese", "Ported software for the Japanese market.",
+ "kde", "Software for the K Desktop Environment.",
+ "korean", "Ported software for the Korean market.",
+ "lang", "Computer languages.",
+ "languages", "Computer languages.",
+ "libraries", "Software development libraries.",
+ "mail", "Electronic mail packages and utilities.",
+ "math", "Mathematical computation software.",
+ "mbone", "Applications and utilities for the MBONE.",
+ "misc", "Miscellaneous utilities.",
+ "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 3Com Palm Pilot(tm) series.",
+ "pilot", "Software support for the 3Com Palm Pilot(tm) series.",
+ "perl5", "Utilities/modules for the PERL5 language.",
+ "plan9", "Software from the Plan9 operating system.",
+ "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.",
+ "russian", "Ported software for the Russian market.",
+ "security", "System security software.",
+ "shells", "Various shells (tcsh, bash, etc).",
+ "sysutils", "Various system utilities.",
+ "textproc", "Text processing/search 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.",
+ "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.",
+ "troff", "TROFF text formatting utilities.",
+ "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-toolkits", "X Window System based development toolkits.",
+ "x11-wm", "X Window System window managers.",
+ 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)
+{
+ 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_exists(name);
+ 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;
+}
+
+int
+index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps)
+{
+ char line[1024];
+ char junk[256];
+ char *cp;
+ int i;
+
+ i = readline(fp, line, 1024);
+ 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 */
+ if (index(cp, '|'))
+ copy_to_sep(rdeps, cp, '|');
+ else
+ strncpy(rdeps, cp, 510);
+ 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[511];
+ PkgNodePtr i;
+
+ while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps) != EOF) {
+ char *cp, *cp2, tmp[511];
+ IndexEntryPtr idx;
+
+ idx = new_index(name, pathto, prefix, comment, descr, maint, deps);
+ /* 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) {
+ /* Subtract out the All category from searches */
+ if (!strcmp(p->name, "All"))
+ continue;
+
+ /* If tp == NULL, we're looking for an exact package match */
+ if (!tp && !strncmp(p->name, str, strlen(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;
+ }
+
+ /* The usual recursion-out-of-laziness ploy */
+ if (p->kids)
+ 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) {
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("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);
+ }
+ }
+ restorescr(save);
+ }
+ 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(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;
+
+ w = savescr();
+ 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);
+ restorescr(w);
+ return DITEM_LEAVE_MENU;
+ }
+
+ 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, 0, &curr, &max);
+ nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, 0, &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, 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, 0, &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 plist)
+{
+ PkgNodePtr tmp;
+ int status = DITEM_SUCCESS;
+
+ for (tmp = plist->kids; tmp && tmp->name; tmp = tmp->next)
+ status = index_extract_one(dev, top, tmp, FALSE);
+ return status;
+}
+
+static int
+index_extract_one(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
+{
+ int status = DITEM_SUCCESS;
+ PkgNodePtr tmp2;
+ IndexEntryPtr id = who->data;
+
+ if (id && id->deps && strlen(id->deps)) {
+ char t[1024], *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_one(dev, top, tmp2, TRUE);
+ if (DITEM_STATUS(status) != DITEM_SUCCESS) {
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Loading of dependant package %s failed", cp);
+ else
+ msgConfirm("Loading of dependant package %s failed", cp);
+ }
+ }
+ else if (!package_exists(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;
+ }
+ return status;
+}
+
+static void
+index_recorddeps(Boolean add, PkgNodePtr root, IndexEntryPtr ie)
+{
+ char depends[1024], *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;
+ }
+}
diff --git a/usr.sbin/sysinstall/install.c b/usr.sbin/sysinstall/install.c
new file mode 100644
index 0000000..f8a6d59
--- /dev/null
+++ b/usr.sbin/sysinstall/install.c
@@ -0,0 +1,1134 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: install.c,v 1.229 1999/02/15 00:49:33 jkh Exp $
+ *
+ * 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/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+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)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = 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) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ 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);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* 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) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Copy our values over */
+ *rdev = rootdev;
+ *sdev = swapdev;
+ *udev = usrdev;
+ *vdev = vardev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ if (!usrdev && whinge && !variable_get(VAR_NO_USR)) {
+ msgConfirm("WARNING: No /usr filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
+ "cause you trouble if you're not exactly sure what you are doing!");
+ }
+ if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
+ msgConfirm("WARNING: No /var filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to link /var to someplace else), but it may otherwise\n"
+ "cause your root filesystem to fill up if you receive lots of mail\n"
+ "or edit large temporary files.");
+ }
+ 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 | DITEM_RESTORE;
+
+ 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 (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 */
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return status;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+}
+
+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 CDROM and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->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 CDROM - 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 CDROM 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/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ /* Yet more iggly hardcoded pathnames. */
+ Mkdir("/usr/libexec");
+ if (!file_readable("/usr/libexec/ld.so")) {
+ if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so"))
+ msgDebug("Couldn't link to ld.so - not necessarily a problem for ELF\n");
+ }
+ if (!file_readable("/usr/libexec/ld-elf.so.1")) {
+ if (symlink("/mnt2/usr/libexec/ld-elf.so.1", "/usr/libexec/ld-elf.so.1")) {
+ msgConfirm("Warning: could not create the symlink for ld-elf.so.1\n"
+ "Dynamic executables from the CDROM 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 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 (!mediaDevice->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/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 (!(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);
+ 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);
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", 0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ 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.");
+ (void)waitpid(child, &waitstatus, 0);
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ variable_set2(SYSTEM_STATE, "express", 0);
+#ifndef __alpha__
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+#endif
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i | DITEM_RESTORE;
+}
+
+/* Novice mode installation */
+int
+installNovice(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "novice", 0);
+#ifndef __alpha__
+ dialog_clear_norefresh();
+ 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;
+ }
+#endif
+
+ dialog_clear_norefresh();
+#ifdef __alpha__
+ 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.");
+#else
+ msgConfirm("First, 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.");
+#endif
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear_norefresh();
+ 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 chose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i | DITEM_RESTORE;
+
+ }
+ else {
+ dialog_clear_norefresh();
+ 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: /stand/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;
+
+ dialog_clear_norefresh();
+ tmp = tcpDeviceSelect();
+ dialog_clear_norefresh();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
+ "between interfaces)?"))
+ variable_set2("gateway_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client_enable", "YES", 1);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to customize your system console settings?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?")) {
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ systemExecute("tzsetup");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Does this system have a mouse attached to it?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ restorescr(w);
+ }
+
+ /* 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?"))
+ configXEnvironment(self);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of hundreds 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?"))
+ configPackages(self);
+
+ dialog_clear_norefresh();
+ 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)."))
+ configUsers(self);
+
+ dialog_clear_norefresh();
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ {
+ WINDOW *w = savescr();
+
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES", 0);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
+ "PLEASE, take just 5 minutes to do this. If we're ever to get any\n"
+ "significant base of commercial software for FreeBSD, we need to\n"
+ "be able to provide more information about the size of our user community.\n"
+ "This is where your registration can really help us, and you can also\n"
+ "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
+ configRegister(NULL);
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("OK, but if you should change your mind then you always can register\n"
+ "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
+ "web site at http://www.freebsd.org/register.html");
+
+ }
+ /* 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 | DITEM_RESTORE;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ 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 DES dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists)
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ 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 (!mediaDevice->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 | DITEM_RESTORE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* 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 | DITEM_RESTORE;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ dialog_clear_norefresh();
+ if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
+ "any last options?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ restorescr(w);
+ }
+ configRC_conf();
+ sync();
+}
+
+int
+installFixupBin(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ /* All of this is done only as init, just to be safe */
+ if (RunningAsInit) {
+ /* Fix up kernel first */
+ if (!file_readable("/kernel")) {
+ if (file_readable("/kernel.GENERIC")) {
+ if (vsystem("cp -p /kernel.GENERIC /kernel")) {
+ msgConfirm("Unable to copy /kernel into place!");
+ return DITEM_FAILURE;
+ }
+#ifndef __alpha__
+ /* Snapshot any boot -c changes back to the new kernel */
+ if (kget("/boot/kernel.conf")) {
+ msgConfirm("Kernel copied OK, but unable to save boot -c changes\n"
+ "to it. See the debug screen (ALT-F2) for details.");
+ }
+ else {
+ if (!file_readable("/boot/loader.rc")) {
+ FILE *fp;
+
+ if ((fp = fopen("/boot/loader.rc", "w")) != NULL) {
+ fprintf(fp, "load /kernel\n");
+ fprintf(fp, "load -t userconfig_script /boot/kernel.conf\n");
+ fprintf(fp, "autoboot 5\n");
+ fclose(fp);
+ }
+ }
+ else {
+ msgConfirm("You already have a /boot/loader.rc file so I won't touch it.\n"
+ "You will need to add a: load -t userconfig_script /boot/kernel.conf\n"
+ "line to your /boot/loader.rc before your saved kernel changes\n"
+ "(if any) can go into effect.");
+ }
+ }
+#endif
+ }
+ else {
+ msgConfirm("Can't find a kernel image to link to on the root file system!\n"
+ "You're going to have a hard time getting this system to\n"
+ "boot from the hard disk, I'm afraid!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* BOGON #1: Resurrect /dev after bin distribution screws it up */
+ msgNotify("Remaking all devices.. Please wait!");
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Resurrecting /dev entries for slices..");
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs)
+ msgFatal("Couldn't get a disk device list!");
+
+ /* Resurrect the slices that the former clobbered */
+ for (i = 0; devs[i]; i++) {
+ Disk *disk = (Disk *)devs[i]->private;
+ Chunk *c1;
+
+ if (!devs[i]->enabled)
+ continue;
+ 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) {
+ msgNotify("Making slice entries for %s", c1->name);
+ if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
+ msgConfirm("Unable to make slice entries for %s!", c1->name);
+ return DITEM_FAILURE;
+ }
+ }
+ }
+ }
+
+ /* 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;
+}
+
+/* Fix side-effects from the the XFree86 installation */
+int
+installFixupXFree(dialogMenuItem *self)
+{
+ /* BOGON #1: XFree86 requires various specialized fixups */
+ if (directory_exists("/usr/X11R6")) {
+ msgNotify("Fixing permissions in XFree86 tree..");
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+
+ /* Also do bogus minimal package registration so ports don't whine */
+ if (file_readable("/usr/X11R6/lib/X11/pkgreg.tar.gz")) {
+ msgNotify("Installing package metainfo..");
+ vsystem("tar xpzf /usr/X11R6/lib/X11/pkgreg.tar.gz -C / && rm /usr/X11R6/lib/X11/pkgreg.tar.gz");
+ }
+ }
+ return DITEM_SUCCESS;
+}
+
+/* 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, *usrdev, *vardev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ extern int MakeDevChunk(Chunk *c, char *n);
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
+ 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 && (!MakeDevChunk(swapdev, "/dev") || !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;
+ }
+
+ if (!Fake) {
+ if (!swapon(dname))
+ 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/r%s", rootdev->name);
+ if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !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;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = vsystem("%s %s", root->newfs_cmd, dname);
+ if (i) {
+ msgConfirm("Unable to make new root filesystem on %s!\n"
+ "Command returned status %d", dname, i);
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, 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;
+ }
+ }
+
+ /* 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;
+ }
+ if (RunningAsInit && root && (root->newfs || upgrade)) {
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ MakeDevDisk(disk, "/mnt/dev");
+ }
+ else if (!RunningAsInit && !Fake)
+ MakeDevDisk(disk, "/dev");
+
+ 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) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == rootdev)
+ continue;
+
+ if (tmp->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == swapdev)
+ continue;
+ sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i)
+ 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->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ if (RunningAsInit) {
+ msgNotify("Copying initial device files..");
+ /* Copy the boot floppy's dev files */
+ if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't clone the /dev files!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ command_sort();
+ command_execute();
+ return DITEM_SUCCESS;
+}
+
+static char *
+getRelname(void)
+{
+ static char buf[64];
+ int 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);
+ 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, "lynx", 0);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/lynx", 0);
+ variable_set2(VAR_FTP_STATE, "passive", 0);
+ variable_set2(VAR_NFS_SECURE, "YES", 0);
+ variable_set2(VAR_PKG_TMPDIR, "/usr/tmp", 0);
+ variable_set2(VAR_GATED_PKG, "gated", 0);
+ variable_set2(VAR_PCNFSD_PKG, "pcnfsd", 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);
+ 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, 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..cade7a2
--- /dev/null
+++ b/usr.sbin/sysinstall/install.cfg
@@ -0,0 +1,96 @@
+# This is the installation configuration file for my test machine,
+# crate.cdrom.com.
+# It is included here merely as a sort-of-documented example.
+
+# 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.
+ftp=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 wd0. 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=wd0
+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
+wd0s1-1=ufs 40960 /
+# And a 20MB swap partition
+wd0s1-2=swap 40960 none
+# Followed by a /usr partition using all remaining space (size 0 = free space)
+wd0s1-3=ufs 0 /usr
+# Let's do it!
+diskLabelEditor
+
+################################
+
+################################
+# Now partition the 2nd disk.
+disk=wd1
+partition=exclusive
+diskPartitionEditor
+
+wd1s1-1=ufs 40960 /var
+wd1s1-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..b153e02
--- /dev/null
+++ b/usr.sbin/sysinstall/installUpgrade.c
@@ -0,0 +1,490 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: installUpgrade.c,v 1.65 1999/03/19 08:22:31 jkh Exp $
+ *
+ * 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, "XF86Config", 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, "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, "host.conf", TRUE, NULL },
+ { JUST_COPY, "hosts", TRUE, NULL },
+ { JUST_COPY, "hosts.equiv", TRUE, NULL },
+ { JUST_COPY, "hosts.lpd", TRUE, NULL },
+ { JUST_COPY, "inetd.conf", TRUE, NULL },
+ { JUST_COPY, "kerberosIV", TRUE, NULL },
+ { JUST_COPY, "localtime", TRUE, NULL },
+ { JUST_COPY, "login.access", TRUE, NULL },
+ { JUST_COPY, "login.conf", 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, "modems", TRUE, NULL },
+ { JUST_COPY, "motd", TRUE, NULL },
+ { JUST_COPY, "namedb", TRUE, NULL },
+ { JUST_COPY, "networks", TRUE, NULL },
+ { JUST_COPY, "newsyslog.conf", TRUE, NULL },
+ { JUST_COPY, "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, "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);
+ systemDisplayHelp("UPGRADE");
+
+ dialog_clear_norefresh();
+ if (msgYesNo("Given all that scary stuff you just read, are you sure you want to\n"
+ "risk it all and proceed with this upgrade?") != 0)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ 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 | DITEM_RESTORE;
+ dialog_clear_norefresh();
+ }
+ else if (!(Dists & DIST_BIN)) { /* No bin selected? Not much of an upgrade.. */
+ if (msgYesNo("You didn't select the bin 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 bin distribution? Chose No to bring up the Distributions\n"
+ "menu again.") != 0) {
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ dialog_clear_norefresh();
+ }
+ }
+
+ /* Still?! OK! They must know what they're doing.. */
+ if (!(Dists & DIST_BIN))
+ 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;
+ }
+ 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;
+ 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("/usr/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 | DITEM_RESTORE;
+ }
+
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /bin /sbin /usr/sbin /usr/bin /usr/lib /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 | DITEM_RESTORE;
+
+ if (!mediaDevice->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;
+ }
+
+ msgNotify("Beginning extraction of distributions..");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the bin 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_BIN)) {
+ 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 bin 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);
+ }
+
+ 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;
+}
+
+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_BIN;
+
+ 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 | 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;
+ }
+
+ 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 | DITEM_RESTORE;
+ }
+ chdir("/");
+ systemCreateHoloshell();
+ }
+
+ if (!mediaVerify() || !mediaDevice->init(mediaDevice)) {
+ msgNotify("Upgrade: Couldn't initialize media.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ saved_etc = "/usr/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 | DITEM_RESTORE;
+ }
+
+ 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 bin 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_BIN)) {
+ 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 bin 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 | DITEM_RESTORE;
+ }
+ 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..459f4cb
--- /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.
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.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/kget.c b/usr.sbin/sysinstall/kget.c
new file mode 100644
index 0000000..b3cc55e
--- /dev/null
+++ b/usr.sbin/sysinstall/kget.c
@@ -0,0 +1,151 @@
+/*-
+ * Copyright (c) 1999 Andrzej Bialecki <abial@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: kget.c,v 1.3 1999/02/16 01:58:04 jkh Exp $
+ */
+
+#include "sysinstall.h"
+#include <sys/sysctl.h>
+#include "i386/isa/isa_device.h"
+#include "i386/isa/pnp.h"
+
+int
+kget(char *out)
+{
+ int len, i, bytes_written = 0;
+ char *buf;
+ char *mib = "machdep.uc_devlist";
+ char *mib2 = "machdep.uc_pnplist";
+ char name[9];
+ FILE *fout;
+ struct isa_device *id;
+ struct pnp_cinfo *c;
+ char *p;
+
+ fout = fopen(out, "w");
+ if (fout == NULL) {
+ msgDebug("kget: Unable to open %s for writing.\n", out);
+ return -1;
+ }
+
+ /* We use sysctlbyname, because the oid is unknown (OID_AUTO) */
+ /* get the buffer size */
+ i = sysctlbyname(mib, NULL, &len, NULL, NULL);
+ if (i) {
+ msgDebug("kget: error buffer sizing\n");
+ return -1;
+ }
+ buf = (char *)malloc(len * sizeof(char));
+ i = sysctlbyname(mib, buf, &len, NULL, NULL);
+ if (i) {
+ msgDebug("kget: error retrieving data\n");
+ return -1;
+ }
+ i = 0;
+ while (i < len) {
+ id = (struct isa_device *)(buf + i);
+ p = (buf + i + sizeof(struct isa_device));
+ strncpy(name, p, 8);
+ if (!id->id_enabled) {
+ bytes_written += fprintf(fout, "di %s%d\n", name, id->id_unit);
+ } else {
+ bytes_written += fprintf(fout, "en %s%d\n", name, id->id_unit);
+ if (id->id_iobase > 0) {
+ bytes_written += fprintf(fout, "po %s%d %#x\n",
+ name, id->id_unit, id->id_iobase);
+ }
+ if (id->id_irq > 0) {
+ bytes_written += fprintf(fout, "ir %s%d %d\n", name,
+ id->id_unit, ffs(id->id_irq) - 1);
+ }
+ if (id->id_drq > 0) {
+ bytes_written += fprintf(fout, "dr %s%d %d\n", name,
+ id->id_unit, id->id_drq);
+ }
+ if (id->id_maddr > 0) {
+ bytes_written += fprintf(fout, "iom %s%d %#x\n", name,
+ id->id_unit, (u_int)id->id_maddr);
+ }
+ if (id->id_msize > 0) {
+ bytes_written += fprintf(fout, "ios %s%d %d\n", name,
+ id->id_unit, id->id_msize);
+ }
+ bytes_written += fprintf(fout, "f %s%d %#x\n", name,
+ id->id_unit, id->id_flags);
+ }
+ i += sizeof(struct isa_device) + 8;
+ }
+ free(buf), buf = NULL;
+ /* Now, print the changes to PnP override table */
+ i = sysctlbyname(mib2, NULL, &len, NULL, NULL);
+ if (i) {
+ /* Possibly our kernel doesn't support PnP. Ignore the error. */
+ msgDebug("kget: can't get PnP data - skipping...\n");
+ goto bail;
+ }
+ buf = (char *)malloc(len * sizeof(char));
+ i = sysctlbyname(mib2, buf, &len, NULL, NULL);
+ if (i) {
+ msgDebug("kget: error retrieving data\n");
+ goto bail;
+ }
+ i = 0;
+ /* Print the PnP override table. Taken from userconfig.c */
+ do {
+ c = (struct pnp_cinfo *)(buf + i);
+ if (c->csn >0 && c->csn != 255) {
+ int pmax, mmax;
+
+ if (c->enable == 0) {
+ bytes_written += fprintf(fout, "pnp %d %d disable\n",
+ c->csn, c->ldn);
+ continue;
+ }
+ bytes_written += fprintf(fout, "pnp %d %d %s irq0 %d irq1 %d drq0 %d drq1 %d",
+ c->csn, c->ldn, c->override ? "os":"bios",
+ c->irq[0], c->irq[1], c->drq[0], c->drq[1]);
+ if (c->flags)
+ bytes_written += fprintf(fout, " flags 0x%lx", c->flags);
+ pmax = 0;
+ while (c->port[pmax] != 0 && pmax < 8) {
+ bytes_written += fprintf(fout, " port%d %d", pmax, c->port[pmax]);
+ pmax++;
+ }
+ mmax = 0;
+ while (c->mem[mmax].base != 0 && mmax < 8) {
+ bytes_written += fprintf(fout, " mem%d %d",
+ mmax, (int)c->mem[mmax].base);
+ mmax++;
+ }
+ bytes_written += fprintf(fout,"\n");
+ }
+ } while ((i += sizeof(struct pnp_cinfo)) < len);
+bail:
+ fprintf(fout, "q\n");
+ fclose(fout);
+ if (buf)
+ free(buf);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/label.c b/usr.sbin/sysinstall/label.c
new file mode 100644
index 0000000..6b69e0f
--- /dev/null
+++ b/usr.sbin/sysinstall/label.c
@@ -0,0 +1,1303 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: label.c,v 1.87 1999/03/09 12:36:28 jkh Exp $
+ *
+ * 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/param.h>
+#include <sys/sysctl.h>
+
+/*
+ * 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
+
+/* The smallest root filesystem we're willing to create */
+#ifdef __alpha__
+#define ROOT_MIN_SIZE 40
+#else
+#define ROOT_MIN_SIZE 30
+#endif
+
+/* The default root filesystem size */
+#ifdef __alpha__
+#define ROOT_DEFAULT_SIZE 60
+#else
+#define ROOT_DEFAULT_SIZE 40
+#endif
+
+/* The smallest swap partition we want to create by default */
+#define SWAP_MIN_SIZE 16
+
+/* The smallest /usr partition we're willing to create by default */
+#define USR_MIN_SIZE 80
+
+/* The smallest /var partition we're willing to create by default */
+#define VAR_MIN_SIZE 20
+
+/* 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 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 | DITEM_RESTORE;
+}
+
+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))
+ 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))
+ 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);
+ }
+ i |= DITEM_RESTORE;
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ char *cp;
+
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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].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 int
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ int 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);
+
+ /* 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;
+ }
+ }
+ }
+
+ /* 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;
+ }
+ }
+ }
+ 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, u_long size)
+{
+ PartInfo *ret;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
+ strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
+ ret->newfs = newfs;
+ if (!size)
+ return ret;
+ return ret;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ if (!old) {
+ DialogX = 14;
+ DialogY = 16;
+ }
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ DialogX = DialogY = 0;
+ 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;
+
+ safe_free(tmp);
+ tmp = new_part(val, TRUE, 0);
+ 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[] = {
+ "FS",
+ "A file system",
+ "Swap",
+ "A swap partition.",
+ };
+ DialogX = 7;
+ DialogY = 8;
+ 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, 2, 2, fs_types, selection, NULL, NULL);
+ DialogX = DialogY = 0;
+ if (!i) {
+ 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 *val;
+
+ val = msgGetInput(p->newfs_cmd,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val)
+ sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
+}
+
+#define MAX_MOUNT_NAME 12
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 8
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 7)
+#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;
+ int 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) + 2, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
+
+ 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;
+
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
+ label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
+ sz, (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;
+
+ /*
+ * 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))
+ 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)
+ newfs = "DOS";
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
+ newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
+ else if (label_chunk_info[i].type == PART_SWAP)
+ newfs = "SWAP";
+ else
+ newfs = "*";
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
+ 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 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, 49, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts T = Newfs Toggle U = Undo Q = Finish");
+ mvprintw(20, 0, "A = Auto Defaults for all!");
+ 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();
+}
+
+#ifdef __alpha__
+
+/*
+ * If there isn't a freebsd chunk already (i.e. there is no label),
+ * dedicate the disk.
+ */
+static void
+maybe_dedicate(Disk* d)
+{
+ struct chunk *c;
+
+ for (c = d->chunks->part; c; c = c->next) {
+ if (c->type == freebsd)
+ break;
+ }
+
+ if (!c) {
+ msgDebug("dedicating disk");
+ All_FreeBSD(d, 1);
+ }
+}
+
+#endif
+
+static int
+diskLabel(Device *dev)
+{
+ int sz, key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+#ifdef __alpha__
+ int i;
+#endif
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+#ifdef __alpha__
+ for (i = 0; devs[i]; i++) {
+ maybe_dedicate((Disk*) devs[i]->private);
+ }
+#endif
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+
+ 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 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ msg = "Not enough free space to create a new partition in the slice";
+ else {
+ struct chunk *tmp;
+ int mib[2];
+ int physmem;
+ size_t size, swsize;
+ char *cp;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
+ if (!rootdev) {
+ cp = variable_get(VAR_ROOT_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : ROOT_DEFAULT_SIZE) * ONE_MEG, part, FS_BSDFFS, CHUNK_IS_ROOT);
+ if (!tmp) {
+ msgConfirm("Unable to create the root partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!swapdev) {
+ cp = variable_get(VAR_SWAP_SIZE);
+ if (cp)
+ swsize = atoi(cp) * ONE_MEG;
+ else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ swsize = 16 * ONE_MEG + (physmem * 2 / 512);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ swsize, part, FS_SWAP, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the swap partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = 0;
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!vardev) {
+ cp = variable_get(VAR_VAR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = variable_get(VAR_NO_USR)
+ ? space_free(label_chunk_info[here].c)
+ : VAR_MIN_SIZE * ONE_MEG;
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Less than %dMB free for /var - you will need to\n"
+ "partition your disk manually with a custom install!",
+ (cp ? atoi(cp) : VAR_MIN_SIZE));
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/var", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!usrdev && !variable_get(VAR_NO_USR)) {
+ cp = variable_get(VAR_USR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Less than %dMB free for /usr - you will need to\n"
+ "partition your disk manually with a custom install!", USR_MIN_SIZE);
+ clear_wins();
+ break;
+ }
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/usr", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ }
+
+ /* At this point, we're reasonably "labelled" */
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ 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;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ DialogX = 3;
+ DialogY = 2;
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing M for\n"
+ "megabytes or C for cylinders. %d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ DialogX = DialogY = 0;
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM) {
+ if ((p = get_mountpoint(NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT)) {
+ if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
+ msgConfirm("This region cannot be used for your root partition as the\n"
+ "FreeBSD boot code cannot deal with a root partition created\n"
+ "in that location. Please choose another location or smaller\n"
+ "size for your root partition and try again!");
+ clear_wins();
+ break;
+ }
+ if (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, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+ if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
+ msgConfirm("This region cannot be used for your root partition as it starts\n"
+ "or extends past the 1024'th cylinder mark and is thus a\n"
+ "poor location to boot from. Please choose another\n"
+ "location (or smaller size) for your root partition and try again!");
+ Delete_Chunk(label_chunk_info[here].c->disk, tmp);
+ clear_wins();
+ break;
+ }
+ if (type != PART_SWAP) {
+ /* This is needed to tell the newfs -u about the size */
+ tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
+ safe_free(p);
+ }
+ else
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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 '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_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->newfs = FALSE;
+ if (label_chunk_info[here].type == PART_FAT
+ && (!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 (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "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)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgYesNo("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;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to start this\n"
+ "procedure again from the beginning.");
+ }
+ else if (!msgYesNo("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 '|':
+ if (!msgYesNo("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 (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes", 0);
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ 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;
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ dialog_clear();
+ 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;
+#ifdef __alpha__
+ maybe_dedicate(d);
+#endif
+ 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];
+ int entries = 1;
+
+ while (entries) {
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) != NULL) {
+ int sz;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ else {
+ Chunk *tmp;
+
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ }
+ else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ 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;
+ continue;
+ }
+ 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 {
+ tmp->private_data = new_part(mpoint, TRUE, sz);
+ tmp->private_free = safe_free;
+ status = DITEM_SUCCESS;
+ }
+ }
+ entries++;
+ }
+ else {
+ /* No more matches, leave the loop */
+ entries = 0;
+ }
+ }
+ }
+ 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) {
+ dialog_clear();
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(mpoint, newfs, 0);
+ 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..05a9fd5
--- /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.
+ *
+ * $Id: list.h,v 1.1 1997/09/16 17:03:58 pst Exp $
+ *
+ * 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..9f6c812
--- /dev/null
+++ b/usr.sbin/sysinstall/main.c
@@ -0,0 +1,141 @@
+/*
+ * 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.
+ *
+ * $Id: main.c,v 1.50 1999/01/08 00:14:22 jkh Exp $
+ *
+ * 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>
+
+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;
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+
+ /* 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;
+ }
+
+ /* 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?");
+ }
+
+ /* 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);
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake)
+ 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"
+ "If you can reproduce the problem, please turn Debug on in\n"
+ "the Options menu for the extra information it provides in\n"
+ "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
+#ifdef __alpha__
+ || !msgYesNo("Are you sure you wish to exit? The system will halt.")
+#else
+ || !msgYesNo("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies 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..10bda7c
--- /dev/null
+++ b/usr.sbin/sysinstall/media.c
@@ -0,0 +1,789 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: media.c,v 1.96 1999/02/15 00:49:33 jkh Exp $
+ *
+ * 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/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.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;
+
+/* 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)
+ mediaDevice->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 CDROM devices found! Please check that your system's\n"
+ "configuration is correct and that the CDROM 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 | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+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 | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice)
+ mediaDevice->private = NULL;
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+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 | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+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 | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice) {
+ char *val;
+
+ val = msgGetInput("/usr/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) | DITEM_RESTORE;
+}
+
+/*
+ * 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, hostname[MAXHOSTNAMELEN], *dir;
+ extern int FtpPort;
+ static Device *networkDev = NULL;
+ int what = DITEM_RESTORE;
+
+ mediaClose();
+ cp = variable_get(VAR_FTP_PATH);
+ /* If we've been through here before ... */
+ if (!variable_get(VAR_NONINTERACTIVE))
+ if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
+ cp = NULL;
+
+ if (!cp) {
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ else
+ cp = variable_get(VAR_FTP_PATH);
+ what = DITEM_RESTORE;
+ }
+ if (!cp)
+ return DITEM_FAILURE | what;
+ else if (!strcmp(cp, "other")) {
+ variable_set2(VAR_FTP_PATH, "ftp://", 0);
+ dialog_clear_norefresh();
+ 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 | what;
+ }
+ }
+ if (strncmp("ftp://", cp, 6)) {
+ msgConfirm("Sorry, %s is an invalid URL!", cp);
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ SAFE_STRCPY(ftpDevice.name, cp);
+ SAFE_STRCPY(hostname, cp + 6);
+
+ dialog_clear_norefresh();
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ if (!(networkDev = tcpDeviceSelect())) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ }
+ if (!networkDev->init(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetFTP: Net device init failed.\n");
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ if ((cp = index(hostname, ':')) != NULL) {
+ *(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 (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 inet_addr().\n", hostname);
+ if (inet_addr(hostname) == INADDR_NONE) {
+ if (isDebug())
+ msgDebug("Looking up hostname, %s, using gethostbyname().\n",
+ hostname);
+ if (gethostbyname(hostname) == NULL) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that"
+ " your\nname server, gateway and network interface are"
+ " correctly configured?", hostname);
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ }
+ 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 | what;
+}
+
+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
+mediaSetUFS(dialogMenuItem *self)
+{
+ static Device ufsDevice;
+ char *cp;
+
+ mediaClose();
+ dialog_clear_norefresh();
+ 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;
+ strcpy(ufsDevice.name, "ufs");
+ 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];
+ mediaClose();
+ dialog_clear_norefresh();
+ 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;
+ }
+ 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)
+ networkDev->shutdown(networkDev);
+ if (!(networkDev = tcpDeviceSelect()))
+ return DITEM_FAILURE;
+ }
+ if (!networkDev->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)
+ networkDev->shutdown(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_NFS_PATH);
+ return DITEM_FAILURE;
+ }
+ else
+ msgDebug("Found DNS entry for %s successfully..", 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 *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ 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(gunzip, gunzip, 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", gunzip, 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(), 0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), 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 gunzip returned status of %d!\n", 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 *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ 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(gunzip, gunzip, 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", gunzip, 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(), 0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), 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;
+ new.sa_mask = 0;
+ 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 gunzip returned status of %d!\n", 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) | DITEM_RESTORE;
+}
+
+/* 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;
+
+ dialog_clear_norefresh();
+ if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:", 0)) {
+ dialog_clear_norefresh();
+ 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) | DITEM_RESTORE;
+}
+
+/* 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..c0a5bd5
--- /dev/null
+++ b/usr.sbin/sysinstall/menus.c
@@ -0,0 +1,1524 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: menus.c,v 1.192 1999/03/09 12:36:28 jkh Exp $
+ *
+ * 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"
+
+#ifdef __alpha__
+#define _AS(str) str "alpha/"
+#else /* i386 */
+#define _AS(str) str "i386/"
+#endif
+#define _AP(str) _AS(str "/pub/FreeBSD/releases/")
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11All(dialogMenuItem *self)
+{
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11All(dialogMenuItem *self)
+{
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+ Dists &= ~DIST_XF86;
+ 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 | DIST_DES | extra))
+
+#define IS_USER(dist, extra) (_IS_SET(dist, _DIST_USER | extra) || \
+ _IS_SET(dist, _DIST_USER | DIST_DES | 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_BIN;
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+ return Dists == DIST_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
+DESFlagCheck(dialogMenuItem *item)
+{
+ return DESDists;
+}
+
+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 [ENTER].\n"
+ "Leave the index page by selecting Cancel [TAB-ENTER].",
+ "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 },
+ { "Console settings", "Customize system console behavior.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Configure", "The system configuration menu.", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Defaults, Load", "Load default settings.", NULL, dispatch_load_floppy },
+ { "Device, Mouse", "The mouse configuration menu.", NULL, dmenuSubmenu, NULL, &MenuMouse },
+ { "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, DES", "DES distribution menu.", NULL, dmenuSubmenu, NULL, &MenuDESDistributions },
+ { "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, 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 },
+ { "Fdisk", "The disk Partition Editor", NULL, diskPartitionEditor },
+ { "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 },
+ { "Install, Novice", "A novice system installation.", NULL, installNovice },
+ { "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", "Select CDROM 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 },
+ { "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 },
+ { "Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { "PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Register", "Register yourself or company as a FreeBSD user.", dmenuVarCheck, configRegister, NULL, "registered" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router" },
+ { "Syscons", "The system console configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Syscons, Font", "The console screen font.", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "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 },
+ { "Syscons, Screenmap", "The console screenmap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "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 },
+ { "XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "/stand/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 by pressing [ENTER] or [TAB-ENTER] to exit the installation.",
+ "Press F1 for Installation Guide", /* help line */
+ "install", /* help file */
+ { { "Select" },
+ { "Exit Install", NULL, NULL, dmenuExit },
+ { "1 Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "2 Novice", "Begin a novice installation (for beginners)", NULL, installNovice },
+ { "3 Express", "Begin a quick installation (for the impatient)", NULL, installExpress },
+ { "4 Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "5 Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "D Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "K Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "O Options", "View/Set various installation options", NULL, optionsEditor },
+ { "F Fixit", "Enter repair mode with CDROM/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "U Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "L Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "I 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",
+ { { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "README" },
+ { "2 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "HARDWARE" },
+ { "3 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "INSTALL" },
+ { "4 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "5 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "RELNOTES" },
+ { "6 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "7 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuMouseType = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "Select a protocol type for your mouse",
+ "If you are not sure, choose \"Auto\". It should always work for bus\n"
+ "and PS/2 style mice. It may not work for the serial mouse if the mouse\n"
+ "does not support the PnP standard. But, it won't hurt. Many 2-button\n"
+ "serial mice are compatible with \"Microsoft\" or \"MouseMan\". 3-button\n"
+ "serial mice may be compatible with \"MouseSystems\" or \"MouseMan\". If\n"
+ "the mouse has a wheel, it may be compatible with \"IntelliMouse\".",
+ NULL,
+ NULL,
+ { { "Auto", "Bus mouse, PS/2 style mouse or PnP serial mouse",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=auto" },
+ { "GlidePoint", "ALPS GlidePoint pad (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=glidepoint" },
+ { "Hitachi","Hitachi tablet (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmhittab" },
+ { "IntelliMouse", "Microsoft IntelliMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=intellimouse" },
+ { "Logitech", "Logitech protocol (old models) (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=logitech" },
+ { "Microsoft", "Microsoft protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=microsoft" },
+ { "MM Series","MM Series protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mmseries" },
+ { "MouseMan", "Logitech MouseMan/TrackMan models (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mouseman" },
+ { "MouseSystems", "MouseSystems protocol (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=mousesystems" },
+ { "ThinkingMouse","Kensington ThinkingMouse (serial)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_TYPE "=thinkingmouse" },
+ { NULL } },
+};
+
+DMenu MenuMousePort = {
+ DMENU_RADIO_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,
+ { { "COM1", "Serial mouse on COM1 (/dev/cuaa0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa0" },
+ { "COM2", "Serial mouse on COM2 (/dev/cuaa1)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa1" },
+ { "COM3", "Serial mouse on COM3 (/dev/cuaa2)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa2" },
+ { "COM4", "Serial mouse on COM4 (/dev/cuaa3)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/cuaa3" },
+ { "BusMouse", "Logitech, ATI or MS bus mouse (/dev/mse0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/mse0" },
+ { "PS/2", "PS/2 style mouse (/dev/psm0)",
+ dmenuVarCheck, dmenuSetVariable, NULL, VAR_MOUSED_PORT "=/dev/psm0" },
+ { NULL } },
+};
+
+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 4 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,
+ { { "1 Type", "Select mouse protocol type", NULL, dmenuSubmenu, NULL, &MenuMouseType },
+ { "2 Port", "Select mouse port", NULL, dmenuSubmenu, NULL, &MenuMousePort },
+ { "3 Enable", "Test and run the mouse daemon", NULL, mousedTest, NULL, NULL },
+ { "4 Disable", "Disable the mouse daemon", NULL, mousedDisable, NULL, NULL },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first tool, XF86Setup, is fully graphical and requires the\n"
+ "VGA16 server in order to work (should have been selected by\n"
+ "default, but if you de-selected it then you won't be able to\n"
+ "use this fancy setup tool). The second tool, 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 fancier one\n"
+ "does not.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "XF86Setup", "Fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF86Setup" },
+ { "xf86config", "Shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { "XF98Setup", "Fully graphical XFree86 configuration tool (PC98).",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF98Setup" },
+ { NULL } },
+};
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CDROM type",
+ "FreeBSD can be installed directly from a CDROM containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CDROM drive was found on your system. Please select one\n"
+ "of the following CDROM 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 chose 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 the Primary site is\n"
+ "guaranteed to carry the full range of possible distributions.",
+ "Select a site that's close!",
+ "install",
+ { { "Primary Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://ftp.freebsd.org/pub/FreeBSD/releases/") },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { "4.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://current.freebsd.org/pub/FreeBSD/snapshots/") },
+ { "3.0 SNAP Server", "releng3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://releng3.freebsd.org/pub/FreeBSD/snapshots/") },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ar.freebsd.org") },
+ { "Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.au.freebsd.org") },
+ { "Australia #2", "ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.au.freebsd.org") },
+ { "Australia #3", "ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.au.freebsd.org") },
+ { "Australia #4", "ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.au.freebsd.org") },
+ { "Australia #5", "ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.au.freebsd.org") },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.br.freebsd.org") },
+ { "Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.br.freebsd.org") },
+ { "Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.br.freebsd.org") },
+ { "Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.br.freebsd.org") },
+ { "Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.br.freebsd.org") },
+ { "Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.br.freebsd.org") },
+ { "Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp7.br.freebsd.org") },
+ { "Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ca.freebsd.org") },
+ { "Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.cz.freebsd.org") },
+ { "Denmark", "ftp.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.dk.freebsd.org") },
+ { "Denmark #2", "ftp2.dk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.dk.freebsd.org") },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ee.freebsd.org") },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.fi.freebsd.org") },
+ { "France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.fr.freebsd.org") },
+ { "France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.fr.freebsd.org") },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.de.freebsd.org") },
+ { "Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.de.freebsd.org") },
+ { "Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.de.freebsd.org") },
+ { "Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.de.freebsd.org") },
+ { "Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.de.freebsd.org") },
+ { "Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.de.freebsd.org") },
+ { "Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp7.de.freebsd.org") },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.nl.freebsd.org") },
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.hk.super.net") },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.is.freebsd.org") },
+ { "Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ie.freebsd.org") },
+ { "Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.il.freebsd.org") },
+ { "Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.il.freebsd.org") },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.jp.freebsd.org") },
+ { "Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.jp.freebsd.org") },
+ { "Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.jp.freebsd.org") },
+ { "Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.jp.freebsd.org") },
+ { "Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.jp.freebsd.org") },
+ { "Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.jp.freebsd.org") },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.kr.freebsd.org") },
+ { "Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.kr.freebsd.org") },
+ { "Korea #3", "ftp3.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.kr.freebsd.org") },
+ { "Korea #4", "ftp4.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.kr.freebsd.org") },
+ { "Korea #5", "ftp5.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.kr.freebsd.org") },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.pl.freebsd.org") },
+ { "Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.pt.freebsd.org") },
+ { "Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.pt.freebsd.org") },
+ { "Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.ru.freebsd.org") },
+ { "Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.ru.freebsd.org") },
+ { "Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.ru.freebsd.org") },
+ { "Russia #4", "ftp4.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.ru.freebsd.org") },
+ { "South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.za.freebsd.org") },
+ { "South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.za.freebsd.org") },
+ { "South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.za.freebsd.org") },
+ { "South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.za.freebsd.org") },
+ { "Spain", "ftp.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.es.freebsd.org") },
+ { "Spain #2", "ftp2.es.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.es.freebsd.org") },
+ { "Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.se.freebsd.org") },
+ { "Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.se.freebsd.org") },
+ { "Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.se.freebsd.org") },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.tw.freebsd.org") },
+ { "Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.tw.freebsd.org") },
+ { "Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.tw.freebsd.org") },
+ { "Thailand", "ftp.nectec.or.th", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AS("=ftp://ftp.nectec.or.th/pub/mirrors/FreeBSD/") },
+ { "UK", "ftp.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.uk.freebsd.org") },
+ { "UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.uk.freebsd.org") },
+ { "UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.uk.freebsd.org") },
+ { "UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.uk.freebsd.org") },
+ { "USA", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp.freebsd.org") },
+ { "USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp2.freebsd.org") },
+ { "USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp3.freebsd.org") },
+ { "USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp4.freebsd.org") },
+ { "USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp5.freebsd.org") },
+ { "USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH _AP("=ftp://ftp6.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 a fairly recent (2.0R or later)\n"
+ "version of FreeBSD.",
+ "Press F1 to read network configuration manual",
+ "network_device",
+ { { 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 CDROM 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 CDROM", "Install from a FreeBSD CDROM", 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 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "5 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "6 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "7 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "8 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "9 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]. When you are finished, chose the Exit\n"
+ "item or press [ENTER].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "1 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "2 X-Developer", "Same as above + X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "3 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "4 X-Kern-Developer", "Same as above + X Window System",
+ checkDistXKernDeveloper, distSetXKernDeveloper },
+ { "5 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "6 X-User", "Same as above + X Window System",
+ checkDistXUser, distSetXUser },
+ { "7 Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "8 Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { "8 All", "All sources and binaries (incl X Window System)",
+ checkDistEverything, distSetEverything },
+ { "9 Clear", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "0 Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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 \"bin\". WARNING: Do not export the\n"
+ "DES distribution out of the U.S.! It is for U.S. customers only.",
+ NULL,
+ NULL,
+ { { "bin", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BIN },
+ { "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 },
+ { "DES", "DES encryption code - NOT FOR EXPORT!",
+ DESFlagCheck, distSetDES },
+ { "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 but DES",
+ srcFlagCheck, distSetSrc },
+ { "ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+ { "XFree86", "The XFree86 3.3.3.1 distribution",
+ x11FlagCheck, distSetXF86 },
+ { "All", "All sources, binaries and X Window System binaries",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuDESDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the encryption facilities you wish to install.",
+ "Please check off any special DES-based encryption distributions\n"
+ "you would like to install. Please note that these services are NOT FOR\n"
+ "EXPORT from the United States. For information on non-U.S. FTP\n"
+ "distributions of this software, please consult the release notes.",
+ NULL,
+ NULL,
+ { { "des", "Basic DES encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_DES, },
+ { "krb", "Kerberos encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_KERBEROS },
+ { "skerbero", "Sources for Kerberos IV",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SKERBEROS },
+ { "ssecure", "Sources for DES",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SSECURE },
+ { "scrypto", "Export controlled crypto sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SCRYPTO },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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,
+ { { "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 },
+ { "share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { "sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { "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 },
+ { "All", "Select all of the above",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 3.3.3.1 Distribution",
+ "Please select the components you need from the XFree86 3.3.3.1\n"
+ "distribution sets.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "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 },
+ { "All", "Select all XFree86 distribution sets", NULL, setX11All },
+ { "Clear", "Reset XFree86 distribution list", NULL, clearX11All },
+ { "Exit", "Exit this menu (returning to previous)", checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 3.3.3.1 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.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { "cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { "doc", "READMEs and release notes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { "html", "HTML documentation files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_HTML },
+ { "lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+ { "lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+ { "lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+ { "man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { "prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { "set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+ { "9set", "XFree86 Setup Utility for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_9SET },
+ { "sources", "XFree86 3.3.3.1 standard sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SRC },
+ { "csources", "XFree86 3.3.3.1 contrib sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CSRC },
+ { "All", "Select all of the above",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "fnts", "Standard 75 DPI and miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_MISC },
+ { "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 },
+ { "non", "Japanese, Chinese and other non-english fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_NON },
+ { "server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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"
+ "If you are unsure as to which server will work for your graphics card,\n"
+ "it is recommended that try the SVGA or VGA16 servers or, for PC98\n"
+ "machines, the 9EGC or 9840 servers.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "SVGA", "Standard VGA or Super VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_SVGA },
+ { "VGA16", "Standard 16 color VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VGA16 },
+ { "Mono", "Standard Monochrome card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MONO },
+ { "8514", "8-bit (256 color) IBM 8514 or compatible card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_8514 },
+ { "AGX", "8-bit AGX card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_AGX },
+ { "I128", "8, 16 and 24-bit #9 Imagine I128 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_I128 },
+ { "Ma8", "8-bit ATI Mach8 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH8 },
+ { "Ma32", "8 and 16-bit (65K color) ATI Mach32 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH32 },
+ { "Ma64", "8 and 16-bit (65K color) ATI Mach64 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH64 },
+ { "P9K", "8, 16, and 24-bit color Weitek P9000 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_P9000 },
+ { "S3", "8, 16 and 24-bit color S3 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3 },
+ { "S3V", "8, 16 and 24-bit color S3 Virge based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3V },
+ { "W32", "8-bit ET4000/W32, /W32i and /W32p cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_W32 },
+ { "nest", "A nested server for testing purposes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { "PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectPC98Server = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "PC98 X Server selection.",
+ "Please check off the types of NEC PC98 X servers you wish to install.\n\
+If you are unsure as to which server will work for your graphics card,\n\
+it is recommended that try the SVGA or VGA16 servers (the VGA16 and\n\
+Mono servers are particularly well-suited to most LCD displays).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "9480", "PC98 8-bit (256 color) PEGC-480 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9480 },
+ { "9EGC", "PC98 4-bit (16 color) EGC card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9EGC },
+ { "9GA9", "PC98 GA-968V4/PCI (S3 968) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GA9 },
+ { "9GAN", "PC98 GANB-WAP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GAN },
+ { "9LPW", "PC98 PowerWindowLB (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9LPW },
+ { "9MGA", "PC98 MGA (Matrox) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9MGA },
+ { "9NKV", "PC98 NKV-NEC (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9NKV },
+ { "9NS3", "PC98 NEC (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9NS3 },
+ { "9SPW", "PC98 SKB-PowerWindow (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9SPW },
+ { "9SVG", "PC98 generic SVGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9SVG },
+ { "9TGU", "PC98 Cyber9320 and TGUI9680 cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9TGU },
+ { "9WEP", "PC98 WAB-EP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WEP },
+ { "9WS", "PC98 WABS (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WS },
+ { "9WSN", "PC98 WSN-A2F (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WSN },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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]. To de-select it, press [SPACE] again.\n\n"
+ "Select OK or Cancel to 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 chose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "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",
+ { { "1 Options", "View/Set various installation options", NULL, optionsEditor },
+#ifdef __alpha__
+ { "2 Label", "Label disk partitions", NULL, diskLabelEditor },
+ { "3 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "4 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+#else
+ { "2 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "3 Label", "Label allocated 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 },
+#endif
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_RADIO_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 } },
+};
+
+/* 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",
+ { { "D Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { "P Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { "R Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "L Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { "F Fdisk", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+ { "1 User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { "2 Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "3 Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "4 Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Mouse", "Configure your mouse",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { "6 Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "7 Startup", "Configure system startup options",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { "8 Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { "D HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+ { "X XFree86", "Configure XFree86",
+ NULL, configXEnvironment },
+ { "U Register", "Register yourself or company as a FreeBSD user.", NULL, configRegister },
+ { "E Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { 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. Remember to use SPACE to select items! The\n"
+ "RETURN key will leave this menu (as with all checkbox menus).",
+ NULL,
+ NULL,
+ { { "APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+ { "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" },
+ { " ", " -- ", 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, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { "nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, dmenuToggleVariable, 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" },
+ { "linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "linux_enable=YES" },
+ { "quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { "SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { 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,
+ { { "Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { "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" },
+ { "AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { "AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { "TCP Extensions", "Allow RFC1323 and RFC1644 TCP extensions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extensions=YES" },
+ { "Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+ { "Ntpdate", "Select a clock-synchronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', "ntpdate_enable=YES" },
+ { "router", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router" },
+ { "Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { "Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_RADIO_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 },
+ { "Australia", "ntp.syd.dms.csiro.au (HP 5061 Cesium Beam)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.syd.dms.csiro.au" },
+ { "Canada", "tick.usask.ca (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.usask.ca" },
+ { "France", "canon.inria.fr (TDF clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=canon.inria.fr" },
+ { "Germany", "ntps1-{0,1,2}.uni-erlangen.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.uni-erlangen.de" },
+ { "Germany #2", "ntps1-0.cs.tu-berlin.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.cs.tu-berlin.de" },
+ { "Japan", "clock.nc.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.nc.fukuoka-u.ac.jp" },
+ { "Japan #2", "clock.tl.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tl.fukuoka-u.ac.jp" },
+ { "Netherlands", "ntp0.nl.net (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.nl.net" },
+ { "Norway", "timer.unik.no (NTP clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timer.unik.no" },
+ { "Sweden", "Time1.Stupi.SE (Cesium/GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=Time1.Stupi.SE" },
+ { "Switzerland", "swisstime.ethz.ch (DCF77 clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=swisstime.ethz.ch" },
+ { "U.S. East Coast", "bitsy.mit.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bitsy.mit.edu" },
+ { "U.S. East Coast #2", "otc1.psu.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=otc1.psu.edu" },
+ { "U.S. West Coast", "apple.com (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=apple.com" },
+ { "U.S. West Coast #2", "clepsydra.dec.com (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clepsydra.dec.com" },
+ { "U.S. West Coast #3", "clock.llnl.gov (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.llnl.gov" },
+ { "U.S. Midwest", "ncar.ucar.edu (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ncar.ucar.edu" },
+ { "U.S. Pacific", "chantry.hawaii.net (WWV/H clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chantry.hawaii.net" },
+ { "U.S. Southwest", "shorty.chpc.utexas.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=shorty.chpc.utexas.edu" },
+ { NULL } },
+};
+
+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,
+ { { "Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeymap = {
+ DMENU_RADIO_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" },
+ { "Croatian ISO", "Croatian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=hr.iso" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { "Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "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" },
+ { "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" },
+ { "Latin American", "Latin American ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=lat-amer" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Polish ISO", "Polish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=pl_PL.ISO_8859-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 CP866", "Russian CP866 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.cp866" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "Slovenian", "Slovenian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=si.iso.acc" },
+ { "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", "Swiss French ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissfrench.iso" },
+ { "Swiss German", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso" },
+ { "U.K. CP850", "United Kingdom Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { "U.K. ISO", "United Kingdom ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { "U.S. Dvorak", "United States Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { "U.S. ISO", "United States ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_RADIO_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_RADIO_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,
+ { { "Blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "Daemon", "\"BSD Daemon\" animated screen saver (text)",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "Fade", "Fade out effect screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=fade" },
+ { "Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "Logo", "\"BSD Daemon\" animated screen saver (graphics)",
+ dmenuVarCheck, configSaver, NULL, "saver=logo" },
+ { "Rain", "Rain drops screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=rain" },
+ { "Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Warp", "A \"stars warping\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=warp" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_RADIO_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,
+ { { "None", "No screenmap, use default font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_RADIO_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,
+ { { "None", "Use default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "IBM 437", "English", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "IBM 866", "Russian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866-8x16" },
+ { "ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "KOI8-R", "Russian, KOI8-R encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=koi8-r-8x8,font8x14=koi8-r-8x14,font8x16=koi8-r-8x16" },
+ { NULL } },
+};
+
+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,
+ { { "Add user", "Add a new user to the system.", NULL, userAddUser },
+ { "Add group", "Add a new user group to the system.", NULL, userAddGroup },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { 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 2nd FreeBSD CDROM, 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",
+{ { "1 CDROM", "Use the 2nd \"live\" CDROM from the distribution", NULL, installFixitCDROM },
+ { "2 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "3 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..bb00639
--- /dev/null
+++ b/usr.sbin/sysinstall/misc.c
@@ -0,0 +1,485 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $Id: misc.c,v 1.37 1998/01/16 15:07:55 jkh Exp $
+ *
+ * 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/dkbad.h>
+#include <sys/disklabel.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 %d!", 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 %d!", size);
+ ptr = realloc(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 *)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 = 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
+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;
+}
+
+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/mouse.c b/usr.sbin/sysinstall/mouse.c
new file mode 100644
index 0000000..4aa01b8
--- /dev/null
+++ b/usr.sbin/sysinstall/mouse.c
@@ -0,0 +1,83 @@
+/*-
+ * 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.
+ *
+ * $Id: mouse.c,v 1.4 1998/03/23 06:08:47 yokota Exp $
+ */
+
+#include "sysinstall.h"
+#include <string.h>
+
+int
+mousedTest(dialogMenuItem *self)
+{
+ char *type;
+ char *port;
+ int ret;
+
+ type = variable_get(VAR_MOUSED_TYPE);
+ port = variable_get(VAR_MOUSED_PORT);
+ 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");
+ 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;
+}
+
+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);
+ msgConfirm("The mouse daemon is disabled.");
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/msg.c b/usr.sbin/sysinstall/msg.c
new file mode 100644
index 0000000..e61b767
--- /dev/null
+++ b/usr.sbin/sysinstall/msg.c
@@ -0,0 +1,320 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: msg.c,v 1.46 1997/09/09 16:27:50 jkh Exp $
+ *
+ * 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 <machine/console.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;
+
+ 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);
+}
+
+/* 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_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 1 for YES, 0 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+
+ 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);
+ }
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ 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;
+
+ 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);
+ 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;
+
+ 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();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm(str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify(str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/network.c b/usr.sbin/sysinstall/network.c
new file mode 100644
index 0000000..f789bcd
--- /dev/null
+++ b/usr.sbin/sysinstall/network.c
@@ -0,0 +1,311 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: network.c,v 1.34 1999/02/05 22:15:51 jkh Exp $
+ *
+ * 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 <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];
+
+ 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;
+ }
+ }
+
+ /* Old PPP process lying around? */
+ if (pppPID) {
+ msgNotify("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];
+
+ dialog_clear_norefresh();
+ /* 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!");
+ 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(attach)) {
+ msgConfirm("slattach returned a bad status! Please verify that\n"
+ "the command is correct and try this operation again.");
+ return FALSE;
+ }
+ }
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (!cp) {
+ 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;
+ }
+ msgNotify("ifconfig %s %s", 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 {
+ msgNotify("Adding default route to %s.", rp);
+ vsystem("route -n add default %s", rp);
+ }
+ 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;
+ msgNotify("ifconfig %s down", 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) {
+ msgNotify("Deleting default route.");
+ vsystem("route -n delete default");
+ }
+ }
+ else if (pppPID) {
+ msgNotify("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;
+ FILE *fp;
+ char *val;
+ pid_t pid = 0;
+ char myaddr[16], provider[16], speed[16];
+
+ /* 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", "w");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (!fp) {
+ msgConfirm("Couldn't open /etc/ppp/ppp.conf file! This isn't going to work");
+ return 0;
+ }
+ fprintf(fp, "default:\n");
+ fprintf(fp, " set speed %s\n", speed);
+ fprintf(fp, " set device %s\n", devp->devname);
+ fprintf(fp, " set ifaddr %s %s\n", myaddr, provider);
+ fprintf(fp, " set timeout 0\n");
+ fprintf(fp, " enable dns\n");
+ fprintf(fp, " set log local phase\n");
+ if (fchmod(fileno(fp), 0640) != 0)
+ msgConfirm("Warning: Failed to fix permissions on /etc/ppp/ppp.conf !");
+ fclose(fp);
+
+ 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!");
+ 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", (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). The only command\n"
+ "you'll probably want or need to use is the \"term\" command\n"
+ "which starts a terminal emulator you can use to talk to your\n"
+ "modem and dial the service provider. Once you're connected,\n"
+ "come back to this screen and press return.\n\n"
+ "DO NOT PRESS [ENTER] HERE UNTIL THE CONNECTION IS FULLY\n"
+ "ESTABLISHED!");
+ }
+ return pid;
+}
diff --git a/usr.sbin/sysinstall/nfs.c b/usr.sbin/sysinstall/nfs.c
new file mode 100644
index 0000000..aeb1dc0
--- /dev/null
+++ b/usr.sbin/sysinstall/nfs.c
@@ -0,0 +1,93 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: nfs.c,v 1.18 1997/02/22 14:12:14 peter Exp $
+ *
+ * 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/syslimits.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+Boolean NFSMounted;
+static char mountpoint[] = "/dist";
+
+Boolean
+mediaInitNFS(Device *dev)
+{
+ Device *netDevice = (Device *)dev->private;
+
+ if (NFSMounted)
+ return TRUE;
+
+ if (netDevice && !netDevice->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",
+ variable_get(VAR_SLOW_ETHER) ? "-r 1024 -w 1024" : "",
+ variable_get(VAR_NFS_SECURE) ? "-P" : "", dev->name, mountpoint)) {
+ msgConfirm("Error mounting %s on %s: %s.", dev->name, mountpoint, strerror(errno));
+ if (netDevice)
+ netDevice->shutdown(netDevice);
+ return FALSE;
+ }
+ NFSMounted = TRUE;
+ if (isDebug())
+ msgDebug("Mounted NFS device %s onto %s\n", dev->name, mountpoint);
+ return TRUE;
+}
+
+FILE *
+mediaGetNFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet(mountpoint, file);
+}
+
+void
+mediaShutdownNFS(Device *dev)
+{
+ if (!NFSMounted)
+ return;
+
+ msgNotify("Unmounting NFS partition on %s", 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..5365d77
--- /dev/null
+++ b/usr.sbin/sysinstall/options.c
@@ -0,0 +1,306 @@
+/*
+ * 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.
+ *
+ * $Id: options.c,v 1.57 1999/02/05 22:15:51 jkh Exp $
+ *
+ * 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>
+
+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 RELNAME_PROMPT "Please specify the release you wish to load or\n\"none\" 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:"
+#define GATED_PKG_PROMPT "Please specify the package name for the gated software:"
+#define PCNFSD_PKG_PROMPT "Please specify the package name for the PCNFSD server:"
+
+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 },
+{ "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 },
+{ "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 },
+{ "Gated package", "The name of the gated package to install if requested",
+ OPT_IS_VAR, GATED_PKG_PROMPT, VAR_GATED_PKG, varCheck },
+{ "PCNFSD package", "The name of the PCNFSD package to install if requested",
+ OPT_IS_VAR, PCNFSD_PKG_PROMPT, VAR_PCNFSD_PKG, 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, "%d", (int)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);
+ if (rcode & (DITEM_RECREATE | DITEM_RESTORE))
+ 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))
+ variable_unset(opt.aux);
+ else
+ variable_set2(opt.aux, "YES", 1);
+ }
+ if (opt.check)
+ opt.check(opt);
+ refresh();
+ return status;
+}
+
+int
+optionsEditor(dialogMenuItem *self)
+{
+ int i, optcol, optrow, key;
+ static int currOpt = 0;
+
+ dialog_clear_norefresh();
+ 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();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+
+ default:
+ beep();
+ }
+ }
+ /* NOTREACHED */
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
diff --git a/usr.sbin/sysinstall/package.c b/usr.sbin/sysinstall/package.c
new file mode 100644
index 0000000..a1991a2
--- /dev/null
+++ b/usr.sbin/sysinstall/package.c
@@ -0,0 +1,219 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: package.c,v 1.65 1997/10/15 04:37:16 jkh Exp $
+ *
+ * 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/time.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+static Boolean sigpipe_caught = FALSE;
+
+static void
+catch_pipe(int sig)
+{
+ sigpipe_caught = TRUE;
+}
+
+/* Like package_extract, but assumes current media device */
+int
+package_add(char *name)
+{
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+ return package_extract(mediaDevice, name, FALSE);
+}
+
+/* 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_exists(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[511];
+ int ret, last_msg = 0;
+ FILE *fp;
+
+ /* Check to make sure it's not already there */
+ if (package_exists(name))
+ return DITEM_SUCCESS;
+
+ /* If necessary, initialize the ldconfig hints */
+ if (!file_readable("/var/run/ld.so.hints"))
+ vsystem("ldconfig /usr/lib /usr/local/lib /usr/X11R6/lib");
+
+ if (!dev->init(dev)) {
+ msgConfirm("Unable to initialize media type for package extract.");
+ return DITEM_FAILURE;
+ }
+
+ /* Be initially optimistic */
+ ret = DITEM_SUCCESS | DITEM_RESTORE;
+ /* 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, "/usr/tmp", 0);
+ }
+ Mkdir(variable_get(VAR_PKG_TMPDIR));
+ vsystem("chmod 1777 %s", variable_get(VAR_PKG_TMPDIR));
+
+ if (!index(name, '/'))
+ sprintf(path, "packages/All/%s%s", name, strstr(name, ".tgz") ? "" : ".tgz");
+ else
+ sprintf(path, "%s%s", name, strstr(name, ".tgz") ? "" : ".tgz");
+ fp = dev->get(dev, path, TRUE);
+ if (fp) {
+ int i = 0, tot, pfd[2];
+ pid_t pid;
+
+ signal(SIGPIPE, catch_pipe);
+ 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);
+ close(2);
+ close(pfd[1]);
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-", 0);
+ if (isDebug())
+ msgDebug("pkg_add returns %d status\n", i);
+ }
+ else {
+ char buf[BUFSIZ];
+ WINDOW *w = savescr();
+ 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) / 1024.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);
+ if (sigpipe_caught || i < 0 || WEXITSTATUS(tot)) {
+ 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);
+ sigpipe_caught = FALSE;
+ }
+ }
+ 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 | DITEM_RESTORE;
+ }
+ return ret;
+}
diff --git a/usr.sbin/sysinstall/rtermcap.c b/usr.sbin/sysinstall/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sysinstall/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/sysinstall.8 b/usr.sbin/sysinstall/sysinstall.8
new file mode 100644
index 0000000..c6e7fcb
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.8
@@ -0,0 +1,802 @@
+.\" 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.
+.\"
+.\" $Id: sysinstall.8,v 1.15 1999/02/05 09:28:16 jkh Exp $
+.\"
+.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
+.Nm
+is a utility for installing and configuring FreeBSD systems.
+It is the first utility invoked by the FreeBSD installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed FreeBSD systems for use in later configuring the system.
+.Pp
+The
+.Nm
+program is generally invoked without arguments for the default
+behavior, where the main installation/configuration menu is presented.
+
+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.
+.Pp
+.Sh NOTES
+.Nm
+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 FreeBSD systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the FreeBSD 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
+.Nm
+currently uses the
+.Xr libdialog 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 ``xterm-color'' termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+be replaced in FreeBSD 3.1 (hopefully) by the
+.Xr setup 1
+utility.
+.Sh RUNNING SCRIPTS
+.Nm
+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 -compact
+.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:
+.nf
+
+/stand/sysinstall _ftpPath=ftp:/ziggy/pub/ mediaSetFTP configPackages
+
+.fi
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Pp
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+
+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. When and where a function
+depends on the settings of one or more variables will be noted in the
+following table:
+
+.Pp
+\fBFunction Glossary:\fR
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+\fBVariables:\fR None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g. ``routed'' or ``gated'', otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+\fBVariables:\fR None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It configRegister
+Register the user with the FreeBSD counter.
+.Pp
+\fBVariables:\fR None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+\fBVariables:\fR None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+\fBVariables:\fR None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+\fBVariables:\fR
+.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 FreeBSD,
+.Ar all
+to use the entire disk for FreeBSD but maintain a proper partition
+table,
+.Ar existing
+to use an existing FreeBSD partition (first found),
+.Ar exclusive
+to use the disk in ``dangerously dedicated'' mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new FreeBSD 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.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, a 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
+\fBVariables:\fR 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 FreeBSD
+(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
+FreeBSD partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar da0s2
+for the whole FreeBSD 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 FreeBSD. 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"
+With the balance of free space (around 316MB) going to the /usr
+file system.
+.El
+
+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:
+.nf
+ da0s1=/dos_c N
+
+.fi
+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
+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
+\fBVariables:\fR None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+\fBVariables:\fR None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+\fBVariables:\fR
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indent
+.It Li bin
+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 des
+DES encryption binaries and libraries.
+.It Li compat1x
+Compatibility with FreeBSD 1.x
+.It Li compat20
+Compatibility with FreeBSD 2.0
+.It Li compat21
+Compatibility with FreeBSD 2.1
+.It Li ports
+The ports collection.
+.It Li krb
+Kerberos binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sebones
+/usr/src/eBones
+.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 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 3.3.3.1 binaries.
+.It Li Xcfg
+XFree86 3.3.3.1 configuration files.
+.It Li Xdoc
+XFree86 3.3.3.1 documentation.
+.It Li Xhtml
+XFree86 3.3.3.1 HTML documentation.
+.It Li Xlib
+XFree86 3.3.3.1 libraries.
+.It Li Xlk98
+XFree86 3.3.3.1 server link-kit for PC98 machines.
+.It Li Xlkit
+XFree86 3.3.3.1 server link-kit for standard machines.
+.It Li Xman
+XFree86 3.3.3.1 manual pages.
+.It Li Xprog
+XFree86 3.3.3.1 programmer's distribution.
+.It Li Xps
+XFree86 3.3.3.1 postscript documentation.
+.It Li Xset
+XFree86 3.3.3.1 graphical setup tool.
+.It Li X8514
+XFree86 3.3.3.1 8514 server.
+.It Li X9480
+XFree86 3.3.3.1 PC98 8-bit (256 color) PEGC-480 server.
+.It Li X9EGC
+XFree86 3.3.3.1 PC98 4-bit (16 color) EGC server.
+.It Li X9GA9
+XFree86 3.3.3.1 PC98 GA-968V4/PCI (S3 968) server.
+.It Li X9GAN
+XFree86 3.3.3.1 PC98 GANB-WAP (cirrus) server.
+.It Li X9LPW
+XFree86 3.3.3.1 PC98 PowerWindowLB (S3) server.
+.It Li X9NKV
+XFree86 3.3.3.1 PC98 NKV-NEC (cirrus) server.
+.It Li X9NS3
+XFree86 3.3.3.1 PC98 NEC (S3) server.
+.It Li X9SPW
+XFree86 3.3.3.1 PC98 SKB-PowerWindow (S3) server.
+.It Li X9TGU
+XFree86 3.3.3.1 PC98 Cyber9320 and TGUI9680 server.
+.It Li X9WEP
+XFree86 3.3.3.1 PC98 WAB-EP (cirrus) server.
+.It Li X9WS
+XFree86 3.3.3.1 PC98 WABS (cirrus) server.
+.It Li X9WSN
+XFree86 3.3.3.1 PC98 WSN-A2F (cirrus) server.
+.It Li XAGX
+XFree86 3.3.3.1 8 bit AGX server.
+.It Li XI128
+XFree86 3.3.3.1 #9 Imagine I128 server.
+.It Li XMa8
+XFree86 3.3.3.1 ATI Mach8 server.
+.It Li XMa32
+XFree86 3.3.3.1 ATI Mach32 server.
+.It Li XMa64
+XFree86 3.3.3.1 ATI Mach64 server.
+.It Li XMono
+XFree86 3.3.3.1 monochrome server.
+.It Li XP9K
+XFree86 3.3.3.1 P9000 server.
+.It Li XS3
+XFree86 3.3.3.1 S3 server.
+.It Li XS3V
+XFree86 3.3.3.1 S3 Virge server.
+.It Li XSVGA
+XFree86 3.3.3.1 SVGA server.
+.It Li XVG16
+XFree86 3.3.3.1 VGA16 server.
+.It Li XW32
+XFree86 3.3.3.1 ET4000/W32, /W32i and /W32p server.
+.It Li Xnest
+XFree86 3.3.3.1 nested X server.
+.It Li Xvfb
+XFree86 3.3.3.1 virtual frame-buffer X server.
+.It Li Xfnts
+XFree86 3.3.3.1 base font set.
+.It Li Xf100
+XFree86 3.3.3.1 100DPI font set.
+.It Li Xfcyr
+XFree86 3.3.3.1 Cyrillic font set.
+.It Li Xfscl
+XFree86 3.3.3.1 scalable font set.
+.It Li Xfnon
+XFree86 3.3.3.1 non-english font set.
+.It Li Xfsrv
+XFree86 3.3.3.1 font server.
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+\fBVariables:\fR None
+.It distSetDES
+Interactively select DES subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetXF86
+Interactively select XFree86 3.3.3.1 subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+\fBVariables:\fR None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest lynx package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to lynx.
+.El
+.It installCommit
+.Pp
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+\fBVariables:\fR None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+\fBVariables:\fR None
+.It installNovice
+Start a "novice" installation, the most user-friendly
+installation type available.
+.Pp
+\fBVariables:\fR None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+\fBVariables:\fR None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init.
+.Pp
+\fBVariables:\fR None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+\fBVariables:\fR None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+\fBVariables:\fR None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+\fBVariables:\fR None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+\fBVariables:\fR None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It file
+The fully pathname of the file to load.
+.El
+.It mediaSetCDROM
+Select a FreeBSD CDROM as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+\fBVariables:\fR None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+\fBVariables:\fR
+.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 FreeBSD
+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
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the FreeBSD distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+\fBVariables:\fR
+.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 FreeBSD distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+\fBVariables:\fR None
+.It register
+Bring up the FreeBSD registration form.
+.Pp
+\fBVariables:\fR None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+\fBVariables:\fR
+.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
+\fBVariables:\fR None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+\fBVariables:\fR None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+\fBVariables:\fR None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+\fBVariables:\fR
+.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
+.El
+.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/release/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted approximately 3 years past
+its expiration date and is greatly in need of death.
+.Sh AUTHOR
+Jordan K. Hubbard <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..8f171d1
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.h
@@ -0,0 +1,722 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: sysinstall.h,v 1.159 1999/03/19 10:54:38 jkh Exp $
+ *
+ * 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 _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 ***/
+
+/* device limits */
+#define DEV_NAME_MAX 64 /* 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_DISK "disk"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_DES "distDES"
+#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_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_GATED_PKG "gated_pkg"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INTERFACES "network_interfaces"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_MOUSED "moused_enable"
+#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_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#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_WARN "noWarn"
+#define VAR_NO_USR "noUsr"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PCNFSD_PKG "pcnfsd_pkg"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "routerflags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_XF86_CONFIG "_xf86config"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+
+/* 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 unsigned int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+ dialogMenuItem items[0]; /* Array of menu items */
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+ int dirty;
+} Variable;
+
+#define NO_ECHO_OBJ(type) ((type) | (DITEM_NO_ECHO << 16))
+#define TYPE_OF_OBJ(type) ((type) & 0xff)
+#define ATTR_OF_OBJ(type) ((type) >> 16)
+
+/* A screen layout structure */
+typedef struct _layout {
+ int y; /* x & Y co-ordinates */
+ int x;
+ int len; /* The size of the dialog on the screen */
+ int maxlen; /* How much the user can type in ... */
+ char *prompt; /* The string for the prompt */
+ char *help; /* The display for the help line */
+ void *var; /* The var to set when this changes */
+ int type; /* The type of the dialog to create */
+ void *obj; /* The obj pointer returned by libdialog */
+} Layout;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_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,
+} 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;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} PartType;
+
+/* The longest newfs command we'll hand to system() */
+#define NEWFS_CMD_MAX 256
+
+typedef struct _part_info {
+ Boolean newfs;
+ char mountpoint[FILENAME_MAX];
+ char newfs_cmd[NEWFS_CMD_MAX];
+} 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 */
+} 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 {
+ 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 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 DESDists; /* 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 */
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+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 MenuMedia; /* Media type menu */
+extern DMenu MenuMouse; /* Mouse type menu */
+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 MenuStartup; /* Startup services menu */
+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 MenuNetworking; /* Network configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuDESDistributions; /* DES 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 MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuDiskDevices; /* 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 */
+
+/* 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, ...);
+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 configRegister(dialogMenuItem *self);
+extern int configResolv(dialogMenuItem *self);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXEnvironment(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+
+/* crc.c */
+extern int crc(int, unsigned long *, unsigned long *);
+
+/* 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);
+
+/* disks.c */
+extern int diskPartitionEditor(dialogMenuItem *self);
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+extern void diskPartition(Device *dev);
+
+/* 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 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 distSetDES(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);
+
+/* 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 plist);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installNovice(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int installFixupBin(dialogMenuItem *self);
+extern int installFixupXFree(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);
+
+/* lndir.c */
+extern int lndir(char *from, char *to);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+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 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 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 Mount(char *, void *data);
+extern WINDOW *openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height);
+extern ComposeObj *initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max);
+extern int layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj,
+ int *n, int max, int *cbutton, int *cancel);
+
+extern WINDOW *savescr(void);
+extern void restorescr(WINDOW *w);
+extern char *sstrncpy(char *dst, const char *src, int size);
+
+/* mouse.c */
+extern int mousedTest(dialogMenuItem *self);
+extern int mousedDisable(dialogMenuItem *self);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...);
+extern void msgYap(char *fmt, ...);
+extern void msgWarn(char *fmt, ...);
+extern void msgDebug(char *fmt, ...);
+extern void msgError(char *fmt, ...);
+extern void msgFatal(char *fmt, ...);
+extern void msgConfirm(char *fmt, ...);
+extern void msgNotify(char *fmt, ...);
+extern void msgWeHaveOutput(char *fmt, ...);
+extern int msgYesNo(char *fmt, ...);
+extern char *msgGetInput(char *buf, char *fmt, ...);
+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_exists(char *name);
+
+/* register.c */
+extern int registerOpenDialog(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 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, ...);
+
+/* 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);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var, int dirty);
+extern void variable_set2(char *name, char *value, int dirty);
+extern char *variable_get(char *var);
+extern int variable_cmp(char *var, char *value);
+extern void variable_unset(char *var);
+extern char *variable_get_value(char *var, char *prompt, int dirty);
+extern int variable_check(char *data);
+extern int dump_variables(dialogMenuItem *self);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/system.c b/usr.sbin/sysinstall/system.c
new file mode 100644
index 0000000..f916555
--- /dev/null
+++ b/usr.sbin/sysinstall/system.c
@@ -0,0 +1,396 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: system.c,v 1.90 1999/02/14 21:26:29 jkh Exp $
+ *
+ * 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 <sys/reboot.h>
+#include <machine/console.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.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 void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("Are you sure you want to abort the installation?"))
+ systemShutdown(-1);
+ else
+ restorescr(save);
+}
+
+/* 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)
+{
+ int i;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd, type;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1)
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ 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.
+ */
+ type = 0; /* normal */
+ if (OnVTY) {
+ int fd2;
+
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ 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");
+ i = 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);
+ }
+ 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);
+ (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);
+#ifdef __alpha__
+ reboot(RB_HALT);
+#else
+ reboot(0);
+#endif
+ }
+ else
+ exit(status);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+
+ 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;
+ return status;
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+
+ 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);
+ }
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", 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, "/usr/src/release/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ snprintf(buf, FILENAME_MAX, "/usr/src/release/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[])
+{
+ extern void init_acs(void);
+
+ if (OnVTY) {
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ init_acs();
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ init_acs();
+ 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)
+{
+ if (OnVTY && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgYesNo("There seems to be an emergency holographic shell\n"
+ "already running von VTY 4.\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 ((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);
+ 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");
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/tape.c b/usr.sbin/sysinstall/tape.c
new file mode 100644
index 0000000..995a0fe
--- /dev/null
+++ b/usr.sbin/sysinstall/tape.c
@@ -0,0 +1,121 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: tape.c,v 1.19 1997/02/22 14:12:21 peter Exp $
+ *
+ * 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) {
+ msgDebug("Tape init routine called for %s (private dir is %s)\n", dev->name, 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.", 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);
+ return (FILE *)IO_ERROR;
+ }
+ }
+
+ 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)) {
+ msgNotify("Cleaning up results of tape extract in %s..",
+ (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..b30d52e
--- /dev/null
+++ b/usr.sbin/sysinstall/tcpip.c
@@ -0,0 +1,397 @@
+/*
+ * $Id: tcpip.c,v 1.74 1998/11/15 09:06:20 jkh Exp $
+ *
+ * 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>
+
+/* 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[IPADDR_FIELD_LEN];
+static int okbutton, cancelbutton;
+static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
+
+/* 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,
+ "Gateway:",
+ "IP address of host forwarding packets to non-local destinations",
+ gateway, STRINGOBJ, NULL },
+#define LAYOUT_NAMESERVER 3
+ { 5, 35, 18, IPADDR_FIELD_LEN - 1,
+ "Name server:", "IP address of your local DNS server",
+ nameserver, STRINGOBJ, NULL },
+#define LAYOUT_IPADDR 4
+ { 10, 10, 18, IPADDR_FIELD_LEN - 1,
+ "IP Address:",
+ "The IP 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:",
+ "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 },
+ { NULL },
+};
+
+#define _validByte(b) ((b) >= 0 && (b) <= 255)
+
+/* whine */
+static void
+feepout(char *msg)
+{
+ beep();
+ msgConfirm(msg);
+}
+
+/* Very basic IP address integrity check - could be drastically improved */
+static int
+verifyIP(char *ip)
+{
+ int a, b, c, d;
+
+ if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
+ _validByte(a) && _validByte(b) && _validByte(c) &&
+ _validByte(d) && (d != 255))
+ return 1;
+ else
+ return 0;
+}
+
+/* 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)
+{
+ if (!hostname[0])
+ feepout("Must specify a host name of some sort!");
+ else if (gateway[0] && strcmp(gateway, "NO") && !verifyIP(gateway))
+ feepout("Invalid gateway IP address specified");
+ else if (nameserver[0] && !verifyIP(nameserver))
+ feepout("Invalid name server IP address specified");
+ else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3'))
+ feepout("Invalid netmask value");
+ else if (ipaddr[0] && !verifyIP(ipaddr))
+ feepout("Invalid IP address");
+ else
+ return 1;
+ return 0;
+}
+
+/* This is it - how to get TCP setup values */
+int
+tcpOpenDialog(Device *devp)
+{
+ WINDOW *ds_win, *save = NULL;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE;
+ int max, ret = DITEM_SUCCESS;
+ char *tmp;
+ char title[80];
+
+ /* 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);
+ }
+ else { /* See if there are any defaults */
+ char *cp;
+
+ 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);
+ }
+ 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);
+ }
+ 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 */
+ tmp = variable_get(VAR_HOSTNAME);
+ if (tmp)
+ SAFE_STRCPY(hostname, tmp);
+ else
+ bzero(hostname, sizeof(hostname));
+ tmp = variable_get(VAR_DOMAINNAME);
+ if (tmp)
+ SAFE_STRCPY(domainname, tmp);
+ else
+ bzero(domainname, sizeof(domainname));
+ tmp = variable_get(VAR_GATEWAY);
+ if (tmp)
+ SAFE_STRCPY(gateway, tmp);
+ else
+ bzero(gateway, sizeof(gateway));
+ tmp = variable_get(VAR_NAMESERVER);
+ if (tmp)
+ SAFE_STRCPY(nameserver, tmp);
+ else
+ bzero(nameserver, sizeof(nameserver));
+
+ save = savescr();
+ /* 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();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(TCP_HELPFILE, " Network Configuration ",
+ 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)) {
+ /* 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] == '\0') {
+ strcpy(netmask, "255.255.255.0");
+ RefreshStringObj(layout[LAYOUT_NETMASK].obj);
+ }
+ if (!index(hostname, '.') && domainname[0]) {
+ strcat(hostname, ".");
+ strcat(hostname, domainname);
+ RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
+ }
+ else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
+ SAFE_STRCPY(domainname, tmp + 1);
+ RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
+ }
+ }
+
+ 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];
+ char *ifaces;
+
+ 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, 1);
+ if (nameserver[0])
+ variable_set2(VAR_NAMESERVER, nameserver, 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);
+
+ sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask);
+ sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
+ variable_set2(ifn, temp, 1);
+ ifaces = variable_get(VAR_INTERFACES);
+ if (!ifaces)
+ variable_set2(VAR_INTERFACES, ifaces = "lo0", 1);
+ /* Only add it if it's not there already */
+ if (!strstr(ifaces, devp->name)) {
+ sprintf(ifn, "%s %s", devp->name, ifaces);
+ variable_set2(VAR_INTERFACES, ifn, 1);
+ }
+ if (ipaddr[0])
+ variable_set2(VAR_IPADDR, ipaddr, 0);
+ 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(NULL, DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ rval = NULL;
+
+ if (!cnt) {
+ msgConfirm("No network devices available!");
+ return NULL;
+ }
+ else if (!RunningAsInit) {
+ 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;
+
+ tmp = tcpDeviceSelect();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
diff --git a/usr.sbin/sysinstall/termcap.c b/usr.sbin/sysinstall/termcap.c
new file mode 100644
index 0000000..1569479
--- /dev/null
+++ b/usr.sbin/sysinstall/termcap.c
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <machine/console.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 } };
+
+ 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("Your choice: (1-4) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 5) {
+ *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 (getenv("SYSINSTALL_DEBUG"))
+ 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));
+ }
+ }
+ 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;
+ }
+ }
+ }
+ 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/ufs.c b/usr.sbin/sysinstall/ufs.c
new file mode 100644
index 0000000..73c0667
--- /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.
+ *
+ * $Id: ufs.c,v 1.13 1998/09/14 19:14:11 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+/* No init or shutdown routines necessary - all done in mediaSetUFS() */
+
+FILE *
+mediaGetUFS(Device *dev, char *file, Boolean probe)
+{
+ return mediaGenericGet((char *)dev->private, file);
+}
diff --git a/usr.sbin/sysinstall/user.c b/usr.sbin/sysinstall/user.c
new file mode 100644
index 0000000..680a543
--- /dev/null
+++ b/usr.sbin/sysinstall/user.c
@@ -0,0 +1,727 @@
+/*
+ * $Id: user.c,v 1.13 1997/02/22 14:12:36 peter Exp $
+ *
+ * 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 },
+ { NULL },
+};
+
+/* 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 },
+ { NULL },
+};
+
+/* 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(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(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 (strlen(shell) > 0) {
+ 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;
+
+ 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));
+
+ 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..ab39a47
--- /dev/null
+++ b/usr.sbin/sysinstall/variable.c
@@ -0,0 +1,224 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: variable.c,v 1.24 1998/07/18 09:42:02 jkh Exp $
+ *
+ * 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"
+
+/* 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);
+ 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);
+ 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!");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2()\n");
+ 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, prompt)) != NULL)
+ variable_set2(var, cp, dirty);
+ else
+ cp = NULL;
+ return cp;
+}
+
+/* Check if value passed in data (in the form "variable=value") is equal to value of
+ variable stored in env */
+int
+variable_check(char *data)
+{
+ char *cp, *cp2, *cp3, tmp[256];
+
+ if (!data)
+ return FALSE;
+ 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 = getenv(tmp);
+ if (cp2) {
+ if (!*cp)
+ return TRUE;
+ else
+ return !strcmp(cp, cp2);
+ }
+ else
+ return FALSE;
+ }
+ else
+ return getenv(tmp) ? TRUE : FALSE;
+}
+
+int
+dump_variables(dialogMenuItem *unused)
+{
+ FILE *fp;
+ Variable *vp;
+
+ if (isDebug())
+ msgDebug("Writing sysinstall variables to file..");
+
+ 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;
+}
diff --git a/usr.sbin/sysinstall/wizard.c b/usr.sbin/sysinstall/wizard.c
new file mode 100644
index 0000000..0009666
--- /dev/null
+++ b/usr.sbin/sysinstall/wizard.c
@@ -0,0 +1,206 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> 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: wizard.c,v 1.12 1998/09/08 10:46:40 jkh Exp $
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+u_char mbr[] = {
+ 250,51,192,142,208,188,0,124,139,244,80,7,80,31,251,252,191,0,6,185,0,1,
+ 242,165,234,29,6,0,0,190,190,7,179,4,128,60,128,116,14,128,60,0,117,28,
+ 131,198,16,254,203,117,239,205,24,139,20,139,76,2,139,238,131,198,16,254,
+ 203,116,26,128,60,0,116,244,190,139,6,172,60,0,116,11,86,187,7,0,180,14,
+ 205,16,94,235,240,235,254,191,5,0,187,0,124,184,1,2,87,205,19,95,115,12,
+ 51,192,205,19,79,117,237,190,163,6,235,211,190,194,6,191,254,125,129,61,
+ 85,170,117,199,139,245,234,0,124,0,0,73,110,118,97,108,105,100,32,112,97,
+ 114,116,105,116,105,111,110,32,116,97,98,108,101,0,69,114,114,111,114,32,
+ 108,111,97,100,105,110,103,32,111,112,101,114,97,116,105,110,103,32,115,
+ 121,115,116,101,109,0,77,105,115,115,105,110,103,32,111,112,101,114,97,
+ 116,105,110,103,32,115,121,115,116,101,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,
+ 1,1,0,4,15,63,60,63,0,0,0,241,239,0,0,0,0,1,61,5,15,63,243,48,240,0,0,144,
+ 208,2,0,0,0,1,244,165,15,63,170,192,192,3,0,144,208,2,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,85,170
+};
+
+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/r");
+ 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;
+
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf(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;
+ }
+ 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;
+ }
+ 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));
+ Free_Disk(d);
+ d = Open_Disk(d->name);
+ 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");
+ printf("create offset size enum subtype flags\n");
+ 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("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+}
diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile
new file mode 100644
index 0000000..8bac8b3
--- /dev/null
+++ b/usr.sbin/syslogd/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= syslogd
+SRCS= syslogd.c ttymsg.c
+.PATH: ${.CURDIR}/../../usr.bin/wall
+MAN5= syslog.conf.5
+MAN8= syslogd.8
+COPTS+= -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..a2ddf5f
--- /dev/null
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -0,0 +1,361 @@
+.\" 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
+.\" $Id: syslog.conf.5,v 1.11 1998/07/22 06:15:16 phk Exp $
+.\"
+.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
+specifications,
+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
+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
+.Em syslog.conf
+might be incompatible with other Unices or Unix-like systems.
+This functionality was added for the ease of configuration
+(e.g. it is possible to cut-and-paste into
+.Em syslog.conf
+),
+and to avoid possible mistakes. This change however preserves
+backwards compatibility with the old style of the
+.Em syslog.conf
+(i.e. tab characters only).
+.Pp
+The
+.Em Selectors
+function
+are encoded as a
+.Em facility ,
+a period
+.Pq Dq \&. ,
+an optional set of comparison flags
+.Pq Bq <=> ,
+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, cron, daemon, ftp, kern, lpr, mail,
+mark, news, ntp, syslog, user, uucp and local0 through local7.
+These keywords (with the exception of mark) correspond to the
+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 set of comparison flags are
+.Dq =>
+(or, if you prefer,
+.Do >=
+.Dc ),
+which means that messages from the specified
+.Em facility
+list of a priority
+level equal or greater than
+.Em level
+will be logged.
+.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 the
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr syslog
+library routine.
+.Pp
+Each block of lines is separated from the previous block by a tag. The tag
+is a line beginning with
+.Em #!prog
+or
+.Em !prog
+(the former is for compatibility with the previous syslogd, if one is sharing
+syslog.conf files, for example)
+and each block will be associated with calls to syslog from that specific
+program. A tag for ``foo'' will also match any message logged by the kernel
+with the prefix ``foo: ''.
+.Pp
+See
+.Xr syslog 3
+for a 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.
+.It
+A hostname (preceded by an at
+.Pq Dq @
+sign).
+Selected messages are forwarded to the
+.Xr syslogd
+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 a
+.Pa /bin/sh
+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 ,
+.Nm
+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
+.Pp
+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 kernel firewall reports to a separate file
+!ipfw
+*.* /var/log/ipfw
+.Ed
+.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 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..bd39df3
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.8
@@ -0,0 +1,243 @@
+.\" 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
+.\" $Id: syslogd.8,v 1.15 1998/06/25 19:39:18 guido Exp $
+.\"
+.Dd October 12, 1995
+.Dt SYSLOGD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl dsuv
+.Op Fl a Ar allowed_peer
+.Op Fl f Ar config_file
+.Op Fl m Ar mark_interval
+.Op Fl p Ar log_socket
+.Op Fl l Ar path
+.Sh DESCRIPTION
+The
+.Nm
+daemon reads and logs messages to the system console, log files, other
+machines and/or users as specified by its configuration file.
+The options are as follows:
+.Bl -tag -width indent
+.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 Ar ipaddr/masklen Ns Op Ar :service
+Accept datagrams from
+.Ar ipaddr
+(in the usual dotted quad notation) with
+.Ar masklen
+bits being taken into account when doing the address comparision. If
+specified,
+.Ar service
+is the name or number of an UDP service (see
+.Xr services 5 ) Ns
+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 .
+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.
+.It Ar domainname Ns Op Ar :service
+Accept datagrams where the reverse address lookup yields
+.Ar domainname
+for the sender address. The meaning of
+.Ar service
+is as explained above.
+.It Ar *domainname Ns Op Ar :service
+Same as before, except that any source host whose name
+.Em ends
+in
+.Ar domainname
+will get permission.
+.El
+.It Fl d
+Put
+.Nm
+into debugging mode. This is probably only of use to developers working on
+.Nm Ns .
+.It Fl f
+Specify the pathname of an alternate configuration file;
+the default is
+.Pa /etc/syslog.conf .
+.It Fl m
+Select the number of minutes between
+.Dq mark
+messages; the default is 20 minutes.
+.It Fl p
+Specify the pathname of an alternate log socket to be used instead;
+the default is
+.Pa /var/run/log .
+.It Fl l
+Specify a location where
+.Nm syslogd
+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 /dev/log
+of various chroot filespaces.
+.It Fl s
+Operate in secure mode. Do not log messages from remote machines.
+The messages will be received and counted and a log entry produced every time
+the count exceeds a power of two.
+.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
+daemon 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
+daemon reads messages from the
+.Tn UNIX
+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
+daemon creates the file
+.Pa /var/run/syslog.pid ,
+and stores its process
+id there.
+This can be used to kill or reconfigure
+.Nm Ns .
+.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
+.Aq Pa sys/syslog.h .
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+configuration file
+.It Pa /var/run/syslog.pid
+process id of current
+.Nm
+.It Pa /var/run/log
+name of the
+.Tn UNIX
+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
+command 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 comparision. 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 filesystem. 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..523de1a
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.c
@@ -0,0 +1,1903 @@
+/*
+ * 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
+static const char rcsid[] =
+ "$Id: syslogd.c,v 1.45 1998/12/29 20:36:22 cwt Exp $";
+#endif /* not lint */
+
+/*
+ * 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 maximimum 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 /* timed out 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 <paths.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 <regex.h>
+#include <setjmp.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"
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+const char *ConfFile = _PATH_LOGCONF;
+const char *PidFile = _PATH_LOGPID;
+const char ctty[] = _PATH_CONSOLE;
+
+#define dprintf if (Debug) printf
+
+#define MAXUNAMES 20 /* maximum number of user names */
+
+#define MAXFUNIX 20
+
+int nfunix = 1;
+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.
+ */
+
+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 */
+ 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+1];
+ struct sockaddr_in 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+1]; /* 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 */
+ int f_repeatcount; /* number of "repeated" msgs */
+};
+
+/*
+ * 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 in_addr addr;
+ struct in_addr 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 */
+
+char *TypeNames[8] = {
+ "UNUSED", "FILE", "TTY", "CONSOLE",
+ "FORW", "USERS", "WALL", "PIPE"
+};
+
+struct filed *Files;
+struct filed consfile;
+
+int Debug; /* debug flag */
+char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
+char *LocalDomain; /* our local domain name */
+int finet; /* Internet datagram socket */
+int LogPort; /* port number for INET connections */
+int Initialized = 0; /* set when we have initialized ourselves */
+int MarkInterval = 20 * 60; /* interval between marks in seconds */
+int MarkSeq = 0; /* mark sequence number */
+int SecureMode = 0; /* when true, receive only unix domain socks */
+u_int Vogons = 0; /* packets arriving in SecureMode */
+
+char bootfile[MAXLINE+1]; /* booted kernel file */
+
+struct allowedpeer *AllowedPeers;
+int NumAllowed = 0; /* # of AllowedPeer entries */
+
+int UniquePriority = 0; /* Only log specified priority? */
+int LogFacPri = 0; /* Put facility and priority in log message: */
+ /* 0=no, 1=numeric, 2=names */
+
+int allowaddr __P((char *));
+void cfline __P((char *, struct filed *, char *));
+char *cvthname __P((struct sockaddr_in *));
+void deadq_enter __P((pid_t));
+int decode __P((const char *, CODE *));
+void die __P((int));
+void domark __P((int));
+void fprintlog __P((struct filed *, int, char *));
+void init __P((int));
+void logerror __P((const char *));
+void logmsg __P((int, char *, char *, int));
+void printline __P((char *, char *));
+void printsys __P((char *));
+int p_open __P((char *, pid_t *));
+void reapchild __P((int));
+char *ttymsg __P((struct iovec *, int, char *, int));
+static void usage __P((void));
+int validate __P((struct sockaddr_in *, const char *));
+void wallmsg __P((struct filed *, struct iovec *));
+int waitdaemon __P((int, int, int));
+void timedout __P((int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, i, l, fklog, len;
+ struct sockaddr_un sunx, fromunix;
+ struct sockaddr_in sin, frominet;
+ FILE *fp;
+ char *p, *hname, line[MAXLINE + 1];
+ struct timeval tv, *tvp;
+ pid_t ppid = 1;
+
+ while ((ch = getopt(argc, argv, "a:dl:f:m:p:suv")) != -1)
+ switch(ch) {
+ case 'd': /* debug */
+ Debug++;
+ break;
+ case 'a': /* allow specific network addresses only */
+ if (allowaddr(optarg) == -1)
+ usage();
+ break;
+ case 'f': /* configuration file */
+ ConfFile = optarg;
+ break;
+ case 'm': /* mark interval */
+ MarkInterval = atoi(optarg) * 60;
+ break;
+ case 'p': /* path */
+ funixn[0] = optarg;
+ break;
+ case 's': /* no network mode */
+ SecureMode++;
+ break;
+ case 'l':
+ if (nfunix < MAXFUNIX)
+ funixn[nfunix++] = optarg;
+ else
+ fprintf(stderr,
+ "syslogd: out of descriptors, ignoring %s\n",
+ optarg);
+ break;
+ case 'u': /* only log specified priority */
+ UniquePriority++;
+ break;
+ case 'v': /* log facility and priority */
+ LogFacPri++;
+ break;
+ case '?':
+ 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)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1);
+ (void)gethostname(LocalHostName, sizeof(LocalHostName));
+ if ((p = strchr(LocalHostName, '.')) != NULL) {
+ *p++ = '\0';
+ LocalDomain = p;
+ } else
+ LocalDomain = "";
+ (void)strcpy(bootfile, getbootfile());
+ (void)signal(SIGTERM, die);
+ (void)signal(SIGINT, Debug ? die : SIG_IGN);
+ (void)signal(SIGQUIT, Debug ? die : SIG_IGN);
+ (void)signal(SIGCHLD, reapchild);
+ (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++) {
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ (void)strncpy(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);
+ }
+ }
+ finet = socket(AF_INET, SOCK_DGRAM, 0);
+ if (finet >= 0) {
+ struct servent *sp;
+
+ sp = getservbyname("syslog", "udp");
+ if (sp == NULL) {
+ errno = 0;
+ logerror("syslog/udp: unknown service");
+ die(0);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = LogPort = sp->s_port;
+
+ if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ logerror("bind");
+ if (!Debug)
+ die(0);
+ }
+ }
+
+ if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) < 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);
+ (void)signal(SIGHUP, init);
+
+ tvp = &tv;
+ tv.tv_sec = tv.tv_usec = 0;
+
+ for (;;) {
+ fd_set readfds;
+ int nfds = 0;
+
+ FD_ZERO(&readfds);
+ if (fklog != -1) {
+ FD_SET(fklog, &readfds);
+ if (fklog > nfds)
+ nfds = fklog;
+ }
+ if (finet != -1) {
+ FD_SET(finet, &readfds);
+ if (finet > nfds)
+ nfds = finet;
+ }
+ for (i = 0; i < nfunix; i++) {
+ if (funix[i] != -1) {
+ FD_SET(funix[i], &readfds);
+ if (funix[i] > nfds)
+ nfds = funix[i];
+ }
+ }
+
+ /*dprintf("readfds = %#x\n", readfds);*/
+ nfds = select(nfds+1, &readfds, (fd_set *)NULL,
+ (fd_set *)NULL, tvp);
+ if (nfds == 0) {
+ if (tvp) {
+ tvp = NULL;
+ if (ppid != 1)
+ kill(ppid, SIGALRM);
+ }
+ continue;
+ }
+ if (nfds < 0) {
+ if (errno != EINTR)
+ logerror("select");
+ continue;
+ }
+ /*dprintf("got a message (%d, %#x)\n", nfds, readfds);*/
+ if (fklog != -1 && FD_ISSET(fklog, &readfds)) {
+ i = read(fklog, line, MAXLINE - 1);
+ if (i > 0) {
+ line[i] = '\0';
+ printsys(line);
+ } else if (i < 0 && errno != EINTR) {
+ logerror("klog");
+ fklog = -1;
+ }
+ }
+ if (finet != -1 && FD_ISSET(finet, &readfds)) {
+ len = sizeof(frominet);
+ l = recvfrom(finet, line, MAXLINE, 0,
+ (struct sockaddr *)&frominet, &len);
+ if (SecureMode) {
+ Vogons++;
+ if (!(Vogons & (Vogons - 1))) {
+ (void)snprintf(line, sizeof line,
+"syslogd: discarded %d unwanted packets in secure mode, last from %s", Vogons,
+ inet_ntoa(frominet.sin_addr));
+ logmsg(LOG_SYSLOG|LOG_AUTH, line,
+ LocalHostName, ADDDATE);
+ }
+ } else if (l > 0) {
+ line[l] = '\0';
+ hname = cvthname(&frominet);
+ if (validate(&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], &readfds)) {
+ 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");
+ }
+ }
+ }
+}
+
+static void
+usage()
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: syslogd [-dsuv] [-a allowed_peer] [-f config_file]",
+ " [-m mark_interval] [-p log_socket]",
+ " [-l log_socket]");
+ exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+void
+printline(hname, msg)
+ char *hname;
+ char *msg;
+{
+ int c, pri;
+ char *p, *q, line[MAXLINE + 1];
+
+ /* test for special codes */
+ pri = DEFUPRI;
+ p = msg;
+ if (*p == '<') {
+ pri = 0;
+ while (isdigit(*++p))
+ pri = 10 * pri + (*p - '0');
+ if (*p == '>')
+ ++p;
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFUPRI;
+
+ /* don't allow users to log kernel messages */
+ if (LOG_FAC(pri) == LOG_KERN)
+ pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+ q = line;
+
+ while ((c = *p++ & 0177) != '\0' &&
+ q < &line[sizeof(line) - 1])
+ if (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);
+}
+
+/*
+ * Take a raw input line from /dev/klog, split and format similar to syslog().
+ */
+void
+printsys(msg)
+ char *msg;
+{
+ int pri, flags;
+ char *p, *q;
+
+ for (p = msg; *p != '\0'; ) {
+ flags = ISKERNEL | SYNC_FILE | ADDDATE; /* fsync after write */
+ pri = DEFSPRI;
+ if (*p == '<') {
+ pri = 0;
+ while (isdigit(*++p))
+ pri = 10 * pri + (*p - '0');
+ if (*p == '>')
+ ++p;
+ } else {
+ /* kernel printf's come out on console */
+ flags |= IGN_CONS;
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFSPRI;
+ for (q = p; *q != '\0' && *q != '\n'; q++);
+ if (*q != '\0')
+ *q++ = '\0';
+ logmsg(pri, p, LocalHostName, flags);
+ p = q;
+ }
+}
+
+time_t now;
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+void
+logmsg(pri, msg, from, flags)
+ int pri;
+ char *msg, *from;
+ int flags;
+{
+ struct filed *f;
+ int i, fac, msglen, omask, prilev;
+ 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(!isalnum(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", bootfile, 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) {
+ 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 program name */
+ if(f->f_program)
+ if(strcmp(prog, f->f_program) != 0)
+ continue;
+
+ 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 ((flags & MARK) == 0 && msglen == f->f_prevlen &&
+ !strcmp(msg, f->f_prevline) &&
+ !strcmp(from, f->f_prevhost)) {
+ (void)strncpy(f->f_lasttime, timestamp, 15);
+ 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)strncpy(f->f_lasttime, timestamp, 15);
+ (void)strncpy(f->f_prevhost, from,
+ sizeof(f->f_prevhost)-1);
+ f->f_prevhost[sizeof(f->f_prevhost)-1] = '\0';
+ if (msglen < MAXSVLINE) {
+ f->f_prevlen = msglen;
+ (void)strcpy(f->f_prevline, msg);
+ fprintlog(f, flags, (char *)NULL);
+ } else {
+ f->f_prevline[0] = 0;
+ f->f_prevlen = 0;
+ fprintlog(f, flags, msg);
+ }
+ }
+ }
+ (void)sigsetmask(omask);
+}
+
+void
+fprintlog(f, flags, msg)
+ struct filed *f;
+ int flags;
+ char *msg;
+{
+ struct iovec iov[7];
+ struct iovec *v;
+ int l;
+ char line[MAXLINE + 1], repbuf[80], greetings[200];
+ 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));
+ v++;
+ v->iov_base = "";
+ v->iov_len = 0;
+ v++;
+ } else {
+ v->iov_base = f->f_lasttime;
+ v->iov_len = 15;
+ v++;
+ v->iov_base = " ";
+ 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);
+ char *f_s = 0;
+ char f_n[5]; /* Hollow laugh */
+ char *p_s = 0;
+ char p_n[5]; /* Hollow laugh */
+
+ if (LogFacPri > 1) {
+ CODE *c;
+
+ for (c = facilitynames; c; c++) {
+ if (c->c_val == fac) {
+ f_s = c->c_name;
+ break;
+ }
+ }
+ for (c = prioritynames; c; 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="";
+ v->iov_len = 0;
+ }
+ v++;
+
+ v->iov_base = f->f_prevhost;
+ v->iov_len = strlen(v->iov_base);
+ v++;
+ v->iov_base = " ";
+ v->iov_len = 1;
+ v++;
+
+ if (msg) {
+ v->iov_base = msg;
+ v->iov_len = strlen(msg);
+ } else if (f->f_prevcount > 1) {
+ v->iov_base = repbuf;
+ v->iov_len = sprintf(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 (strcmp(f->f_prevhost, LocalHostName))
+ l = snprintf(line, sizeof line - 1,
+ "<%d>%.15s Forwarded from %s: %s",
+ f->f_prevpri, iov[0].iov_base, f->f_prevhost,
+ iov[5].iov_base);
+ else
+ l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
+ f->f_prevpri, iov[0].iov_base, iov[5].iov_base);
+ if (l > MAXLINE)
+ l = MAXLINE;
+ if ((finet >= 0) &&
+ (sendto(finet, line, l, 0,
+ (struct sockaddr *)&f->f_un.f_forw.f_addr,
+ sizeof(f->f_un.f_forw.f_addr)) != l)) {
+ int e = errno;
+ (void)close(f->f_file);
+ f->f_type = F_UNUSED;
+ errno = e;
+ logerror("sendto");
+ }
+ break;
+
+ case F_FILE:
+ dprintf(" %s\n", f->f_un.f_fname);
+ v->iov_base = "\n";
+ 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)
+ (void)fsync(f->f_file);
+ break;
+
+ case F_PIPE:
+ dprintf(" %s\n", f->f_un.f_pipe.f_pname);
+ v->iov_base = "\n";
+ 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_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 = "\r\n";
+ 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 = "\r\n";
+ v->iov_len = 2;
+ wallmsg(f, iov);
+ break;
+ }
+ f->f_prevcount = 0;
+}
+
+/*
+ * WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ */
+void
+wallmsg(f, iov)
+ struct filed *f;
+ struct iovec *iov;
+{
+ static int reenter; /* avoid calling ourselves */
+ FILE *uf;
+ struct utmp ut;
+ int i;
+ 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;
+ strncpy(line, ut.ut_line, sizeof(ut.ut_line));
+ line[sizeof(ut.ut_line)] = '\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;
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+ int status, code;
+ pid_t pid;
+ struct filed *f;
+ char buf[256];
+ const char *reason;
+ dq_t q;
+
+ 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. */
+ for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
+ if (q->dq_pid == pid) {
+ TAILQ_REMOVE(&deadq_head, q, dq_entries);
+ free(q);
+ 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);
+
+ errno = 0; /* Keep strerror() stuff out of logerror messages. */
+ f->f_un.f_pipe.f_pid = 0;
+ if (WIFSIGNALED(status)) {
+ reason = "due to signal";
+ code = WTERMSIG(status);
+ } else {
+ reason = "with status";
+ code = WEXITSTATUS(status);
+ if (code == 0)
+ goto oncemore; /* Exited OK. */
+ }
+ (void)snprintf(buf, sizeof buf,
+ "Logging subprocess %d (%s) exited %s %d.",
+ pid, f->f_un.f_pipe.f_pname,
+ reason, code);
+ logerror(buf);
+ break;
+ }
+ oncemore:
+ }
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+char *
+cvthname(f)
+ struct sockaddr_in *f;
+{
+ struct hostent *hp;
+ sigset_t omask, nmask;
+ char *p;
+
+ dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
+
+ if (f->sin_family != AF_INET) {
+ dprintf("Malformed from address\n");
+ return ("???");
+ }
+ sigemptyset(&nmask);
+ sigaddset(&nmask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &nmask, &omask);
+ hp = gethostbyaddr((char *)&f->sin_addr,
+ sizeof(struct in_addr), f->sin_family);
+ sigprocmask(SIG_SETMASK, &omask, NULL);
+ if (hp == 0) {
+ dprintf("Host name for your address (%s) unknown\n",
+ inet_ntoa(f->sin_addr));
+ return (inet_ntoa(f->sin_addr));
+ }
+ if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
+ *p = '\0';
+ return (hp->h_name);
+}
+
+void
+domark(signo)
+ int signo;
+{
+ struct filed *f;
+ dq_t q;
+
+ 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 = TAILQ_NEXT(q, dq_entries))
+ switch (q->dq_timeout) {
+ case 0:
+ /* Already signalled once, try harder now. */
+ kill(q->dq_pid, SIGKILL);
+ 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.
+ */
+ kill(q->dq_pid, SIGTERM);
+ /* FALLTROUGH */
+
+ default:
+ q->dq_timeout--;
+ }
+
+ (void)alarm(TIMERINTVL);
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+void
+logerror(type)
+ const char *type;
+{
+ char buf[512];
+
+ 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);
+}
+
+void
+die(signo)
+ 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)
+ (void)close(f->f_file);
+ }
+ Initialized = was_initialized;
+ if (signo) {
+ dprintf("syslogd: exiting on signal %d\n", signo);
+ (void)sprintf(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
+ */
+void
+init(signo)
+ int signo;
+{
+ int i;
+ FILE *cf;
+ struct filed *f, *next, **nextp;
+ char *p;
+ char cline[LINE_MAX];
+ char prog[NAME_MAX+1];
+
+ dprintf("init\n");
+
+ /*
+ * 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:
+ (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_pid = 0;
+ break;
+ }
+ next = f->f_next;
+ if(f->f_program) free(f->f_program);
+ 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));
+ cfline("*.ERR\t/dev/console", *nextp, "*");
+ (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+ cfline("*.PANIC\t*", (*nextp)->f_next, "*");
+ Initialized = 1;
+ return;
+ }
+
+ /*
+ * Foreach line in the conf table, open that file.
+ */
+ f = NULL;
+ strcpy(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!='!')
+ continue;
+ }
+ if(*p=='!') {
+ p++;
+ while(isspace(*p)) p++;
+ if((!*p) || (*p == '*')) {
+ strcpy(prog, "*");
+ continue;
+ }
+ for(i = 0; i < NAME_MAX; i++) {
+ if(!isalnum(p[i]))
+ break;
+ prog[i] = p[i];
+ }
+ prog[i] = 0;
+ continue;
+ }
+ for (p = strchr(cline, '\0'); isspace(*--p);)
+ continue;
+ *++p = '\0';
+ f = (struct filed *)calloc(1, sizeof(*f));
+ *nextp = f;
+ nextp = &f->f_next;
+ cfline(cline, f, prog);
+ }
+
+ /* 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");
+}
+
+/*
+ * Crack a configuration file line
+ */
+void
+cfline(line, f, prog)
+ char *line;
+ struct filed *f;
+ char *prog;
+{
+ struct hostent *hp;
+ int i, pri;
+ char *bp, *p, *q;
+ char buf[MAXLINE], ebuf[100];
+
+ dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
+
+ 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 program name if any */
+ if(prog && *prog=='*') prog = NULL;
+ if(prog) {
+ f->f_program = calloc(1, strlen(prog)+1);
+ if(f->f_program) {
+ strcpy(f->f_program, prog);
+ }
+ }
+
+ /* scan through the list of selectors */
+ for (p = line; *p && *p != '\t' && *p != ' ';) {
+ int pri_done;
+ int pri_cmp;
+
+ /* 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;
+ 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;
+ }
+ }
+ if (!pri_cmp)
+ pri_cmp = (UniquePriority)
+ ? (PRI_EQ)
+ : (PRI_EQ | PRI_GT)
+ ;
+
+ /* 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;
+ else {
+ pri = decode(buf, prioritynames);
+ if (pri < 0) {
+ (void)snprintf(ebuf, sizeof ebuf,
+ "unknown priority name \"%s\"", buf);
+ logerror(ebuf);
+ return;
+ }
+ }
+
+ /* 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++;
+
+ switch (*p)
+ {
+ case '@':
+ (void)strncpy(f->f_un.f_forw.f_hname, ++p,
+ sizeof(f->f_un.f_forw.f_hname)-1);
+ f->f_un.f_forw.f_hname[sizeof(f->f_un.f_forw.f_hname)-1] = '\0';
+ hp = gethostbyname(f->f_un.f_forw.f_hname);
+ if (hp == NULL) {
+ extern int h_errno;
+
+ logerror(hstrerror(h_errno));
+ break;
+ }
+ memset(&f->f_un.f_forw.f_addr, 0,
+ sizeof(f->f_un.f_forw.f_addr));
+ f->f_un.f_forw.f_addr.sin_family = AF_INET;
+ f->f_un.f_forw.f_addr.sin_port = LogPort;
+ memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
+ 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 (isatty(f->f_file)) {
+ if (strcmp(p, ctty) == 0)
+ f->f_type = F_CONSOLE;
+ else
+ f->f_type = F_TTY;
+ (void)strcpy(f->f_un.f_fname, p + sizeof _PATH_DEV - 1);
+ } else {
+ (void)strcpy(f->f_un.f_fname, p);
+ f->f_type = F_FILE;
+ }
+ break;
+
+ case '|':
+ f->f_un.f_pipe.f_pid = 0;
+ (void)strcpy(f->f_un.f_pipe.f_pname, p + 1);
+ 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
+ */
+int
+decode(name, codetab)
+ 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);
+}
+
+/*
+ * 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.
+ */
+int
+waitdaemon(nochdir, noclose, maxwait)
+ int nochdir, noclose, 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.
+ */
+void
+timedout(sig)
+ 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.
+ */
+int
+allowaddr(s)
+ char *s;
+{
+ char *cp1, *cp2;
+ struct allowedpeer ap;
+ struct servent *se;
+ regex_t re;
+ int i;
+
+ if ((cp1 = strrchr(s, ':'))) {
+ /* service/port provided */
+ *cp1++ = '\0';
+ if (strlen(cp1) == 1 && *cp1 == '*')
+ /* any port allowed */
+ ap.port = htons(0);
+ else if ((se = getservbyname(cp1, "udp")))
+ ap.port = se->s_port;
+ else {
+ ap.port = htons((int)strtol(cp1, &cp2, 0));
+ if (*cp2 != '\0')
+ return -1; /* port not numeric */
+ }
+ } else {
+ if ((se = getservbyname("syslog", "udp")))
+ ap.port = se->s_port;
+ else
+ /* sanity, should not happen */
+ ap.port = htons(514);
+ }
+
+ /* the regexp's are ugly, but the cleanest way */
+
+ if (regcomp(&re, "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+(/[0-9]+)?$",
+ REG_EXTENDED))
+ /* if RE compilation fails, that's an internal error */
+ abort();
+ if (regexec(&re, s, 0, 0, 0) == 0) {
+ /* arg `s' is numeric */
+ ap.isnumeric = 1;
+ if ((cp1 = strchr(s, '/')) != NULL) {
+ *cp1++ = '\0';
+ i = atoi(cp1);
+ if (i < 0 || i > 32)
+ return -1;
+ /* convert masklen to netmask */
+ ap.a_mask.s_addr = htonl(~((1 << (32 - i)) - 1));
+ }
+ if (ascii2addr(AF_INET, s, &ap.a_addr) == -1)
+ return -1;
+ if (cp1 == NULL) {
+ /* use default netmask */
+ if (IN_CLASSA(ntohl(ap.a_addr.s_addr)))
+ ap.a_mask.s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(ap.a_addr.s_addr)))
+ ap.a_mask.s_addr = htonl(IN_CLASSB_NET);
+ else
+ ap.a_mask.s_addr = htonl(IN_CLASSC_NET);
+ }
+ } else {
+ /* arg `s' is domain name */
+ ap.isnumeric = 0;
+ ap.a_name = s;
+ }
+ regfree(&re);
+
+ if (Debug) {
+ printf("allowaddr: rule %d: ", NumAllowed);
+ if (ap.isnumeric) {
+ printf("numeric, ");
+ printf("addr = %s, ",
+ addr2ascii(AF_INET, &ap.a_addr, sizeof(struct in_addr), 0));
+ printf("mask = %s; ",
+ addr2ascii(AF_INET, &ap.a_mask, sizeof(struct in_addr), 0));
+ } else
+ printf("domainname = %s; ", ap.a_name);
+ printf("port = %d\n", ntohs(ap.port));
+ }
+
+ if ((AllowedPeers = realloc(AllowedPeers,
+ ++NumAllowed * sizeof(struct allowedpeer)))
+ == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(EX_OSERR);
+ }
+ memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
+ return 0;
+}
+
+/*
+ * Validate that the remote peer has permission to log to us.
+ */
+int
+validate(sin, hname)
+ struct sockaddr_in *sin;
+ const char *hname;
+{
+ int i;
+ size_t l1, l2;
+ char *cp, name[MAXHOSTNAMELEN];
+ struct allowedpeer *ap;
+
+ if (NumAllowed == 0)
+ /* traditional behaviour, allow everything */
+ return 1;
+
+ strncpy(name, hname, sizeof name);
+ if (strchr(name, '.') == NULL) {
+ strncat(name, ".", sizeof name - strlen(name) - 1);
+ strncat(name, LocalDomain, sizeof name - strlen(name) - 1);
+ }
+ dprintf("validate: dgram from IP %s, port %d, name %s;\n",
+ addr2ascii(AF_INET, &sin->sin_addr, sizeof(struct in_addr), 0),
+ ntohs(sin->sin_port), name);
+
+ /* now, walk down the list */
+ for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
+ if (ntohs(ap->port) != 0 && ap->port != sin->sin_port) {
+ dprintf("rejected in rule %d due to port mismatch.\n", i);
+ continue;
+ }
+
+ if (ap->isnumeric) {
+ if ((sin->sin_addr.s_addr & ap->a_mask.s_addr)
+ != ap->a_addr.s_addr) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ 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 *.
+ */
+int
+p_open(prog, pid)
+ char *prog;
+ pid_t *pid;
+{
+ int pfd[2], nulldesc, i;
+ 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] = "sh";
+ argv[1] = "-c";
+ argv[2] = prog;
+ argv[3] = NULL;
+
+ 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);
+ }
+ return pfd[1];
+}
+
+void
+deadq_enter(pid)
+ pid_t pid;
+{
+ dq_t p;
+
+ p = malloc(sizeof(struct deadq_entry));
+ if (p == 0) {
+ errno = 0;
+ logerror("panic: out of virtual memory!");
+ exit(1);
+ }
+
+ p->dq_pid = pid;
+ p->dq_timeout = DQ_TIMO_INIT;
+ TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
+}
diff --git a/usr.sbin/tcpdchk/Makefile b/usr.sbin/tcpdchk/Makefile
new file mode 100644
index 0000000..ba9be97
--- /dev/null
+++ b/usr.sbin/tcpdchk/Makefile
@@ -0,0 +1,20 @@
+#
+# $Id$
+#
+
+MAINTAINER=markm@FreeBSD.org
+
+PROG= tcpdchk
+MAN8= tcpdchk.8
+SRCS= tcpdchk.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS= -DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10 \
+ -DHOSTS_DENY=\"/etc/hosts.deny\" -DHOSTS_ALLOW=\"/etc/hosts.allow\"
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdmatch/Makefile b/usr.sbin/tcpdmatch/Makefile
new file mode 100644
index 0000000..3bf9eb4
--- /dev/null
+++ b/usr.sbin/tcpdmatch/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id$
+#
+
+MAINTAINER=markm@FreeBSD.org
+
+PROG= tcpdmatch
+MAN8= tcpdmatch.8
+SRCS= tcpdmatch.c fakelog.c inetcf.c scaffold.c
+
+CFLAGS= -DREAL_DAEMON_DIR=\"/usr/libexec\" \
+ -DSEVERITY=LOG_INFO -DRFC931_TIMEOUT=10
+
+DPADD= ${LIBWRAP}
+LDADD= -lwrap
+
+.PATH: ${.CURDIR}/../../contrib/tcp_wrappers
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile
new file mode 100644
index 0000000..0a1f253
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 0.1 (RGrimes) 4/4/93
+
+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..26c6f1c
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/tcpdump/tcpdump/Makefile b/usr.sbin/tcpdump/tcpdump/Makefile
new file mode 100644
index 0000000..604d7f0
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/Makefile
@@ -0,0 +1,32 @@
+# $Id: Makefile,v 1.20 1998/10/02 21:56:30 fenner Exp $
+
+PROG= tcpdump
+CFLAGS+=-DHAVE_FCNTL_H=1 -DHAVE_NET_SLIP_H=1 -DTIME_WITH_SYS_TIME=1 \
+ -DHAVE_ETHER_NTOHOST=1 -DHAVE_SETLINEBUF=1 -DSTDC_HEADERS=1 \
+ -DRETSIGTYPE=void -DRETSIGVAL= -DHAVE_SOCKADDR_SA_LEN=1 \
+ -DHAVE_TM_GMTOFF=1 -DLBL_ALIGN=1 -DPPP -DHAVE_FDDI -g
+MAN1= tcpdump.1
+SRCS = version.c tcpdump.c \
+ print-arp.c print-atalk.c print-atm.c print-fr.c print-bootp.c \
+ print-decnet.c print-domain.c print-dvmrp.c print-egp.c \
+ print-ether.c print-fddi.c print-gre.c print-icmp.c \
+ print-igrp.c print-ip.c print-ipx.c print-isoclns.c print-krb.c \
+ print-llc.c print-nfs.c print-ntp.c print-null.c print-ospf.c \
+ print-pim.c print-ppp.c print-raw.c print-rip.c print-sl.c \
+ print-snmp.c print-sunrpc.c print-tcp.c print-tftp.c print-udp.c \
+ print-wb.c \
+ addrtoname.c bpf_dump.c gmt2local.c machdep.c parsenfsfh.c \
+ setsignal.c savestr.c util.c print-token.c
+CLEANFILES+= version.c
+DPADD+= ${LIBL} ${LIBPCAP}
+LDADD+= -ll -lpcap
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+CFLAGS+= -I${TCPDUMP_DISTDIR}/lbl
+.PATH: ${TCPDUMP_DISTDIR}
+
+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/tcpslice/Makefile b/usr.sbin/tcpdump/tcpslice/Makefile
new file mode 100644
index 0000000..2d0f33d
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/Makefile
@@ -0,0 +1,21 @@
+# @(#)Makefile 0.1 (RWGrimes) 3/24/93
+
+PROG= tcpslice
+CFLAGS+=-Wall -I.
+MAN1= tcpslice.1
+SRCS= gwtm2secs.c search.c tcpslice.c util.c version.c version.h
+CLEANFILES+= version.c version.h
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+
+.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..51ea194
--- /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[] =
+ "@(#)$Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/gwtm2secs.c,v 1.2 1995/03/08 12:53:38 olah Exp $ (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..b546158
--- /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[] =
+ "@(#)$Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/search.c,v 1.2 1995/03/08 12:53:39 olah Exp $ (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..2ac75e1
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.1
@@ -0,0 +1,269 @@
+.\" 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.
+.\"
+.\" $Id: tcpslice.1,v 1.5 1998/01/20 07:30:26 charnier Exp $
+.\"
+.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 tcpslice
+.Op Fl dRrt
+.Op Fl w Ar file
+.Op Ar start-time Op end-time
+.Ar
+.Sh DESCRIPTION
+.Nm Tcpslice
+is a program for extracting 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
+.Pp
+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 (
+.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 Ns ).
+.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
+.Nm Tcpslice
+cannot read its input from
+.Pa stdin ,
+since it uses random-access
+to rummage through its input files.
+.Pp
+.Nm Tcpslice
+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
+.Nm Tcpslice
+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..a1ff54e
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.c
@@ -0,0 +1,621 @@
+/*
+ * 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[] =
+ "$Id: tcpslice.c,v 1.7 1999/01/15 07:37:00 imp Exp $";
+#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;
+
+ if (gettimeofday(&now, &tz) < 0)
+ err(1, "gettimeofday");
+ localzone = tz.tz_minuteswest * -60;
+
+ if (localtime((time_t *) &timestamp)->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)
+{
+ struct tm *bt = localtime((time_t *) &base_time.tv_sec);
+ 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;
+#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:
+ t = localtime((time_t *) &timestamp->tv_sec);
+ strcpy( buf, asctime( t ) );
+ buf[24] = '\0'; /* nuke final newline */
+ break;
+
+ case TIMESTAMP_PARSEABLE:
+ t = localtime((time_t *) &timestamp->tv_sec);
+ 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..6596f6e
--- /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[] =
+ "@(#) $Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/util.c,v 1.1 1995/03/08 12:53:42 olah Exp $ (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..a89ab16
--- /dev/null
+++ b/usr.sbin/timed/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+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..22ff6e6
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+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..f5a3b19
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/timed.ms
@@ -0,0 +1,504 @@
+.\" 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
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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 an 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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.DS L
+.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
+.DE
+.SH
+.DS L
+.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.
+.DE
+.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..ae43850
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+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..773f477
--- /dev/null
+++ b/usr.sbin/timed/timed/CHANGES
@@ -0,0 +1,144 @@
+# @(#)CHANGES 5.1 (Berkeley) 5/11/93
+
+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 preceeded 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..7edc521
--- /dev/null
+++ b/usr.sbin/timed/timed/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+#
+# optional flags are: MEASURE TESTING DEBUG
+
+PROG= timed
+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
+MAN8= timed.8
+
+.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..766505c
--- /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[] =
+ "$Id: acksend.c,v 1.2 1997/10/22 06:19:48 charnier Exp $";
+#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..3a6ef02
--- /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[] =
+ "$Id$";
+#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..b64f296
--- /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[] =
+ "$Id: candidate.c,v 1.3 1997/10/22 06:19:48 charnier Exp $";
+#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..14cbf52
--- /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[] =
+ "$Id$";
+#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..f28d103
--- /dev/null
+++ b/usr.sbin/timed/timed/correct.c
@@ -0,0 +1,294 @@
+/*-
+ * 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[] =
+ "$Id: correct.c,v 1.2 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#ifdef sgi
+#include <sys/syssgi.h>
+#endif /* sgi */
+
+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;
+ 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(&to.tsp_time,0);
+ timevaladd(&to.tsp_time, &adjlocal);
+ to.tsp_type = TSP_SETTIME;
+ } else {
+ mstotvround(&to.tsp_time, corr);
+ 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 %d 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");
+ }
+
+#ifdef sgi
+ /* Accumulate the total change, and use it to adjust the basic
+ * clock rate.
+ */
+ if (++passes > 2) {
+#define F_USEC_PER_SEC (1000000*1.0) /* reduce typos */
+#define F_NSEC_PER_SEC (F_USEC_PER_SEC*1000.0)
+
+ extern char *timetrim_fn;
+ extern char *timetrim_wpat;
+ extern long timetrim;
+ extern double tot_adj, hr_adj; /* totals in nsec */
+ extern double tot_ticks, hr_ticks;
+
+ static double nag_tick;
+ double cur_ticks, hr_delta_ticks, tot_delta_ticks;
+ double tru_tot_adj, tru_hr_adj; /* nsecs of adjustment */
+ double tot_trim, hr_trim; /* nsec/sec */
+ struct tms tm;
+ FILE *timetrim_st;
+
+ cur_ticks = times(&tm);
+ tot_adj += delta*1000.0;
+ hr_adj += delta*1000.0;
+
+ tot_delta_ticks = cur_ticks-tot_ticks;
+ if (tot_delta_ticks >= 16*SECDAY*CLK_TCK) {
+ tot_adj -= rint(tot_adj/16);
+ tot_ticks += rint(tot_delta_ticks/16);
+ tot_delta_ticks = cur_ticks-tot_ticks;
+ }
+ hr_delta_ticks = cur_ticks-hr_ticks;
+
+ tru_hr_adj = hr_adj + timetrim*rint(hr_delta_ticks/CLK_TCK);
+ tru_tot_adj = (tot_adj
+ + timetrim*rint(tot_delta_ticks/CLK_TCK));
+
+ if (hr_delta_ticks >= SECDAY*CLK_TCK
+ || (tot_delta_ticks < 4*SECDAY*CLK_TCK
+ && hr_delta_ticks >= SECHR*CLK_TCK)
+ || (trace && hr_delta_ticks >= (SECHR/10)*CLK_TCK)) {
+
+ tot_trim = rint(tru_tot_adj*CLK_TCK/tot_delta_ticks);
+ hr_trim = rint(tru_hr_adj*CLK_TCK/hr_delta_ticks);
+
+ if (trace
+ || (abs(timetrim - hr_trim) > 100000.0
+ && 0 == timetrim_fn
+ && ((cur_ticks - nag_tick)
+ >= 24*SECDAY*CLK_TCK))) {
+ nag_tick = cur_ticks;
+ syslog(LOG_NOTICE,
+ "%+.3f/%.2f or %+.3f/%.2f sec/hr; timetrim=%+.0f or %+.0f",
+ tru_tot_adj/F_NSEC_PER_SEC,
+ tot_delta_ticks/(SECHR*CLK_TCK*1.0),
+ tru_hr_adj/F_NSEC_PER_SEC,
+ hr_delta_ticks/(SECHR*CLK_TCK*1.0),
+ tot_trim,
+ hr_trim);
+ }
+
+ if (tot_trim < -MAX_TRIM || tot_trim > MAX_TRIM) {
+ tot_ticks = hr_ticks;
+ tot_adj = hr_adj;
+ } else if (0 > syssgi(SGI_SETTIMETRIM,
+ (long)tot_trim)) {
+ syslog(LOG_ERR, "SETTIMETRIM(%d): %m",
+ (long)tot_trim);
+ } else {
+ if (0 != timetrim_fn) {
+ timetrim_st = fopen(timetrim_fn, "w");
+ if (0 == timetrim_st) {
+ syslog(LOG_ERR, "fopen(%s): %m",
+ timetrim_fn);
+ } else {
+ if (0 > fprintf(timetrim_st,
+ timetrim_wpat,
+ (long)tot_trim,
+ tru_tot_adj,
+ tot_delta_ticks)) {
+ syslog(LOG_ERR,
+ "fprintf(%s): %m",
+ timetrim_fn);
+ }
+ (void)fclose(timetrim_st);
+ }
+ }
+
+ tot_adj -= ((tot_trim - timetrim)
+ * rint(tot_delta_ticks/CLK_TCK));
+ timetrim = tot_trim;
+ }
+
+ hr_ticks = cur_ticks;
+ hr_adj = 0;
+ }
+ }
+#endif /* sgi */
+}
+
+
+/* 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..852dd0c
--- /dev/null
+++ b/usr.sbin/timed/timed/globals.h
@@ -0,0 +1,182 @@
+/*-
+ * 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
+ */
+
+#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>
+#ifdef sgi
+#include <bstring.h>
+#include <sys/clock.h>
+/* use the constant HZ instead of the function CLK_TCK */
+#undef CLK_TCK
+#define CLK_TCK HZ
+#else
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+#endif /* sgi */
+
+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_long 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..c6b399b
--- /dev/null
+++ b/usr.sbin/timed/timed/master.c
@@ -0,0 +1,910 @@
+/*-
+ * 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[] =
+ "$Id: master.c,v 1.4 1997/10/29 07:32:28 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <setjmp.h>
+#ifdef sgi
+#include <sys/schedctl.h>
+#endif /* sgi */
+#include <utmp.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 *));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
+
+/*
+ * 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
+ */
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+#endif /* sgi */
+ 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;
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+#endif /* sgi */
+ 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;
+
+ (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);
+
+ timevalsub(&ntime, &msg->tsp_time, &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 {
+#ifdef sgi
+ if (0 > settimeofday(&msg->tsp_time, 0)) {
+ syslog(LOG_ERR, "settimeofday(): %m");
+ }
+ logwtmp(&otime, &msg->tsp_time);
+#else
+ logwtmp("|", "date", "");
+ (void)settimeofday(&msg->tsp_time, 0);
+ logwtmp("{", "date", "");
+#endif /* sgi */
+ 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;
+#ifdef sgi
+ int pri;
+#endif /* sgi */
+
+ if (slvcount > 0) {
+ if (trace)
+ fprintf(fd, "measurements starting at %s\n", date());
+ (void)gettimeofday(&check, 0);
+#ifdef sgi
+ /* run fast to get good time */
+ pri = schedctl(NDPRI,0,NDPHIMIN);
+ if (pri < 0)
+ syslog(LOG_ERR, "schedctl(): %m");
+#endif /* sgi */
+ 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);
+ }
+ }
+#ifdef sgi
+ if (pri >= 0)
+ (void)schedctl(NDPRI,0,pri);
+#endif /* sgi */
+ 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;
+
+/* 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(&to.tsp_time, 0);
+ 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 (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;
+
+ 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(&to.tsp_time, 0);
+ 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;
+}
+
+
+#ifdef sgi
+void
+logwtmp(otime, ntime)
+ struct timeval *otime, *ntime;
+{
+ static struct utmp wtmp[2] = {
+ {"","",OTIME_MSG,0,OLD_TIME,0,0,0},
+ {"","",NTIME_MSG,0,NEW_TIME,0,0,0}
+ };
+ static char *wtmpfile = WTMP_FILE;
+ int f;
+
+ wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000;
+ wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000;
+ if (wtmp[0].ut_time == wtmp[1].ut_time)
+ return;
+
+ setutent();
+ (void)pututline(&wtmp[0]);
+ (void)pututline(&wtmp[1]);
+ endutent();
+ if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
+ (void) write(f, (char *)wtmp, sizeof(wtmp));
+ (void) close(f);
+ }
+}
+#endif /* sgi */
diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c
new file mode 100644
index 0000000..0882d5e
--- /dev/null
+++ b/usr.sbin/timed/timed/measure.c
@@ -0,0 +1,352 @@
+/*-
+ * 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[] =
+ "$Id: measure.c,v 1.4 1997/10/22 06:19:48 charnier Exp $";
+#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);
+
+#ifdef sgi
+ sginap(1); /* start at a clock tick */
+#endif /* sgi */
+
+ (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;
+{
+#ifndef sgi
+ if (x < 0)
+ x = -((-x + 3)/5);
+ else
+ x = (x+3)/5;
+ x *= 5;
+#endif /* sgi */
+ 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..aeb4a51
--- /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[] =
+ "$Id$";
+#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..ae5e1c5
--- /dev/null
+++ b/usr.sbin/timed/timed/pathnames.h
@@ -0,0 +1,44 @@
+/*-
+ * 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
+ */
+
+#include <paths.h>
+
+#ifdef sgi
+#define _PATH_MASTERLOG "/usr/adm/timed.masterlog"
+#define _PATH_TIMEDLOG "/usr/adm/timed.log"
+#else
+#define _PATH_MASTERLOG "/var/log/timed.masterlog"
+#define _PATH_TIMEDLOG "/var/log/timed.log"
+#endif
diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c
new file mode 100644
index 0000000..6ea6910
--- /dev/null
+++ b/usr.sbin/timed/timed/readmsg.c
@@ -0,0 +1,490 @@
+/*-
+ * 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[] =
+ "$Id: readmsg.c,v 1.3 1997/10/29 07:32:29 charnier Exp $";
+#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;
+
+ 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 (recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
+ (struct sockaddr*)&from, &length) < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
+ (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;
+ }
+
+ 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;
+ 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:
+#ifdef sgi
+ (void)cftime(tm, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ strncpy(tm, ctime(&tsp_time_sec)+3+1, sizeof(tm));
+ tm[15] = '\0'; /* ugh */
+#endif /* sgi */
+ 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..82551f6
--- /dev/null
+++ b/usr.sbin/timed/timed/slave.c
@@ -0,0 +1,720 @@
+/*-
+ * 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[] =
+ "$Id: slave.c,v 1.5 1997/10/31 12:33:06 charnier Exp $";
+#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));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
+
+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;
+ 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);
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+ (void)cftime(olddate, "%D %T", &otime.tv_sec);
+#else
+ /*
+ * 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));
+#endif /* sgi */
+
+ 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);
+ timevalsub(&ntime, &msg->tsp_time, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ synch(tvtomsround(ntime));
+ } else {
+#ifdef sgi
+ if (0 > settimeofday(&msg->tsp_time, 0)) {
+ syslog(LOG_ERR,"settimeofdate(): %m");
+ break;
+ }
+ logwtmp(&otime, &msg->tsp_time);
+#else
+ logwtmp("|", "date", "");
+ (void)settimeofday(&msg->tsp_time, 0);
+ logwtmp("{", "date", "");
+#endif /* sgi */
+ 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:
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+#endif /* sgi */
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_SETDATEREQ:
+ if (fromnet->status != MASTER)
+ break;
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ tsp_time_sec = msg->tsp_time.tv_sec;
+ (void)strcpy(newdate, ctime(&tsp_time_sec));
+#endif /* sgi */
+ 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()
+{
+#ifdef sgi
+ sginap(delay1);
+#else
+ 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;
+#endif /* sgi */
+}
diff --git a/usr.sbin/timed/timed/timed.8 b/usr.sbin/timed/timed/timed.8
new file mode 100644
index 0000000..a8eb74e
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.8
@@ -0,0 +1,226 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt TIMED 8
+.Os BSD 4.3
+.Sh NAME
+.Nm timed
+.Nd time server daemon
+.Sh SYNOPSIS
+.Nm timed
+.Op Fl M
+.Op Fl t
+.Op Fl d
+.Op Fl i Ar network
+.Op Fl n Ar network
+.Op Fl F Ar host1 host2 ...
+.Sh DESCRIPTION
+This
+is a time server daemon and is normally invoked
+at boot time from the
+.Xr rc 8
+file.
+It synchronizes the host's time with the time of other
+machines in a local area network running
+.Nm Ns .
+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 service provided by
+.Nm
+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 timed control program.
+If the machine running the master crashes, then the slaves will elect
+a new master from among slaves running with the
+.Fl M
+flag.
+A
+.Nm
+running without the
+.Fl M
+or
+.Fl F
+flags will remain a slave.
+The
+.Fl t
+flag enables
+.Nm timed
+to trace the messages it receives in the
+file
+.Pa /var/log/timed.log .
+Tracing can be turned on or off by the program
+.Xr timedc 8 .
+The
+.Fl d
+flag is for debugging the daemon.
+It causes the program to not put itself into the background.
+.Pp
+Normally
+.Nm
+checks for a master time server on each network to which
+it is connected, except as modified by the options described below.
+It will request synchronization service from the first master server
+located.
+If permitted by the
+.Fl M
+flag, it will provide synchronization service on any attached networks
+on which no current master server was detected.
+Such a server propagates the time computed by the top-level master.
+The
+.Fl n
+flag, followed by the name of a network which the host is connected to
+(see
+.Xr networks 5 ) ,
+overrides the default choice of the
+network addresses made by the program.
+Each time the
+.Fl n
+flag appears, that network name is added to a list of valid networks.
+All other networks are ignored.
+The
+.Fl i
+flag, followed by the name of a network to which the host is connected
+(see
+.Xr networks 5 ) ,
+overrides the default choice of the network addresses made by the program.
+Each time the
+.Fl i
+flag appears, that network name is added to a list of networks to ignore.
+All other networks are used by the time daemon.
+The
+.Fl n
+and
+.Fl i
+flags are meaningless if used together.
+.Pp
+.Nm Timed
+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.
+If it finds masters on more than one network, it chooses one network
+on which to be a "slave," and then periodically checks the other
+networks to see if the masters there have disappeared.
+.Pp
+One way to synchronize a group of machines is to use an
+.Tn NTP
+daemon to
+synchronize the clock of one machine to a distant standard or a radio
+receiver and
+.Fl F Ar hostname
+to tell its timed daemon 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 "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
+If two or more time daemons, whether
+.Nm Ns ,
+.Tn NTP ,
+try to adjust the same clock, temporal chaos will result.
+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
+"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 timed
+.It Pa /var/log/timed.masterlog
+log file for master timed
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr gettimeofday 2 ,
+.Xr icmp 4 ,
+.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
+daemon 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..7fa1c11
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.c
@@ -0,0 +1,966 @@
+/*-
+ * 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[] = "@(#)timed.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: timed.c,v 1.7 1997/10/31 12:33:06 charnier Exp $";
+#endif /* not lint */
+
+#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>
+#ifdef sgi
+#include <unistd.h>
+#include <sys/syssgi.h>
+#include <sys/schedctl.h>
+#endif /* sgi */
+
+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 __P((void));
+static void pickslavenet __P((struct netinfo *));
+static void add_good_host __P((char *, int));
+
+#ifdef sgi
+char *timetrim_fn;
+char *timetrim_wpat = "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
+char *timetrim_rpat = "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
+long timetrim;
+double tot_adj, hr_adj; /* totals in nsec */
+double tot_ticks, hr_ticks;
+
+int bufspace = 60*1024;
+#endif
+
+static void usage __P((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 sgi
+ FILE *timetrim_st;
+#endif
+
+#ifdef sgi
+ struct tms tms;
+#endif /* sgi */
+
+#ifdef lint
+ ntip = NULL;
+#endif
+
+ on = 1;
+ nflag = OFF;
+ iflag = OFF;
+
+#ifdef sgi
+ if (0 > syssgi(SGI_GETTIMETRIM, &timetrim)) {
+ warn("syssgi(GETTIMETRIM)");
+ timetrim = 0;
+ }
+ tot_ticks = hr_ticks = times(&tms);
+#endif /* sgi */
+
+ 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;
+#ifdef sgi
+ case 'P':
+ timetrim_fn = optarg;
+ timetrim_st = fopen(timetrim_fn, "r+");
+ if (0 == timetrim_st) {
+ if (errno != ENOENT) {
+ warn("%s", timetrim_fn);
+ timetrim_fn = 0;
+ }
+ } else {
+ int i;
+ long trim;
+ double adj, ticks;
+
+ i = fscanf(timetrim_st, timetrim_rpat,
+ &trim, &adj, &ticks);
+ if (i < 1
+ || trim > MAX_TRIM
+ || trim < -MAX_TRIM
+ || i == 2
+ || (i == 3
+ && trim != rint(adj*CLK_TCK/ticks))) {
+ if (trace && i != EOF)
+ warn(
+ "unrecognized contents in %s",
+ timetrim_fn);
+ } else {
+ if (0 > syssgi(SGI_SETTIMETRIM,
+ trim)) {
+ warn("syssgi(SETTIMETRIM)");
+ } else {
+ timetrim = trim;
+ }
+ if (i == 3) {
+ tot_adj = adj;
+ tot_ticks -= ticks;
+ }
+ }
+ (void)fclose(timetrim_st);
+ }
+ break;
+#endif /* sgi */
+
+ 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, "unknown service 'timed/udp'");
+ 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);
+ }
+#ifdef sgi
+ /*
+ * handle many slaves with our buffer
+ */
+ if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace,
+ sizeof(bufspace)))
+ err(1, "setsockopt");
+#endif /* sgi */
+
+ /* choose a unique seed for random number generation */
+ (void)gettimeofday(&ntime, 0);
+ srandom(ntime.tv_sec + ntime.tv_usec);
+
+ sequence = random(); /* initial seq number */
+
+#ifndef sgi
+ /* 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);
+#endif /* sgi */
+
+ 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;
+#ifdef sgi
+#define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */
+#else
+#define size(p) max((p).sa_len, sizeof(p))
+#endif
+ 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");
+
+
+#ifdef sgi
+ (void)schedctl(RENICE,0,10); /* run fast to get good time */
+
+ /* ticks to delay before responding to a broadcast */
+ delay1 = casual(0, CLK_TCK/10);
+#else
+
+ /* microseconds to delay before responding to a broadcast */
+ delay1 = casual(1, 100*1000);
+#endif /* sgi */
+
+ /* election timer delay in secs. */
+ delay2 = casual(MINTOUT, MAXTOUT);
+
+
+#ifdef sgi
+ (void)_daemonize(debug ? _DF_NOFORK|_DF_NOCHDIR : 0, sock, -1, -1);
+#else
+ if (!debug)
+ daemon(debug, 0);
+#endif /* sgi */
+
+ 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 sgi
+ fprintf(stderr, "%s\n%s\n",
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]",
+" [-G netgp] [-P trimfile]");
+#else
+#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 */
+#endif /* sgi */
+ 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()
+{
+#ifdef sgi
+ struct timeval tv;
+ static char tm[32];
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ (void)cftime(tm, "%D %T", &tv.tv_sec);
+ return (tm);
+#else
+ struct timeval tv;
+ time_t tv_sec;
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ tv_sec = tv.tv_sec;
+ return (ctime(&tv_sec));
+#endif /* sgi */
+}
+
+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..9cb4716
--- /dev/null
+++ b/usr.sbin/timed/timedc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= timedc
+SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.c
+MAN8= timedc.8
+BINMODE=4555
+.PATH: ${.CURDIR}/../timed
+
+.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..2c97808
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmds.c
@@ -0,0 +1,526 @@
+/*-
+ * 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[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: cmds.c,v 1.4 1997/10/29 07:35:27 charnier Exp $";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <sys/file.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#define TSPTYPES
+#include <protocols/timed.h>
+
+#ifdef sgi
+#include <bstring.h>
+#include <sys/clock.h>
+#else
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+#endif /* sgi */
+
+# 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 is an 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 transmitts 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 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("udp/timed: 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)strcpy(msg.tsp_name, myname);
+ 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(struct sockaddr);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ &from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ continue;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK) {
+ printf("master timedaemon at %s is %s\n",
+ tgtname, msg.tsp_name);
+ } else {
+ printf("received wrong ack: %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("udp/timed: 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)strcpy(msg.tsp_name, myname);
+ 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 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("udp/timed: 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(struct sockaddr);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ &from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ return;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK)
+ if (onflag)
+ printf("timed tracing enabled\n");
+ else
+ printf("timed tracing disabled\n");
+ 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..85b4bee
--- /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[] =
+ "$Id$";
+#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..7647e08
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.8
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timedc.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TIMEDC 8
+.Os BSD 4.3
+.ad
+.Sh NAME
+.Nm timedc
+.Nd timed control program
+.Sh SYNOPSIS
+.Nm timedc
+.Oo Ar command\ \&
+.Op Ar argument ...
+.Oc
+.Sh DESCRIPTION
+.Nm Timedc
+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
+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 -tag -width Ds -compact
+.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
+command 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..4187f61
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.c
@@ -0,0 +1,260 @@
+/*-
+ * 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <ctype.h>
+#include <err.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <unistd.h>
+
+int trace = 0;
+FILE *fd = 0;
+int margc;
+int fromatty;
+char *margv[20];
+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; *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..dfdc2f5
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.h
@@ -0,0 +1,64 @@
+/*-
+ * 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
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sgi
+#include <sys/uio.h>
+#endif
+#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..e0d9724
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,30 @@
+# $Id: Makefile,v 1.9 1998/09/19 22:42:13 obrien Exp $
+
+PROG= traceroute
+MAN8= traceroute.8
+BINMODE=4555
+CFLAGS+=-DHAVE_SYS_SELECT_H=1 -DHAVE_SETLINEBUF=1 -DHAVE_RAW_OPTIONS=1 \
+ -DSTDC_HEADERS=1
+# 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
+
+SRCS= version.c traceroute.c
+CLEANFILES+= version.c
+
+TRACEROUTE_DISTDIR?= ${.CURDIR}/../../contrib/traceroute
+
+.if ${MACHINE_ARCH} == "alpha"
+# gcc builtin memcpy causes unaligned access
+CFLAGS+= -fno-builtin
+.endif
+
+CFLAGS+= -I${TRACEROUTE_DISTDIR}/lbl
+.PATH: ${TRACEROUTE_DISTDIR}
+
+version.c: ${TRACEROUTE_DISTDIR}/VERSION
+ @rm -f ${.TARGET}
+ sed -e 's/.*/char version[] = "&";/' ${TRACEROUTE_DISTDIR}/VERSION > ${.TARGET}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/Makefile b/usr.sbin/trpt/Makefile
new file mode 100644
index 0000000..19b41da
--- /dev/null
+++ b/usr.sbin/trpt/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= trpt
+MAN8= trpt.8
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8
new file mode 100644
index 0000000..fbed94f
--- /dev/null
+++ b/usr.sbin/trpt/trpt.8
@@ -0,0 +1,152 @@
+.\" 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
+.\"
+.Dd December 11, 1993
+.Dt TRPT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm trpt
+.Nd transliterate protocol trace
+.Sh SYNOPSIS
+.Nm trpt
+.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
+.Nm Trpt
+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 .
+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 /dev/kmem -compact
+.It Pa /kernel
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr setsockopt 2
+.\".Xr setsockopt 2 ,
+.\".Xr trsp 8
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds
+.It Sy 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
+command 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..2d604af
--- /dev/null
+++ b/usr.sbin/trpt/trpt.c
@@ -0,0 +1,380 @@
+/*
+ * 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.
+ */
+
+#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
+#if 0
+static char sccsid[] = "@(#)trpt.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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>
+#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 <unistd.h>
+
+struct nlist nl[] = {
+#define N_TCP_DEBUG 0
+ { "_tcp_debug" },
+#define N_TCP_DEBX 1
+ { "_tcp_debx" },
+ { "" },
+};
+
+static caddr_t tcp_pcbs[TCP_NDEBUG];
+static n_time ntime;
+static int aflag, kflag, memf, follow, sflag, tflag;
+
+void dotrace __P((caddr_t));
+void klseek __P((int, off_t, int));
+int numeric __P((caddr_t *, caddr_t *));
+void tcp_trace __P((short, short, struct tcpcb *, struct tcpcb *,
+ struct tcpiphdr *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, i, jflag, npcbs;
+ char *system, *core;
+
+ 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) {
+ system = *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
+ system = (char *)getbootfile();
+
+ if (nlist(system, nl) < 0 || !nl[0].n_value)
+ errx(1, "%s: no namelist", system);
+ if ((memf = open(core, O_RDONLY)) < 0)
+ err(2, "%s", core);
+ 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("%x", (int)tcp_pcbs[i]);
+ if (++i == npcbs)
+ break;
+ fputs(", ", stdout);
+ }
+ putchar('\n');
+ }
+ else for (i = 0; i < npcbs; i++) {
+ printf("\n%x:\n", (int)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;
+
+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);
+ tcp_trace(td->td_act, td->td_ostate, td->td_tcb, &td->td_cb,
+ &td->td_ti, td->td_req);
+ 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);
+ tcp_trace(td->td_act, td->td_ostate, td->td_tcb, &td->td_cb,
+ &td->td_ti, td->td_req);
+ }
+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, atp, tp, ti, req)
+ short act, ostate;
+ struct tcpcb *atp, *tp;
+ struct tcpiphdr *ti;
+ int req;
+{
+ tcp_seq seq, ack;
+ int flags, len, win, timer;
+
+ printf("%03ld %s:%s ",(ntime/10) % 1000, tcpstates[ostate],
+ tanames[act]);
+ switch (act) {
+ case TA_INPUT:
+ case TA_OUTPUT:
+ case TA_DROP:
+ if (aflag) {
+ printf("(src=%s,%u, ",
+ inet_ntoa(ti->ti_src), ntohs(ti->ti_sport));
+ printf("dst=%s,%u)",
+ inet_ntoa(ti->ti_dst), ntohs(ti->ti_dport));
+ }
+ seq = ti->ti_seq;
+ ack = ti->ti_ack;
+ len = ti->ti_len;
+ win = ti->ti_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)", seq, seq + len);
+ else
+ printf("%lx", seq);
+ printf("@%lx", ack);
+ if (win)
+ printf("(win=%x)", win);
+ flags = ti->ti_flags;
+ if (flags) {
+ register char *cp = "<";
+#define pf(flag, string) { \
+ if (ti->ti_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 %x snd_una %lx snd_nxt %lx snd_max %lx\n",
+ tp->rcv_nxt, tp->rcv_wnd, tp->snd_una, tp->snd_nxt,
+ tp->snd_max);
+ printf("\tsnd_wl1 %lx snd_wl2 %lx snd_wnd %x\n", tp->snd_wl1,
+ tp->snd_wl2, tp->snd_wnd);
+ }
+ /* print out timers? */
+ 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');
+ }
+}
+
+int
+numeric(c1, c2)
+ caddr_t *c1, *c2;
+{
+ 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..5501792
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= tzsetup
+MAN8= tzsetup.8
+CFLAGS+= -I${.CURDIR}
+LDADD+= -ldialog -lncurses -lmytinfo
+DPADD+= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO}
+
+.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..30f5179
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,127 @@
+.\" 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.
+.\"
+.\" $Id: tzsetup.8,v 1.8 1997/10/27 07:49:45 charnier Exp $
+.Dd January 24, 1996
+.Dt TZSETUP 8
+.Os FreeBSD
+.Sh NAME
+.Nm tzsetup
+.Nd set local timezone
+.Sh SYNOPSIS
+.Nm tzsetup
+.Op Fl n
+.Op Ar default
+.Sh DESCRIPTION
+The
+.Nm
+program 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.
+.Nm
+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
+.Pq in Tn ISO No 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
+.Tn FreeeBSD
+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..70b91a5
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,690 @@
+/*
+ * 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[] =
+ "$Id: tzsetup.c,v 1.11 1998/12/16 05:34:49 peter Exp $";
+#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 <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 ((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);
+ cp->tlc = strdup(t);
+ }
+
+ 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)
+ err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
+
+ if (cp->nzones == 0)
+ TAILQ_INIT(&cp->zones);
+
+ zp->descr = strdup(descr);
+ zp->filename = strdup(file);
+ 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);
+ 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 {
+ for (zp = cp->zones.tqh_first; zp;
+ zp = zp->link.tqe_next) {
+ cont = zp->continent;
+ for (zp2 = cp->zones.tqh_first;
+ zp2->continent != cont;
+ zp2 = zp2->link.tqe_next)
+ ;
+ 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)
+ err(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)
+ err(1, "malloc for submenu");
+ cp->nzones = 0;
+ for (zp = cp->zones.tqh_first; zp;
+ zp = zp->link.tqe_next) {
+ 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 = cp->zones.tqh_first;
+ zp2->continent != cont;
+ zp2 = zp2->link.tqe_next)
+ ;
+ 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,
+ 0444);
+ 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",
+ 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, 4, 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;
+
+ while ((c = getopt(argc, argv, "n")) != -1) {
+ switch(c) {
+ case 'n':
+ reallydoit = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (argc - optind > 1)
+ usage();
+
+ read_iso3166_table();
+ read_zones();
+ sort_countries();
+ make_menus();
+
+ init_dialog();
+ if (!dialog_yesno("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"
+ "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, 0666);
+ 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/usbd/Makefile b/usr.sbin/usbd/Makefile
new file mode 100644
index 0000000..526ab61
--- /dev/null
+++ b/usr.sbin/usbd/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= usbd
+MAN8= usbd.8
+CFLAGS+=-I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbd/usbd.8 b/usr.sbin/usbd/usbd.8
new file mode 100644
index 0000000..41e96ae
--- /dev/null
+++ b/usr.sbin/usbd/usbd.8
@@ -0,0 +1,81 @@
+.\" $NetBSD: usbd.8,v 1.2 1998/07/13 11:01:50 augustss Exp $
+.\" FreeBSD $Id$
+.\" 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.
+.\"
+.Dd July 12, 1998
+.Dt USBD 8
+.Os
+.Sh NAME
+.Nm usbd
+.Nd supervise USB attach/detach
+.Sh SYNOPSIS
+.Nm
+.Op Fl d
+.Op Fl f Ar device
+.Op Fl t Ar timeout
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+handles the USB device attachment and detachment.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.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 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 ,
+.Pa /dev/usb1 ,
+.Pa /dev/usb2 ,
+and
+.Pa /dev/usb3 .
+.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.
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Nx 1.4 .
diff --git a/usr.sbin/usbd/usbd.c b/usr.sbin/usbd/usbd.c
new file mode 100644
index 0000000..bde1cc6
--- /dev/null
+++ b/usr.sbin/usbd/usbd.c
@@ -0,0 +1,184 @@
+/* $NetBSD: usbd.c,v 1.2 1998/07/23 18:39:53 augustss Exp $ */
+/* FreeBSD $Id$ */
+
+/*
+ * 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.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/time.h>
+#if defined(__FreeBSD__)
+#include <sys/ioctl.h>
+#include <sys/malloc.h>
+#endif
+#include <dev/usb/usb.h>
+
+#define USBDEV "/dev/usb"
+#define MAXUSBDEV 4
+
+extern char *__progname;
+
+void usage(void);
+
+void
+usage(void)
+{
+ fprintf(stderr, "Usage: %s [-d] [-e] [-f dev] [-t timeout] [-v]\n",
+ __progname);
+ exit(1);
+}
+
+#define NDEVS 20 /* maximum number of usb controllers */
+
+/*
+ * 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
+
+int
+main(int argc, char **argv)
+{
+ int r, i;
+ char *devs[NDEVS];
+ int ndevs = 0;
+ int fds[NDEVS];
+ fd_set fdset;
+ int ch, verbose = 0;
+ int debug = 0;
+ int explore = 0;
+ int itimo = TIMEOUT;
+ int maxfd;
+ char buf[50];
+ struct timeval timo;
+ extern char *optarg;
+ extern int optind;
+
+ while ((ch = getopt(argc, argv, "def:t:v")) != -1) {
+ switch(ch) {
+ case 'd':
+ debug++;
+ break;
+ case 'e':
+ explore++;
+ break;
+ case 'f':
+ if (ndevs < NDEVS)
+ devs[ndevs++] = optarg;
+ break;
+ case 't':
+ itimo = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ maxfd = 0;
+ if (ndevs == 0) {
+ 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 (verbose)
+ printf("%s: opening %s\n",
+ __progname, devs[ndevs]);
+ if (fds[ndevs] > maxfd)
+ maxfd = fds[ndevs];
+ ndevs++;
+ }
+ }
+ } else {
+ for (i = 0; i < ndevs; i++) {
+ fds[i] = open(devs[i], O_RDWR);
+ if (fds[i] < 0)
+ err(1, "%s", devs[i]);
+ else if (fds[i] > maxfd)
+ maxfd = fds[i];
+ }
+ }
+ if (ndevs == 0) {
+ if (verbose)
+ printf("%s: no USB controllers found\n", __progname);
+ exit(0);
+ }
+
+ if (explore) {
+ for (i = 0; i < ndevs; i++) {
+ r = ioctl(fds[i], USB_DISCOVER);
+ if (r < 0)
+ err(1, "USB_DISCOVER");
+ }
+ exit(0);
+ }
+
+ if (!debug)
+ daemon(0, 0);
+
+
+
+ FD_ZERO(&fdset);
+ for (;;) {
+ for (i = 0; i < ndevs; i++)
+ FD_SET(fds[i], &fdset);
+ timo.tv_usec = 0;
+ timo.tv_sec = itimo;
+ r = select(maxfd+1, &fdset, &fdset, 0, itimo ? &timo : 0);
+ if (r < 0)
+ warn("select failed\n");
+ for (i = 0; i < ndevs; i++)
+ if (r == 0 || FD_ISSET(fds[i], &fdset)) {
+ if (verbose)
+ printf("%s: doing %sdiscovery on %s\n",
+ __progname, r ? "" : "timeout ",
+ devs[i]);
+ if (ioctl(fds[i], USB_DISCOVER) < 0)
+ err(1, "USB_DISCOVER");
+ }
+ }
+}
diff --git a/usr.sbin/usbdevs/Makefile b/usr.sbin/usbdevs/Makefile
new file mode 100644
index 0000000..0393156
--- /dev/null
+++ b/usr.sbin/usbdevs/Makefile
@@ -0,0 +1,8 @@
+# $NetBSD: Makefile,v 1.2 1998/07/12 20:40:45 augustss Exp $
+# FreeBSD $Id: Makefile,v 1.2 1998/12/13 15:52:26 bde Exp $
+
+PROG= usbdevs
+MAN8= usbdevs.8
+CFLAGS+=-I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/usbdevs/usbdevs.8 b/usr.sbin/usbdevs/usbdevs.8
new file mode 100644
index 0000000..46ed36e
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.8
@@ -0,0 +1,68 @@
+.\" $NetBSD: usbdevs.8,v 1.3 1998/07/23 13:57:51 augustss Exp $
+.\" FreeBSD $Id$
+.\" 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.
+.\"
+.Dd July 12, 1998
+.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 f Ar dev
+.Op Fl v
+.Sh DESCRIPTION
+.Nm
+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 Ds
+.It Fl a Ar addr
+only print information about the device at the given address.
+.It Fl f Ar dev
+only print information for the given USB controller.
+.It Fl v
+Be verbose.
+.El
+.Sh SEE ALSO
+.Xr usb 4
+.Sh HISTORY
+The
+.Nm
+command 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..171c908
--- /dev/null
+++ b/usr.sbin/usbdevs/usbdevs.c
@@ -0,0 +1,213 @@
+/* $NetBSD: usbdevs.c,v 1.4 1998/07/23 13:57:51 augustss Exp $ */
+/* FreeBSD $Id$ */
+
+/*
+ * 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.
+ */
+
+#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;
+
+void usage __P((void));
+void usbdev __P((int f, int a, int rec));
+void usbdump __P((int f));
+void dumpone __P((char *name, int f, int addr));
+int main __P((int, char **));
+
+extern char *__progname;
+
+void
+usage()
+{
+ fprintf(stderr, "Usage: %s [-a addr] [-f dev] [-v]\n", __progname);
+ exit(1);
+}
+
+char done[USB_MAX_DEVICES];
+int indent;
+
+void
+usbdev(f, a, rec)
+ int f;
+ int a;
+ int rec;
+{
+ struct usb_device_info di;
+ int e, p;
+
+ di.addr = a;
+ e = ioctl(f, USB_DEVICEINFO, &di);
+ if (e)
+ return;
+ done[a] = 1;
+ printf("addr %d: ", di.addr);
+ if (verbose) {
+ if (di.lowspeed)
+ printf("low speed, ");
+ if (di.power)
+ printf("power %d mA, ", di.power);
+ else
+ printf("self powered, ");
+ if (di.config)
+ printf("config %d, ", di.config);
+ else
+ printf("unconfigured, ");
+ }
+ printf("%s, %s", di.product, di.vendor);
+ if (verbose)
+ printf(", rev %s", di.revision);
+ printf("\n");
+ if (!rec)
+ return;
+ for (p = 0; p < di.nports; p++) {
+ int s = di.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);
+ usbdev(f, di.ports[p], 1);
+ indent--;
+ }
+}
+
+void
+usbdump(f)
+ int f;
+{
+ int a;
+
+ for (a = 1; a < USB_MAX_DEVICES; a++) {
+ if (!done[a])
+ usbdev(f, a, 1);
+ }
+}
+
+void
+dumpone(name, f, addr)
+ 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(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, i, f;
+ char buf[50];
+ extern int optind;
+ extern char *optarg;
+ char *dev = 0;
+ int addr = 0;
+ int ncont;
+
+ while ((ch = getopt(argc, argv, "a:f:v")) != -1) {
+ switch(ch) {
+ case 'a':
+ addr = atoi(optarg);
+ break;
+ case 'f':
+ dev = optarg;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (dev == 0) {
+ for (ncont = 0, i = 0; i < 10; i++) {
+ sprintf(buf, "%s%d", USBDEV, i);
+ f = open(buf, O_RDONLY);
+ if (f >= 0) {
+ ncont++;
+ dumpone(buf, f, addr);
+ close(f);
+ } else {
+ if (errno == EACCES)
+ warn("%s", buf);
+ }
+ }
+ if (verbose && ncont == 0)
+ printf("%s: no USB controllers found\n", __progname);
+ } 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..0200812
--- /dev/null
+++ b/usr.sbin/vidcontrol/Makefile
@@ -0,0 +1,6 @@
+PROG= vidcontrol
+SRCS= vidcontrol.c decode.c
+
+CFLAGS += -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c
new file mode 100644
index 0000000..fd24e76
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.c
@@ -0,0 +1,80 @@
+/*-
+ * 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 withough 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[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include "decode.h"
+
+int decode(FILE *fd, char *buffer)
+{
+ int n, pos = 0;
+ char *p;
+ 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);
+ for (;;) {
+ if (!fgets(p = temp, sizeof(temp), fd))
+ return(0);
+ if ((n = DEC(*p)) <= 0)
+ break;
+ for (++p; n > 0; p += 4, n -= 3)
+ if (n >= 3) {
+ buffer[pos++] = DEC(p[0])<<2 | DEC(p[1])>>4;
+ buffer[pos++] = DEC(p[1])<<4 | DEC(p[2])>>2;
+ buffer[pos++] = DEC(p[2])<<6 | DEC(p[3]);
+ }
+ else {
+ if (n >= 1) {
+ buffer[pos++] =
+ DEC(p[0])<<2 | DEC(p[1])>>4;
+ }
+ if (n >= 2) {
+ buffer[pos++] =
+ DEC(p[1])<<4 | DEC(p[2])>>2;
+ }
+ if (n >= 3) {
+ buffer[pos++] =
+ DEC(p[2])<<6 | DEC(p[3]);
+ }
+ }
+ }
+ 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..b939af4
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.h
@@ -0,0 +1 @@
+int decode(FILE *fd, char *buffer);
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..16495dd
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.1
@@ -0,0 +1,147 @@
+.\"
+.\" 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
+.\" $Id: vidcontrol.1,v 1.13 1998/09/15 18:16:39 sos Exp $
+.\"
+.Dd May 22, 1994
+.Dt VIDCONTROL 1
+.Os
+.Sh NAME
+.Nm vidcontrol
+.Nd a utility for manipulating the syscons video driver.
+.Sh SYNOPSIS
+.Nm
+.Op Fl r Ar fg Ar bg
+.Op Fl b Ar color
+.Op Fl c Ar appearance
+.Op Fl d
+.Op Fl i Ar adapter|mode
+.Op Fl l Ar scrmap
+.Op Fl L
+.Op Fl m Ar on|off
+.Op Fl f Ar size Ar file
+.Op Fl s Ar number
+.Op Fl t Ar N|off
+.Op Fl x
+.Op mode
+.Op fgcol Op bgcol
+.Op show
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various options for the syscons video driver,
+such as video mode, colors, cursors, scrnmaps, font and screensaver timeout.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It mode
+Select a new video mode. The modes currently recognized are:
+.Ar 40x25 ,
+.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 EGA_80x25 ,
+.Ar EGA_80x43 ,
+.Ar VESA_132x25 ,
+.Ar VESA_132x30 ,
+.Ar VESA_132x43 ,
+.Ar VESA_132x50 ,
+.Ar VESA_132x60 .
+The graphic mode
+.Ar VGA_320x200
+and
+.Ar VESA_800x600
+can also be chosen.
+Note that not all modes listed above may be supported by the video
+hardware, and that the VESA BIOS support must be linked to the kernel
+or loaded as a LKM if you wish to use VESA video modes or
+132 column modes.
+.It fgcol Op bgcol
+Change colors when displaying text. Specify the foreground color
+(e.g. "vidcontrol white"), or both a foreground & background color
+(e.g. "vidcontrol yellow blue").
+.It show
+See the supported colors on a given platform.
+.It Fl r Ar foreground Ar background
+Change reverse mode colors to
+.Ar foreground
+and
+.Ar background .
+.It Fl b Ar color
+Set border color to
+.Ar color
+(only supported on VGA hardware).
+.It Fl c Ar normal|blink|destructive
+Change the cursor appearance. The cursor is either an inverting block
+(normal) that eventually can "blink". Or it can be like the old hardware cursor
+(destructive). The latter is actually a simulation.
+.It Fl d
+Print out current screen output map.
+.It Fl l Ar scrmap
+Install screen output map file from
+.Ar scrmap
+.It Fl L
+Install default screen output map.
+.It Fl i Ar adapter
+Shows info about the current videoadapter.
+.It Fl i Ar mode
+Shows the possible videomodes with the current video hardware.
+.It Fl m Ar on|off
+Switch the mousepointer
+.Ar on
+or
+.Ar off .
+Used together with the moused
+daemon for textmode cut & paste functionality.
+.It Fl f Ar size Ar file
+Load font
+.Ar file
+for
+.Ar size
+(currently, only 8x8, 8x14 or 8x16).
+The font file can be either uuencoded or in raw binary format.
+.It Fl s Ar number
+Set the current vty to
+.Ar number .
+.It Fl t Ar N|off
+Set the screensaver timeout to
+.Ar N
+seconds, or turns it
+.Ar off .
+.It Fl x
+Use hexadecimal digits for output.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/scrnmaps -compact
+.It Pa /usr/share/syscons/fonts
+.It Pa /usr/share/syscons/scrnmaps
+.El
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr moused 8
+.Sh AUTHORS
+.An Søren Schmidt Aq sos@FreeBSD.org
diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c
new file mode 100644
index 0000000..f9604fc
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.c
@@ -0,0 +1,584 @@
+/*-
+ * 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 withough 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[] =
+ "$Id: vidcontrol.c,v 1.26 1999/01/12 23:05:45 mjacob Exp $";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.h>
+#include <sys/errno.h>
+#include "path.h"
+#include "decode.h"
+
+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;
+char letter;
+struct vid_info info;
+
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: vidcontrol [-r fg bg] [-b color] [-c appearance] [-d] [-l scrmap]",
+" [-i adapter | mode] [-L] [-m on|off] [-f size file]",
+" [-s number] [-t N|off] [-x] [mode] [fgcol [bgcol]] [show]");
+ exit(1);
+}
+
+char *
+nextarg(int ac, char **av, int *indp, int oc)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ errx(1, "option requires two arguments -- %c", oc);
+ return("");
+}
+
+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);
+}
+
+void
+load_scrnmap(char *filename)
+{
+ FILE *fd = 0;
+ int i, size;
+ char *name;
+ scrmap_t scrnmap;
+ char *prefix[] = {"", "", SCRNMAP_PATH, SCRNMAP_PATH, NULL};
+ char *postfix[] = {"", ".scm", "", ".scm"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], filename, postfix[i]);
+ fd = fopen(name, "r");
+ if (fd)
+ break;
+ }
+ if (fd == NULL) {
+ warn("screenmap file not found");
+ return;
+ }
+ size = sizeof(scrnmap);
+ if (decode(fd, (char *)&scrnmap) != 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");
+
+}
+
+void
+load_font(char *type, char *filename)
+{
+ FILE *fd = 0;
+ int i, size;
+ unsigned long io;
+ char *name, *fontmap;
+ char *prefix[] = {"", "", FONT_PATH, FONT_PATH, NULL};
+ char *postfix[] = {"", ".fnt", "", ".fnt"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], filename, postfix[i]);
+ fd = fopen(name, "r");
+ if (fd)
+ break;
+ }
+ if (fd == NULL) {
+ warn("font file not found");
+ return;
+ }
+ if (!strcmp(type, "8x8")) {
+ size = 8*256;
+ io = PIO_FONT8x8;
+ }
+ else if (!strcmp(type, "8x14")) {
+ size = 14*256;
+ io = PIO_FONT8x14;
+ }
+ else if (!strcmp(type, "8x16")) {
+ size = 16*256;
+ io = PIO_FONT8x16;
+ }
+ else {
+ warn("bad font size specification");
+ fclose(fd);
+ return;
+ }
+ fontmap = (char*) malloc(size);
+ if (decode(fd, fontmap) != size) {
+ rewind(fd);
+ if (fread(fontmap, 1, size, fd) != size) {
+ warnx("bad font file");
+ 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);
+}
+
+void
+video_mode(int argc, char **argv, int *index)
+{
+ static struct {
+ char *name;
+ unsigned long mode;
+ } modes[] = {
+#ifdef SW_TEXT_80x25
+ { "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 },
+#endif
+ { "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 },
+ { "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 size[3];
+ int i;
+
+ 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;
+ if (ioctl(0, mode, NULL) < 0)
+ warn("cannot set videomode");
+ if (mode == SW_VESA_800x600) {
+ size[0] = 80; /* columns */
+ size[1] = 25; /* rows */
+ size[2] = 16; /* font size */
+ if (ioctl(0, KDRASTER, size))
+ warn("cannot activate raster display");
+ }
+ (*index)++;
+ }
+ return;
+}
+
+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 > 12) {
+ 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 *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 either on or off");
+ return;
+ }
+ ioctl(0, CONS_MOUSECTL, &mouse);
+}
+
+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" },
+ { -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);
+}
+
+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;
+
+ 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 %2dk\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 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);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+
+
+ info.size = sizeof(info);
+ if (ioctl(0, CONS_GETINFO, &info) < 0)
+ err(1, "must be on a virtual console");
+ while((opt = getopt(argc, argv, "b:c:df:i:l:Lm:r:s:t:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_border_color(optarg);
+ break;
+ case 'c':
+ set_cursor_type(optarg);
+ break;
+ case 'd':
+ print_scrnmap();
+ break;
+ case 'f':
+ load_font(optarg,
+ nextarg(argc, argv, &optind, 'f'));
+ break;
+ case 'i':
+ show_info(optarg);
+ break;
+ case 'l':
+ load_scrnmap(optarg);
+ break;
+ case 'L':
+ load_default_scrnmap();
+ break;
+ case 'm':
+ set_mouse(optarg);
+ break;
+ case 'r':
+ set_reverse_colors(argc, argv, &optind);
+ break;
+ case 's':
+ set_console(optarg);
+ break;
+ case 't':
+ set_screensaver_timeout(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+ 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 0;
+}
+
diff --git a/usr.sbin/vipw/Makefile b/usr.sbin/vipw/Makefile
new file mode 100644
index 0000000..e1c3978
--- /dev/null
+++ b/usr.sbin/vipw/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= vipw
+SRCS= pw_util.c vipw.c
+MAN8= vipw.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vipw/pw_util.c b/usr.sbin/vipw/pw_util.c
new file mode 100644
index 0000000..2003cd6
--- /dev/null
+++ b/usr.sbin/vipw/pw_util.c
@@ -0,0 +1,256 @@
+/*-
+ * Copyright (c) 1990, 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
+#if 0
+static const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id: pw_util.c,v 1.12 1998/12/13 01:36:45 dillon Exp $";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pw_util.h"
+
+extern char *tempname;
+static pid_t editpid = -1;
+static int lockfd;
+
+void
+pw_cont(sig)
+ int sig;
+{
+
+ if (editpid != -1)
+ kill(editpid, sig);
+}
+
+void
+pw_init()
+{
+ struct rlimit rlim;
+
+ /* 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_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGCONT, pw_cont);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+}
+
+int
+pw_lock()
+{
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ for (;;) {
+ struct stat st;
+
+ lockfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
+ if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1)
+ err(1, "%s", _PATH_MASTERPASSWD);
+ if (flock(lockfd, LOCK_EX|LOCK_NB))
+ errx(1, "the password db file is busy");
+
+ /*
+ * If the password file was replaced while we were trying to
+ * get the lock, our hardlink count will be 0 and we have to
+ * close and retry.
+ */
+ if (fstat(lockfd, &st) < 0)
+ errx(1, "fstat() failed");
+ if (st.st_nlink != 0)
+ break;
+ close(lockfd);
+ lockfd = -1;
+ }
+ return (lockfd);
+}
+
+int
+pw_tmp()
+{
+ static char path[MAXPATHLEN] = _PATH_MASTERPASSWD;
+ int fd;
+ char *p;
+
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "pw.XXXXXX");
+ if ((fd = mkstemp(path)) == -1)
+ err(1, "%s", path);
+ tempname = path;
+ return (fd);
+}
+
+int
+pw_mkdb(username)
+char *username;
+{
+ int pstat;
+ pid_t pid;
+
+ (void)fflush(stderr);
+ if (!(pid = fork())) {
+ if(!username) {
+ warnx("rebuilding the database...");
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", tempname, NULL);
+ } else {
+ warnx("updating the database...");
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-u",
+ username, tempname, NULL);
+ }
+ pw_error(_PATH_PWD_MKDB, 1, 1);
+ }
+ pid = waitpid(pid, &pstat, 0);
+ if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
+ return (0);
+ warnx("done");
+ return (1);
+}
+
+void
+pw_edit(notsetuid)
+ int notsetuid;
+{
+ int pstat;
+ char *p, *editor;
+
+ if (!(editor = getenv("EDITOR")))
+ editor = _PATH_VI;
+ if ((p = strrchr(editor, '/')))
+ ++p;
+ else
+ p = editor;
+
+ if (!(editpid = fork())) {
+ if (notsetuid) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+ errno = 0;
+ execlp(editor, p, tempname, NULL);
+ _exit(errno);
+ }
+ for (;;) {
+ editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
+ errno = WEXITSTATUS(pstat);
+ if (editpid == -1)
+ pw_error(editor, 1, 1);
+ else if (WIFSTOPPED(pstat))
+ raise(WSTOPSIG(pstat));
+ else if (WIFEXITED(pstat) && errno == 0)
+ break;
+ else
+ pw_error(editor, 1, 1);
+ }
+ editpid = -1;
+}
+
+void
+pw_prompt()
+{
+ int c, first;
+
+ (void)printf("re-edit the password file? [y]: ");
+ (void)fflush(stdout);
+ first = c = getchar();
+ while (c != '\n' && c != EOF)
+ c = getchar();
+ if (first == 'n')
+ pw_error(NULL, 0, 0);
+}
+
+void
+pw_error(name, err, eval)
+ char *name;
+ int err, eval;
+{
+#ifdef YP
+ extern int _use_yp;
+#endif /* YP */
+ if (err)
+ warn(name);
+#ifdef YP
+ if (_use_yp)
+ warnx("NIS information unchanged");
+ else
+#endif /* YP */
+ warnx("%s: unchanged", _PATH_MASTERPASSWD);
+ (void)unlink(tempname);
+ exit(eval);
+}
diff --git a/usr.sbin/vipw/pw_util.h b/usr.sbin/vipw/pw_util.h
new file mode 100644
index 0000000..c6aab8e
--- /dev/null
+++ b/usr.sbin/vipw/pw_util.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pw_util.h 8.2 (Berkeley) 4/1/94
+ */
+
+void pw_edit __P((int));
+void pw_error __P((char *, int, int));
+void pw_init __P((void));
+int pw_lock __P((void));
+int pw_mkdb __P((char *));
+void pw_prompt __P((void));
+int pw_tmp __P((void));
diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8
new file mode 100644
index 0000000..ca70b5e
--- /dev/null
+++ b/usr.sbin/vipw/vipw.8
@@ -0,0 +1,93 @@
+.\" 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
+.\"
+.Dd June 6, 1993
+.Dt VIPW 8
+.Os BSD 4
+.Sh NAME
+.Nm vipw
+.Nd edit the password file
+.Sh SYNOPSIS
+.Nm vipw
+.Sh DESCRIPTION
+.Nm Vipw
+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
+.Nm Vipw
+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 Ns :
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pwd_mkdb 8
+.Sh HISTORY
+The
+.Nm
+command 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..30b789e
--- /dev/null
+++ b/usr.sbin/vipw/vipw.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1987, 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) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)vipw.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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 "pw_util.h"
+
+char *tempname;
+
+void copyfile __P((int, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int pfd, tfd;
+ struct stat begin, end;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ pw_init();
+ pfd = pw_lock();
+ tfd = pw_tmp();
+ copyfile(pfd, tfd);
+ (void)close(tfd);
+
+ for (;;) {
+ if (stat(tempname, &begin))
+ pw_error(tempname, 1, 1);
+ pw_edit(0);
+ if (stat(tempname, &end))
+ pw_error(tempname, 1, 1);
+ if (begin.st_mtime == end.st_mtime) {
+ warnx("no changes made");
+ pw_error((char *)NULL, 0, 0);
+ }
+ if (pw_mkdb((char *)NULL))
+ break;
+ pw_prompt();
+ }
+ exit(0);
+}
+
+void
+copyfile(from, to)
+ int from, to;
+{
+ int nr, nw, off;
+ char buf[8*1024];
+
+ while ((nr = read(from, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; nr -= nw, off += nw)
+ if ((nw = write(to, buf + off, nr)) < 0)
+ pw_error(tempname, 1, 1);
+ if (nr < 0)
+ pw_error(_PATH_MASTERPASSWD, 1, 1);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: vipw\n");
+ exit(1);
+}
diff --git a/usr.sbin/vnconfig/Makefile b/usr.sbin/vnconfig/Makefile
new file mode 100644
index 0000000..eba48cb
--- /dev/null
+++ b/usr.sbin/vnconfig/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= vnconfig
+MAN8= vnconfig.8
+MLINKS= vnconfig.8 swapfile.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vnconfig/vnconfig.8 b/usr.sbin/vnconfig/vnconfig.8
new file mode 100644
index 0000000..e953133
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.8
@@ -0,0 +1,211 @@
+.\" Copyright (c) 1993 University of Utah.
+.\" Copyright (c) 1980, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Systems Programming Group of the University of Utah Computer
+.\" Science Department.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vnconfig.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd July 8, 1993
+.Dt VNCONFIG 8
+.Os BSD 4
+.Sh NAME
+.Nm vnconfig
+.Nd configure and enable vnode disks
+.Sh SYNOPSIS
+.Nm vnconfig
+.Op Fl cdeguv
+.Op Fl s Ar option
+.Op Fl r Ar option
+.Op Fl S Ar value
+.Ar special_file Ar [regular_file]
+.Op Ar feature
+.Nm vnconfig
+.Fl a
+.Op Fl cdeguv
+.Op Fl s Ar option
+.Op Fl r Ar option
+.Op Fl f Ar config_file
+.Sh DESCRIPTION
+The
+.Nm
+command configures and enables vnode pseudo disk devices.
+The first form of the command will associate the special file
+.Ar special_file
+with the regular file
+.Ar regular_file
+allowing the latter to be accessed as though it were a disk.
+Hence a regular file within the filesystem can be used for swapping
+or can contain a filesystem that is mounted in the name space. If you
+want to use swap backing store for your device instead of a file, you
+can leave regular_file out and specify the size of the block device
+with the -S option.
+.Pp
+Options indicate an action to be performed:
+.Bl -tag -width indent
+.It Fl a
+Read a command file and performs the
+specified actions for each device/file pair.
+.It Fl c
+Configure the device.
+If successful, references to
+.Ar special_file
+will access the contents of
+.Ar regular_file .
+.It Fl d
+Disable (if possible) the specified feature.
+.It Fl e
+Configure the device and enables any
+.Ar feature
+that was specified.
+If no feature was specified,
+.Fl e
+is the same as
+.Fl c .
+.It Fl f Ar config_file
+Use
+.Ar config_file
+as an alternate config file.
+.It Fl g
+Fiddle global options.
+.It Fl r Ar flag
+Reset
+.Ar flag .
+The list of allowed flags and their meanings are:
+.Bl -tag -width "follow"
+.It Ar labels
+use disk/slice labels.
+.It Ar follow
+debug flow in the
+.Xr vn 4
+driver.
+.It Ar debug
+debug data in the
+.Xr vn 4
+driver.
+.It Ar io
+debug I/O in the
+.Xr vn 4
+driver.
+.It Ar all
+turn on all flags.
+.It Ar none
+turn off all flags.
+.El
+.It Fl s Ar flag
+Set
+.Ar flag .
+The list of allowed flags and their meanings are the same as for the
+.Fl r
+option.
+.It Fl S Ar value{k,m,g,t}
+If no regular file is specified, VN will use swap for backing store.
+This option specifies the size of the device. For example, '23m' for
+23 megabytes. The VN device will round the size up to a machine page boundry.
+Filesystems up to 7.9 terrabytes are supported.
+.It Fl u
+Disable and ``unconfigure'' the device.
+.It Fl v
+Print messages to stdout describing actions taken.
+.El
+.Pp
+If no action option is given,
+.Fl c
+is assumed.
+.Pp
+The
+.Ar feature
+argument specifies a feature that can be enabled via the
+.Fl e
+option:
+.Bl -tag -width indent
+.It Dv swap
+Swapping is enabled on the special file.
+See
+.Xr swapon 2 .
+.It Dv Pf mountro= Pa mount_point
+The special file is mounted read-only on
+.Ar mount_point .
+See
+.Xr mount 2 .
+.It Dv Pf mountrw= Pa mount_point
+The special file is mounted read-write on
+.Ar mount_point .
+See
+.Xr mount 2 .
+.It Dv Pf mount= Pa mount_point
+Same as ``mountrw=''.
+.El
+.Pp
+A configuration file contains one line per device/file pair in the form:
+.Bd -literal
+ special_file regular_file [ feature ]
+.Ed
+.Pp
+where fields are seperated by white space.
+The previously described action options serve to configure, enable,
+disable or unconfigure all devices in the configuration file.
+.Sh FILES
+.Bl -tag -width /etc/vntab -compact
+.It Pa /etc/vntab
+default configuration file for
+.Fl a
+option.
+.El
+.Sh EXAMPLES
+.Pp
+.Dl vnconfig /dev/vn0c /tmp/diskimage
+.Pp
+Configures the vnode disk
+.Pa vn0c .
+.Pp
+.Dl vnconfig -e /dev/vn0c /var/swapfile swap
+.Pp
+Configures
+.Pa vn0c
+and enables swapping on it.
+.Pp
+.Dl vnconfig -d /dev/vn0c myfilesystem mount=/mnt
+.Pp
+Unmounts (disables)
+.Pa vn0c .
+.Pp
+.Dl vnconfig -ae
+.Pp
+Configures and enables all devices specified in
+.Pa /etc/vntab .
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr swapon 2 ,
+.Xr unmount 2 ,
+.Xr vn 4 .
diff --git a/usr.sbin/vnconfig/vnconfig.c b/usr.sbin/vnconfig/vnconfig.c
new file mode 100644
index 0000000..311c08b
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.c
@@ -0,0 +1,537 @@
+/*
+ * Copyright (c) 1993 University of Utah.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)vnconfig.c 8.1 (Berkeley) 12/15/93";
+#endif
+static const char rcsid[] =
+ "$Id: vnconfig.c,v 1.8 1999/01/26 04:53:09 peter Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/module.h>
+#include <sys/stat.h>
+#include <sys/vnioctl.h>
+#include <ufs/ufs/ufsmount.h>
+
+#define MAXVNDISK 16
+#define LINESIZE 1024
+
+struct vndisk {
+ char *dev;
+ char *file;
+ char *autolabel;
+ int flags;
+ int size;
+ char *oarg;
+} vndisks[MAXVNDISK];
+
+#define VN_CONFIG 0x01
+#define VN_UNCONFIG 0x02
+#define VN_ENABLE 0x04
+#define VN_DISABLE 0x08
+#define VN_SWAP 0x10
+#define VN_MOUNTRO 0x20
+#define VN_MOUNTRW 0x40
+#define VN_IGNORE 0x80
+#define VN_SET 0x100
+#define VN_RESET 0x200
+
+int nvndisks;
+
+int all = 0;
+int verbose = 0;
+int global = 0;
+u_long setopt = 0;
+u_long resetopt = 0;
+char *configfile;
+
+int config __P((struct vndisk *));
+void getoptions __P((struct vndisk *, char *));
+char *rawdevice __P((char *));
+void readconfig __P((int));
+static void usage __P((void));
+static int getsize(const char *arg);
+static void do_autolabel(const char *dev, const char *label);
+int what_opt __P((char *, u_long *));
+
+int
+main(argc, argv)
+ char **argv;
+{
+ register int i, rv;
+ int flags = 0;
+ int size = 0;
+ char *autolabel = NULL;
+
+ configfile = _PATH_VNTAB;
+ while ((i = getopt(argc, argv, "acdef:gr:s:S:L:uv")) != -1)
+ switch (i) {
+
+ /* all -- use config file */
+ case 'a':
+ all++;
+ break;
+
+ /* configure */
+ case 'c':
+ flags |= VN_CONFIG;
+ flags &= ~VN_UNCONFIG;
+ break;
+
+ /* disable */
+ case 'd':
+ flags |= VN_DISABLE;
+ flags &= ~VN_ENABLE;
+ break;
+
+ /* enable */
+ case 'e':
+ flags |= (VN_ENABLE|VN_CONFIG);
+ flags &= ~(VN_DISABLE|VN_UNCONFIG);
+ break;
+
+ /* alternate config file */
+ case 'f':
+ configfile = optarg;
+ break;
+
+ /* fiddle global options */
+ case 'g':
+ global = 1 - global;
+ break;
+
+ /* reset options */
+ case 'r':
+ if (what_opt(optarg,&resetopt))
+ errx(1, "invalid options '%s'", optarg);
+ flags |= VN_RESET;
+ break;
+
+ /* set options */
+ case 's':
+ if (what_opt(optarg,&setopt))
+ errx(1, "invalid options '%s'", optarg);
+ flags |= VN_SET;
+ break;
+
+ /* unconfigure */
+ case 'u':
+ flags |= (VN_DISABLE|VN_UNCONFIG);
+ flags &= ~(VN_ENABLE|VN_CONFIG);
+ break;
+
+ /* verbose */
+ case 'v':
+ verbose++;
+ break;
+
+ case 'S':
+ size = getsize(optarg);
+ break;
+
+ case 'L':
+ autolabel = optarg;
+ break;
+
+ default:
+ usage();
+ }
+
+ if (modfind("vn") < 0)
+ if (kldload("vn") < 0 || modfind("vn") < 0)
+ err(1, "cannot find or load \"vn\" kernel module");
+
+ if (flags == 0)
+ flags = VN_CONFIG;
+ if (all)
+ readconfig(flags);
+ else {
+ if (argc < optind + 1)
+ usage();
+ vndisks[0].dev = argv[optind++];
+ vndisks[0].file = argv[optind++];
+ vndisks[0].flags = flags;
+ vndisks[0].size = size;
+ vndisks[0].autolabel = autolabel;
+ if (optind < argc)
+ getoptions(&vndisks[0], argv[optind]);
+ nvndisks = 1;
+ }
+ rv = 0;
+ for (i = 0; i < nvndisks; i++)
+ rv += config(&vndisks[i]);
+ exit(rv);
+}
+
+int
+what_opt(str,p)
+ char *str;
+ u_long *p;
+{
+ if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
+ if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
+ if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
+ if (!strcmp(str,"io")) { *p |= VN_IO; return 0; }
+ if (!strcmp(str,"all")) { *p |= ~0; return 0; }
+ if (!strcmp(str,"none")) { *p |= 0; return 0; }
+ return 1;
+}
+
+int
+config(vnp)
+ struct vndisk *vnp;
+{
+ char *dev, *file, *oarg;
+ int flags;
+ struct vn_ioctl vnio;
+ register int rv;
+ char *rdev;
+ FILE *f;
+ u_long l;
+
+ dev = vnp->dev;
+ file = vnp->file;
+ flags = vnp->flags;
+ oarg = vnp->oarg;
+
+ if (flags & VN_IGNORE)
+ return(0);
+
+ rdev = rawdevice(dev);
+ f = fopen(rdev, "rw");
+ if (f == NULL) {
+ warn("open");
+ return(1);
+ }
+ vnio.vn_file = file;
+ vnio.vn_size = vnp->size; /* non-zero only if swap backed */
+
+ /*
+ * Disable the device
+ */
+ if (flags & VN_DISABLE) {
+ if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
+ rv = unmount(oarg, 0);
+ if (rv) {
+ if (errno == EBUSY)
+ flags &= ~VN_UNCONFIG;
+ if ((flags & VN_UNCONFIG) == 0)
+ warn("umount");
+ } else if (verbose)
+ printf("%s: unmounted\n", dev);
+ }
+ }
+ /*
+ * Clear (un-configure) the device
+ */
+ if (flags & VN_UNCONFIG) {
+ rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
+ if (rv) {
+ if (errno == ENODEV) {
+ if (verbose)
+ printf("%s: not configured\n", dev);
+ rv = 0;
+ } else
+ warn("VNIOCDETACH");
+ } else if (verbose)
+ printf("%s: cleared\n", dev);
+ }
+ /*
+ * Configure the device
+ */
+ if (flags & VN_CONFIG) {
+ rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
+ if (rv) {
+ warn("VNIOCATTACH");
+ flags &= ~VN_ENABLE;
+ } else {
+ if (verbose) {
+ printf(
+ "%s: %d bytes on %s\n",
+ dev, vnio.vn_size, file
+ );
+ }
+ /*
+ * autolabel
+ */
+ if (vnp->autolabel) {
+ do_autolabel(vnp->dev, vnp->autolabel);
+ }
+ }
+ }
+ /*
+ * Set an option
+ */
+ if (flags & VN_SET) {
+ l = setopt;
+ if (global)
+ rv = ioctl(fileno(f), VNIOCGSET, &l);
+ else
+ rv = ioctl(fileno(f), VNIOCUSET, &l);
+ if (rv) {
+ warn("VNIO[GU]SET");
+ } else if (verbose)
+ printf("%s: flags now=%08x\n",dev,l);
+ }
+ /*
+ * Reset an option
+ */
+ if (flags & VN_RESET) {
+ l = resetopt;
+ if (global)
+ rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
+ else
+ rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
+ if (rv) {
+ warn("VNIO[GU]CLEAR");
+ } else if (verbose)
+ printf("%s: flags now=%08x\n",dev,l);
+ }
+
+ /*
+ * Enable special functions on the device
+ */
+ if (flags & VN_ENABLE) {
+ if (flags & VN_SWAP) {
+ rv = swapon(dev);
+ if (rv)
+ warn("swapon");
+ else if (verbose)
+ printf("%s: swapping enabled\n", dev);
+ }
+ if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
+ struct ufs_args args;
+ int mflags;
+
+ args.fspec = dev;
+ mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
+ rv = mount("ufs", oarg, mflags, &args);
+ if (rv)
+ warn("mount");
+ else if (verbose)
+ printf("%s: mounted on %s\n", dev, oarg);
+ }
+ }
+/* done: */
+ fclose(f);
+ fflush(stdout);
+ return(rv < 0);
+}
+
+#define EOL(c) ((c) == '\0' || (c) == '\n')
+#define WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+void
+readconfig(flags)
+ int flags;
+{
+ char buf[LINESIZE];
+ FILE *f;
+ register char *cp, *sp;
+ register int ix;
+
+ f = fopen(configfile, "r");
+ if (f == NULL)
+ err(1, "%s", configfile);
+ ix = 0;
+ while (fgets(buf, LINESIZE, f) != NULL) {
+ cp = buf;
+ if (*cp == '#')
+ continue;
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ *cp++ = '\0';
+ vndisks[ix].dev = malloc(cp - sp);
+ strcpy(vndisks[ix].dev, sp);
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ *cp++ = '\0';
+
+ if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) {
+ vndisks[ix].size = getsize(sp + 1);
+ } else {
+ vndisks[ix].file = malloc(cp - sp);
+ strcpy(vndisks[ix].file, sp);
+ }
+
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ vndisks[ix].flags = flags;
+ if (!EOL(*cp)) {
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ *cp++ = '\0';
+ getoptions(&vndisks[ix], sp);
+ }
+ nvndisks++;
+ ix++;
+ }
+}
+
+void
+getoptions(vnp, fstr)
+ struct vndisk *vnp;
+ char *fstr;
+{
+ int flags = 0;
+ char *oarg = NULL;
+
+ if (strcmp(fstr, "swap") == 0)
+ flags |= VN_SWAP;
+ else if (strncmp(fstr, "mount=", 6) == 0) {
+ flags |= VN_MOUNTRW;
+ oarg = &fstr[6];
+ } else if (strncmp(fstr, "mountrw=", 8) == 0) {
+ flags |= VN_MOUNTRW;
+ oarg = &fstr[8];
+ } else if (strncmp(fstr, "mountro=", 8) == 0) {
+ flags |= VN_MOUNTRO;
+ oarg = &fstr[8];
+ } else if (strcmp(fstr, "ignore") == 0)
+ flags |= VN_IGNORE;
+ vnp->flags |= flags;
+ if (oarg) {
+ vnp->oarg = malloc(strlen(oarg) + 1);
+ strcpy(vnp->oarg, oarg);
+ } else
+ vnp->oarg = NULL;
+}
+
+char *
+rawdevice(dev)
+ char *dev;
+{
+ register char *rawbuf, *dp, *ep;
+ struct stat sb;
+ int len;
+
+ len = strlen(dev);
+ rawbuf = malloc(len + 2);
+ strcpy(rawbuf, dev);
+ if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
+ dp = rindex(rawbuf, '/');
+ if (dp) {
+ for (ep = &rawbuf[len]; ep > dp; --ep)
+ *(ep+1) = *ep;
+ *++ep = 'r';
+ }
+ }
+ return (rawbuf);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+"usage: vnconfig [-acdefguv] [-s option] [-r option] [special-device file]\n");
+ exit(1);
+}
+
+static int
+getsize(const char *arg)
+{
+ char *ptr;
+ int pgsize = getpagesize();
+ quad_t size = strtoq(arg, &ptr, 0);
+
+ switch(tolower(*ptr)) {
+ case 't':
+ /*
+ * GULP! Terrabytes. It's actually possible to create
+ * a 7.9 TB VN device, though newfs can't handle any single
+ * filesystem larger then 1 TB.
+ */
+ size *= 1024;
+ /* fall through */
+ case 'g':
+ size *= 1024;
+ /* fall through */
+ default:
+ case 'm':
+ size *= 1024;
+ /* fall through */
+ case 'k':
+ size *= 1024;
+ /* fall through */
+ case 'c':
+ break;
+ }
+ size = (size + pgsize - 1) / pgsize;
+ return((int)size);
+}
+
+/*
+ * DO_AUTOLABEL
+ *
+ * Automatically label the device. This will wipe any preexisting
+ * label.
+ */
+
+static void
+do_autolabel(const char *dev, const char *label)
+{
+ /* XXX not yet implemented */
+ fprintf(stderr, "autolabel not yet implemented, sorry\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/watch/Makefile b/usr.sbin/watch/Makefile
new file mode 100644
index 0000000..12e44fc
--- /dev/null
+++ b/usr.sbin/watch/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= watch
+MAN8= watch.8
+BINMODE=500
+LDADD+= -ltermcap
+DPADD+= ${LIBTERMCAP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/watch/watch.8 b/usr.sbin/watch/watch.8
new file mode 100644
index 0000000..8ae0527
--- /dev/null
+++ b/usr.sbin/watch/watch.8
@@ -0,0 +1,86 @@
+.\"
+.\" @(#)watch.8 1.1 (FreeBSD) 2/17/95
+.\"
+.Dd February 17, 1995
+.Dt WATCH 8
+.Os
+.Sh NAME
+.Nm watch
+.Nd snoop on another tty line
+.Sh SYNOPSIS
+.Nm watch
+.Op Fl ciotnW
+.Ar tty
+.\" watch [-ciotnW] [<tty name>]
+.Sh DESCRIPTION
+.Nm Watch
+allows the superuser to examine all data coming through a specified tty.
+.Nm Watch
+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 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 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 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, watch will exit. The reconnect flags are unaffected by
+this option.
+.It Fl W
+Allow write access to observed tty.
+.It Ar tty
+Tty may be specified as an tty-style device, such as a pseudo tty device,
+a virtual console, or a serial line, etc.
+Names may be preceded by "/dev/".
+.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 Ns .
+.It Sy "<control-W>"
+Clear screen.
+.It Sy "<control-X>"
+Change attached tty.
+.Sh RESTRICTIONS
+Only the superuser can run
+.Nm Ns .
+.Sh SEE ALSO
+.Xr pty 4 ,
+.Xr sio 4 ,
+.Xr snp 4
+.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
+.Nm Watch
+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..9277efb
--- /dev/null
+++ b/usr.sbin/watch/watch.c
@@ -0,0 +1,430 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/snoop.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termcap.h>
+#include <termios.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 */
+
+
+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;
+
+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], buf[1024];
+
+
+void
+clear()
+{
+ if (clear_ok)
+ tputs(buf, 1, putchar);
+ fflush(stdout);
+}
+
+void
+timestamp(buf)
+ 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);
+}
+
+void
+set_tty()
+{
+ 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);
+}
+
+void
+unset_tty()
+{
+ tcsetattr (std_in, TCSANOW, &otty);
+}
+
+
+void
+fatal(err, buf)
+ unsigned int err;
+ char *buf;
+{
+ unset_tty();
+ if (buf)
+ errx(err, "fatal: %s", buf);
+ else
+ exit(err);
+}
+
+int
+open_snp()
+{
+ char snp[] = {"/dev/snpX"};
+ char c;
+ int f, mode;
+
+ if (opt_write)
+ mode = O_RDWR;
+ else
+ mode = O_RDONLY;
+
+ for (c = '0'; c <= '9'; c++) {
+ snp[8] = c;
+ if ((f = open(snp, mode)) < 0)
+ continue;
+ return f;
+ }
+ fatal(EX_OSFILE, "cannot open snoop device");
+ return (0);
+}
+
+
+void
+cleanup()
+{
+ if (opt_timestamp)
+ timestamp("Logging Exited.");
+ close(snp_io);
+ unset_tty();
+ exit(EX_OK);
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
+ exit(EX_USAGE);
+}
+
+void
+setup_scr()
+{
+ char *cbuf = buf, *term;
+ if (!opt_interactive)
+ return;
+ if ((term = getenv("TERM")))
+ if (tgetent(tbuf, term) == 1)
+ if (tgetstr("cl", &cbuf))
+ clear_ok = 1;
+ set_tty();
+ clear();
+}
+
+
+int
+ctoh(c)
+ char c;
+{
+ if (c >= '0' && c <= '9')
+ return (int) (c - '0');
+
+ if (c >= 'a' && c <= 'f')
+ return (int) (c - 'a' + 10);
+
+ fatal(EX_DATAERR, "bad tty number");
+ return (0);
+}
+
+
+void
+detach_snp()
+{
+ dev_t dev;
+
+ dev = -1;
+ ioctl(snp_io, SNPSTTY, &dev);
+}
+
+void
+attach_snp()
+{
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal(EX_UNAVAILABLE, "cannot attach to tty");
+ if (opt_timestamp)
+ timestamp("Logging Started.");
+}
+
+
+void
+set_dev(name)
+ char *name;
+{
+ char buf[DEV_NAME_LEN];
+ struct stat sb;
+
+ if (strlen(name) > 5 && !strncmp(name, "/dev/", 5)) {
+ snprintf(buf, sizeof buf, "%s", name);
+ }
+ else {
+ if (strlen(name) == 2)
+ sprintf(buf, "/dev/tty%s", name);
+ else
+ sprintf(buf, "/dev/%s", 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(dev_name, msg)
+ char *dev_name, *msg;
+{
+ char buf[DEV_NAME_LEN];
+ int len;
+
+ clear();
+ unset_tty();
+
+ if (msg)
+ printf("%s\n", msg);
+ if (dev_name)
+ printf("Enter device name [%s]:", dev_name);
+ 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(dev_name, buf);
+ }
+ set_tty();
+}
+
+#define READB_LEN 5
+
+void
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int res, nread, b_size = MIN_SIZE;
+ extern int optind;
+ char ch, *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, "Wciotn")) != -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 '?':
+ default:
+ usage();
+ }
+
+ 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;
+ if (read(std_in,chb,nread)!=nread)
+ fatal(EX_IOERR, "read (stdin) failed");
+
+ switch (chb[0]) {
+ case CHR_CLEAR:
+ clear();
+ break;
+ case CHR_SWITCH:
+ if (opt_no_switch)
+ break;
+ detach_snp();
+ ask_dev(dev_name, MSG_CHANGE);
+ set_dev(dev_name);
+ break;
+ default:
+ if (opt_write) {
+ if (write(snp_io,chb,nread) != 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();
+ 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();
+ 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");
+ }
+ if (read(snp_io, buf, nread) < nread)
+ fatal(EX_IOERR, "read failed");
+ if (write(std_out, buf, nread) < nread)
+ fatal(EX_IOERR, "write failed");
+ }
+ } /* While */
+}
+
diff --git a/usr.sbin/wlconfig/Makefile b/usr.sbin/wlconfig/Makefile
index 21fd7cc..8a50cc8 100644
--- a/usr.sbin/wlconfig/Makefile
+++ b/usr.sbin/wlconfig/Makefile
@@ -1,7 +1,7 @@
-# $Id$
+# $Id: Makefile,v 1.1.1.1 1997/05/22 08:58:18 msmith Exp $
PROG= wlconfig
SRCS= wlconfig.c
-CFLAGS+= Wall
+CFLAGS+= -Wall
MAN8= wlconfig.8
.include <bsd.prog.mk>
diff --git a/usr.sbin/wlconfig/wlconfig.8 b/usr.sbin/wlconfig/wlconfig.8
index 6267c18..76c595e 100644
--- a/usr.sbin/wlconfig/wlconfig.8
+++ b/usr.sbin/wlconfig/wlconfig.8
@@ -10,13 +10,14 @@
.Op Ar param value ...
.Sh DESCRIPTION
The
-.Nm wlconfig
+.Nm
command can be used to read and set parameters for the NCR/AT&T Wavelan
radio LAN card. Various parameters stored in the nonvolatile Parameter
Storage Area (PSA) on the card can be modified with this program, which
obviates the need for the DOS-based
.Nm instconf.exe
-program.
+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
@@ -29,7 +30,7 @@ The
.Ar param
and
.Ar value
-arguments can be used to change the value of several of the parameters.
+arguments can be used to change the value of several parameters.
Any number of
.Ar param value
pairs may be supplied.
@@ -37,9 +38,9 @@ pairs may be supplied.
.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
+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)
+Local MAC value (ethernet address).
.It macsel
.Sq soft
(as set by the
@@ -56,14 +57,33 @@ In the hardware, NWIDs are stored long-term in non-volative 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 curnwid
+.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 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 wlconfig
+.Nm
program should then be used to reconfigure the card to a sensible
value.
.Sh EXAMPLES
@@ -96,8 +116,18 @@ Databus width : 16 (variable)
Configuration state : unconfigured
CRC-16 : 0x3c26
CRC status : OK
+.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 wlconfig
-command is completely new, written for Hilink Internet by Michael Smith.
+.Nm
+command 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
index 10771c9..9891048 100644
--- a/usr.sbin/wlconfig/wlconfig.c
+++ b/usr.sbin/wlconfig/wlconfig.c
@@ -22,10 +22,13 @@
* LIABILITY, OR TORT (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$
- *
*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: wlconfig.c,v 1.6 1997/10/27 12:23:08 charnier Exp $";
+#endif /* not lint */
+
/*
* wlconfig.c
*
@@ -60,6 +63,7 @@
#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>
@@ -80,6 +84,10 @@ 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)
{
@@ -196,23 +204,86 @@ print_psa(u_char *psa, int currnwid)
}
-void
-syntax(char *pname)
+static void
+usage()
{
- fprintf(stderr,"Usage: %s <ifname> [<param> <value> ...]\n",pname);
- fprintf(stderr," <ifname> Wavelan interface name.\n");
- fprintf(stderr," <param> Parameter name (see below)\n");
- fprintf(stderr," <value> New value for parameter.\n");
- fprintf(stderr," Parameter name: Value:\n");
- fprintf(stderr," irq 3,4,5,6,10,11,12,15\n");
- fprintf(stderr," mac soft ethernet address\n");
- fprintf(stderr," macsel soft or default\n");
- fprintf(stderr," nwid default NWID (0x0-0xffff)\n");
- fprintf(stderr," currnwid current NWID (0x0-0xffff) or 'get'\n");
+ 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;
@@ -225,7 +296,7 @@ main(int argc, char *argv[])
int currnwid;
if ((argc < 2) || (argc % 2))
- syntax(argv[0]);
+ usage();
/* get a socket */
sd = socket(AF_INET, SOCK_DGRAM, 0);
@@ -237,11 +308,11 @@ main(int argc, char *argv[])
/* get the PSA */
ifr.ifr_data = (caddr_t)psabuf;
if (ioctl(sd, SIOCGWLPSA, (caddr_t)&ifr))
- err(1,"Get PSA");
+ err(1,"get PSA");
/* get the current NWID */
if (ioctl(sd, SIOCGWLCNWID, (caddr_t)&ifr))
- err(1,"Get NWID");
+ err(1,"get NWID");
currnwid = (int)ifr.ifr_data;
/* just dump and exit? */
@@ -261,11 +332,11 @@ main(int argc, char *argv[])
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);
+ 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?)");
+ err(1,"set NWID (interface not up?)");
continue ;
}
@@ -273,7 +344,7 @@ main(int argc, char *argv[])
val = strtol(value,&cp,0);
val = irqvals[val];
if ((val == 0) || (cp == value))
- errx(1,"Bad IRQ '%s'",value);
+ errx(1,"bad IRQ '%s'",value);
psabuf[WLPSA_IRQNO] = (u_char)val;
work = 1;
continue;
@@ -281,7 +352,7 @@ main(int argc, char *argv[])
if (!strcasecmp(param,"mac")) {
if ((ea = ether_aton(value)) == NULL)
- errx(1,"Bad ethernet address '%s'",value);
+ errx(1,"bad ethernet address '%s'",value);
for (i = 0; i < 6; i++)
psabuf[WLPSA_LOCALMAC + i] = ea->octet[i];
work = 1;
@@ -299,27 +370,49 @@ main(int argc, char *argv[])
work = 1;
continue;
}
- errx(1,"Bad macsel value '%s'",value);
+ 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);
+ errx(1,"bad NWID '%s'",value);
psabuf[WLPSA_NWID] = (val >> 8) & 0xff;
psabuf[WLPSA_NWID+1] = val & 0xff;
work = 1;
continue;
}
- errx(1,"Unknown parameter '%s'",param);
+ 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");
+ err(1,"set PSA");
}
+ return(0);
}
-
-
-
-
diff --git a/usr.sbin/wormcontrol/Makefile b/usr.sbin/wormcontrol/Makefile
new file mode 100644
index 0000000..c94d349
--- /dev/null
+++ b/usr.sbin/wormcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG= wormcontrol
+MAN8= wormcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wormcontrol/wormcontrol.8 b/usr.sbin/wormcontrol/wormcontrol.8
new file mode 100644
index 0000000..6e3a6aa
--- /dev/null
+++ b/usr.sbin/wormcontrol/wormcontrol.8
@@ -0,0 +1,180 @@
+.\"
+.\" Copyright (C) 1996
+.\" interface business GmbH
+.\" Tolkewitzer Strasse 49
+.\" D-01277 Dresden
+.\" F.R. Germany
+.\"
+.\" All rights reserved.
+.\"
+.\" Written by Joerg Wunsch <joerg_wunsch@interface-business.de>
+.\"
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list 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.
+.\"
+.\" $Id: wormcontrol.8,v 1.11 1998/03/23 08:30:03 charnier Exp $
+.\"
+.\" arrgh, hilit19 needs this" :-(
+.Dd January 27, 1996
+.Os
+.Dt WORMCONTROL 8
+.Sh NAME
+.Nm wormcontrol
+.Nd control the CD-R driver
+.Sh SYNOPSIS
+.Nm wormcontrol
+.Op Fl f Ar device
+.Ar command
+.Op Ar params...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to control the behaviour of the
+.Xr worm 4
+driver in order to adjust various parameters of a recordable CD
+.Pq CD-R .
+.Pp
+Unlike many other devices, CD-R's require a very strict handling order.
+Prior to writing data, the speed of the drive must be selected, and
+the drive can also be turned into a
+.Ql dummy
+mode, where every action is only performed with the laser turned off.
+This way, it's possible to test whether the environment provides a
+sufficiently high data flow rate in order to actually burn the CD-R,
+without destroying the medium in case of a catastrophic failure.
+.Pp
+In order to write a new track, the drive must be told whether the new
+track will become an audio or a data track. Audio tracks are written
+with a block size of 2352 bytes, while data tracks have 2048 bytes per
+block. For audio tracks, the driver does further need to know
+whether the data are recorded with a preemphasis.
+.Pp
+Once all tracks of a session have been written, the disk must be
+.Em fixated .
+This writes the table of contents and leadout information to the disk.
+The disk won't be usable without doing this.
+.Pp
+The following options are available:
+.Bl -tag -width ident
+.It Fl f Ar device
+Use
+.Ar device
+instead of the default
+.Pa /dev/rworm0 .
+.It prepdisk Ar single \&| double Op Ar dummy
+Prepare the disk for recording. This must be done before any tracks
+can be prepared, and remains in effect until the session has been
+fixated. Either single speed
+.Pq for audio data
+or double speed
+.Pq for CD-ROM data
+must be selected, and optionally, the argument
+.Ar dummy
+can be used to tell the drive to keep the laser turned off, for testing.
+.It track Ar audio \&| data Op Ar preemp
+Inform the driver about the format of the next track. Either
+.Ar audio
+or
+.Ar data
+.Pq CD-ROM
+must be selected, with an optional argument
+.Ar preemp
+that must be specified for an audio track where data records with
+preemphasis are being used. Once this command has been successfully
+specified, the track is ready for being written.
+.It fixate Ar toc-type Op Ar onp
+Once all tracks have been written, this closes the current session.
+The argument
+.Ar toc-type
+is a single digit between 0 and 4, with the following meaning:
+.Bl -item
+.It
+0 CD audio
+.It
+1 CD-ROM
+.It
+2 CD-ROM with first track in mode 1
+.It
+3 CD-ROM with first track in mode 2
+.It
+4 CDI
+.El
+.Pp
+The optional argument
+.Ar onp
+stands for
+.Dq open next program area ,
+which means that the next session on the CD-R will be opened and can
+be recorded in the future. Otherwise, the CD-R will be closed and
+remains unchangeable.
+.It blank
+Blank a CD-RW disk.
+.It nextwriteable
+Sets the next writeable location on the drive, used when writing
+more than one track ie CD-DA (audio) disks.
+.El
+.Sh DIAGNOSTICS
+Error codes for the underlying
+.Xr ioctl 2
+commands are printed by the
+.Xr err 3
+facility.
+.Sh EXAMPLES
+The typical sequence of burning a data CD-R looks like:
+.Bd -literal
+# wormcontrol prepdisk double
+# wormcontrol track data
+# rtprio 5 team -v 1m 5 < cdrom.image | rtprio 5 dd of=/dev/rworm0 obs=20k
+# wormcontrol fixate 1
+.Ed
+.Pp
+Note that the
+.Xr dd 1
+command above is mainly used in order to
+.Dq slice
+the data stream. It's particularly useful when recording audio data
+with their rather unusual blocksize. Since the underlying device is a
+.Em raw
+device, the blocksize used in that command must be an integral multiple
+of the CD-R blocksize.
+.Pp
+The mentioned command
+.Xr team 1
+is not part of the base system, but comes extremely handy in order to
+pipe a constant data stream into the CD recorder.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr team 1 ,
+.Xr ioctl 2 ,
+.Xr err 3 ,
+.Xr worm 4
+.Pp
+.Pa /usr/share/examples/worm/*
+.Sh HISTORY
+.Nm Wormcontrol
+is currently under development.
+.Sh AUTHORS
+The program has been contributed by
+.ie t J\(:org Wunsch,
+.el Joerg Wunsch,
+Dresden.
diff --git a/usr.sbin/wormcontrol/wormcontrol.c b/usr.sbin/wormcontrol/wormcontrol.c
new file mode 100644
index 0000000..5f43cbd
--- /dev/null
+++ b/usr.sbin/wormcontrol/wormcontrol.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 1996
+ * interface business GmbH
+ * Tolkewitzer Strasse 49
+ * D-01277 Dresden
+ * F.R. Germany
+ *
+ * All rights reserved.
+ *
+ * Written by Joerg Wunsch <joerg_wunsch@interface-business.de>
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list 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[] =
+ "$Id: wormcontrol.c,v 1.7 1997/10/27 12:25:38 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/wormio.h>
+
+static void
+usage()
+{
+ fprintf(stderr,"usage: wormcontrol [-f device] command [args]\n");
+ exit(EX_USAGE);
+}
+
+#define eq(a, b) (strcmp(a, b) == 0)
+
+int
+main(int argc, char **argv)
+{
+ int fd, c, i;
+ int errs = 0;
+ const char *devname = "/dev/rworm0";
+
+ while ((c = getopt(argc, argv, "f:")) != -1)
+ switch(c) {
+ case 'f':
+ devname = optarg;
+ break;
+
+ case '?':
+ default:
+ errs++;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (errs || argc < 1)
+ usage();
+
+ if ((fd = open(devname, O_RDONLY | O_NONBLOCK, 0)) == -1)
+ err(EX_NOINPUT, "open(%s)", devname);
+
+ if (eq(argv[0], "prepdisk")) {
+ struct wormio_prepare_disk d;
+ d.dummy = 0;
+ d.speed = -1;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "dummy"))
+ d.dummy = 1;
+ else if (eq(argv[i], "single"))
+ d.speed = 1;
+ else if (eq(argv[i], "double"))
+ d.speed = 2;
+ else
+ errx(EX_USAGE,
+ "wrong param for \"prepdisk\": %s",
+ argv[i]);
+ }
+ if (d.speed == -1)
+ errx(EX_USAGE, "missing speed parameter");
+ if (ioctl(fd, WORMIOCPREPDISK, &d) == -1)
+ err(EX_IOERR, "ioctl(WORMIOCPREPDISK)");
+ }
+ else if (eq(argv[0], "track")) {
+ struct wormio_prepare_track t;
+ bzero(&t, sizeof (t));
+ t.audio = -1;
+ t.preemp = 0;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "audio"))
+ t.audio = 1;
+ else if (eq(argv[i], "data")) {
+ t.audio = 0;
+ t.track_type = BLOCK_MODE_1;
+ } else if (eq(argv[i], "preemp"))
+ t.preemp = 1;
+ else
+ errx(EX_USAGE,
+ "wrong param for \"track\": %s",
+ argv[i]);
+ }
+ if (t.audio == -1)
+ errx(EX_USAGE, "missing track type parameter");
+ if (t.audio == 0 && t.preemp == 1)
+ errx(EX_USAGE, "\"preemp\" attempted on data track");
+ if (ioctl(fd, WORMIOCPREPTRACK, &t) == -1)
+ err(EX_IOERR, "ioctl(WORMIOCPREPTRACK)");
+ }
+ else if (eq(argv[0], "fixate")) {
+ struct wormio_fixation f;
+ f.toc_type = -1;
+ f.onp = 0;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "onp"))
+ f.onp = 1;
+ else if (argv[i][0] >= '0' && argv[i][0] <= '4' &&
+ argv[i][1] == '\0')
+ f.toc_type = argv[i][0] - '0';
+ else
+ errx(EX_USAGE,
+ "wrong param for \"fixate\": %s",
+ argv[i]);
+ }
+ if (f.toc_type == -1)
+ errx(EX_USAGE, "missing TOC type parameter");
+ if (ioctl(fd, WORMIOCFIXATION, &f) == -1)
+ err(EX_IOERR, "ioctl(WORMIOFIXATION)");
+ }
+ else if (eq(argv[0], "blank")) {
+#define CDRIOCBLANK _IO('c',100) /* Blank a CDRW disc */
+ if (ioctl(fd, CDRIOCBLANK) == -1)
+ err(EX_IOERR, "ioctl(CDRIOCBLANK)");
+ }
+ else if (eq(argv[0], "nextwriteable")) {
+ int addr;
+#define CDRIOCNEXTWRITEABLEADDR _IOR('c',101,int)
+ if (ioctl(fd, CDRIOCNEXTWRITEABLEADDR, &addr) == -1)
+ err(EX_IOERR, "ioctl(CDRIOCNEXTWRITEABLEADDR)");
+ printf("%d\n", addr);
+ }
+ else
+ errx(EX_USAGE, "unknown command: %s", argv[0]);
+
+ return EX_OK;
+}
diff --git a/usr.sbin/xntpd/COPYRIGHT b/usr.sbin/xntpd/COPYRIGHT
new file mode 100644
index 0000000..b5d6282
--- /dev/null
+++ b/usr.sbin/xntpd/COPYRIGHT
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1992, 1993, 1994 *
+ * *
+ * 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 appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * For all files included in this distribution and not specifically marked
+ * otherwise, the above copyright information applies.
+ *
+ * Authors
+ *
+ * Dennis Ferguson <dennis@mrbill.canet.ca> (foundation code for NTP
+ * Version 2 as specified in RFC-1119)
+ * Lars H. Mathiesen <thorinn@diku.dk> (adaptation of foundation code for
+ * Version 3 as specified in RFC-1305)
+ * Louis A. Mamakos <louie@ni.umd.edu> (support for md5-based
+ * authentication)
+ * Craig Leres <leres@ee.lbl.gov> (port to 4.4BSD operating system,
+ * ppsclock, Maganavox GPS clock driver)
+ * Nick Sayer <mrapple@quack.kfu.com> (SunOS streams modules)
+ * Frank Kardel <Frank.Kardel@informatik.uni-erlangen.de>(PARSE (GENERIC)
+ * driver, STREAMS module for PARSE, support scripts, reference clock
+ * configuration scripts, Makefile cleanup)
+ * Rainer Pruy <Rainer.Pruy@informatik.uni-erlangen.de> (monitoring/trap
+ * scripts, statistics file handling)
+ * Glenn Hollinger <glenn@herald.usask.ca> (GOES clock driver)
+ * Kenneth Stone <ken@sdd.hp.com> (port to HPUX operating system)
+ * Dave Katz <dkatz@cisco.com> (port to RS/6000 AIX operating system)
+ * William L. Jones <jones@hermes.chpc.utexas.edu> (RS/6000 AIX
+ * modifications, HPUX modifications)
+ * John A. Dundas III <dundas@salt.jpl.nasa.gov> (Apple A/UX port)
+ * David L. Mills <mills@udel.edu> (Spectractom WWVB, Austron GPS,
+ * Heath, ATOM, ACTS, KSI/Odetics IRIG-B clock drivers; pps support)
+ * Jeffrey Mogul <mogul@pa.dec.com> (ntptrace utility)
+ * Steve Clift (clift@ml.csiro.au) OMEGA clock driver)
+ * Mike Iglesias (iglesias@uci.edu) (DEC Alpha changes)
+ * Mark Andrews <marka@syd.dms.csiro.au> (Leitch atomic clock controller)
+ * George Lindholm <lindholm@ucs.ubc.ca> (port to SunOS 5.1 operating system)
+ * Jeff Johnson <jbj@chatham.usdesign.com> (massive prototyping overhaul)
+ * Tom Moore <tmoore@fievel.daytonoh.ncr.com> (port to i386 svr4)
+ * Piete Brooks <Piete.Brooks@cl.cam.ac.uk> (MSF clock driver, Trimble PARSE
+ * support)
+ * Karl Berry <karl@owl.HQ.ileaf.com> (syslog to file option)
+ * Torsten Duwe <duwe@immd4.informatik.uni-erlangen.de> (Linux Port)
+ * Paul A Vixie <vixie@vix.com> (TrueTime GPS driver)
+ * Jim Jagielski <jim@jagubox.gsfc.nasa.gov> (A/UX port)
+ * Ray Schnitzler <schnitz@unipress.com> (First pass at a Unixware1 port.)
+ * Ajit Thyagarajan <ajit@ee.udel.edu> (IP multicast support)
+ * Jeff Steinman <jss@pebbles.jpl.nasa.gov> (Datum PTS clock driver)
+ * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> (TRAK clock driver)
+ */
diff --git a/usr.sbin/xntpd/Makefile b/usr.sbin/xntpd/Makefile
new file mode 100644
index 0000000..409f99c
--- /dev/null
+++ b/usr.sbin/xntpd/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for xntpd.
+# $Id: Makefile,v 1.6 1997/02/22 16:14:13 peter Exp $
+#
+SUBDIR= lib parse xntpd xntpdc ntpq ntpdate ntptrace authstuff
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/xntpd/Makefile.inc b/usr.sbin/xntpd/Makefile.inc
new file mode 100644
index 0000000..cecc8ec
--- /dev/null
+++ b/usr.sbin/xntpd/Makefile.inc
@@ -0,0 +1,21 @@
+# $Id$
+
+DEFS_LOCAL=-DREFCLOCK -DPARSE -DUDP_WILDCARD_DELIVERY
+NTPDEFS= -DSYS_FREEBSD -DSYS_44BSD
+AUTHDEFS= -DMD5
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DAS2201 -DGOES -DGPSTM -DOMEGA \
+ -DLEITCH -DTRAK -DACTS -DATOM -DDATUM -DHEATH -DMSFEES \
+ -DMX4200 -DNMEA -DBOEDER
+CFLAGS+= ${NTPDEFS} ${DEFS_LOCAL} ${AUTHDEFS} ${CLOCKDEFS} ${COPTS}
+
+.if exists(${.OBJDIR}/../lib)
+LIBNTP= ${.OBJDIR}/../lib/libntp.a
+.else
+LIBNTP= ${.CURDIR}/../lib/libntp.a
+.endif
+
+.if !defined(NOCRYPT) && exists(${.CURDIR}/../../../secure/usr.sbin/xntpd/lib)
+AUTHDEFS+= -DDES
+.endif
+
+.include "../Makefile.inc"
diff --git a/usr.sbin/xntpd/PORTING b/usr.sbin/xntpd/PORTING
new file mode 100644
index 0000000..7f23642
--- /dev/null
+++ b/usr.sbin/xntpd/PORTING
@@ -0,0 +1,37 @@
+These are the rules so that older bsd systems and the POSIX standard
+system can coexist togather.
+
+ 1) If you use select then include "ntp_select.h"
+ select is not standard, since it is very system depenedent as to where
+ select is defined. The logic to include the right system dependent
+ include file is in "ntp_select.h".
+ 2) Always use POSIX defintion of strings. Inlcude "ntp_string.h" instaed
+ of <string.h>.
+ 3) Always include "ntp_malloc.h" if you use malloc.
+ 4) Always include "ntp_io.h" instead of <sys/file.h> or <fnctl.h> to
+ get O_* flags.
+ 5) Always include "ntp_if.h" instead of <net/if.h>.
+ 6) Always include "ntp_stdlib.h" instead of <stdlib.h>.
+ 7) Always define a system identifier for any new system added to the
+ machines directory. The identifier should always start with SYS_!
+ 8) Define any special defines needed for a system in
+ ./include/ntp_machine.h based on system identifier. This file is
+ included by the "ntp_types.h" file and should always be placed
+ first after the <> defines.
+ 9) Define any special library prototypes left over from the system
+ library and include files in the "l_stdlib.h" file. This file is
+ included by the "ntp_stdlib.h" file and should ordinarily be
+ placed last in the includes list.
+ 10) Don't define a include file by the same name as a system include file.
+
+
+"l_stdlib.h" can contain any extra definitions that are needed so that
+gcc will shut up. They should be controlled by a system identifier and
+there should be a seperate section for each system. Really this will
+make it easier to maintain.
+
+See include/ntp_machines.h for the verious compile time options.
+
+Good luck.
+
+Bill Jones, with amendments by Dave Mills
diff --git a/usr.sbin/xntpd/README b/usr.sbin/xntpd/README
new file mode 100644
index 0000000..43780d6
--- /dev/null
+++ b/usr.sbin/xntpd/README
@@ -0,0 +1,156 @@
+The xntp3 Distribution
+
+This directory and its subdirectories contain the Network Time Protocol
+Version 3 (NTP) distribution for Unix systems. It contains source code
+for the daemon, together with related auxiliary programs, documentation
+and strange stuff. You are welcome to the lot, with due consideration of
+the COPYRIGHT files stashed in the distributions. You are also invited
+to contribute bugfixes and drivers for new and exotic radios, telephones
+and sundials. This distribution is normally available by anonymous ftp
+as the compressed tar archive xntp-<version>.tar.Z in the pub/ntp directory
+on louie.udel.edu.
+
+The base directory contains the distributions and related stuff. The files
+marked with a "*" are not distributed, but generated. Most of
+the subdirectories contain README files describing their contents. The
+base directory ./ includes:
+
+COPYRIGHT file specifying copyright conditions, together with a
+ list of major authors and electric addresses.
+
+Config * configuration file built by the configuration script
+ "make makeconfig" and used to build the makefiles in the
+ various subdirectories. Do not edit.
+
+Config.local * Unless you have a reference clock (besides the local
+ computer clock) or want to change the default installation
+ directory (/usr/local/bin) not action is needed. For
+ configuring a reference clock a "make refconf" should
+ suffice. Diehards can still use an editor on this file.
+
+Config.local.dist file used to generate a plausible Config.local by commands
+ such as "make Config.local.green".
+
+Config.sed * sed script used to build makefiles from the
+ configuration file. Do not edit.
+
+Makefile this is the root of the makefile tree. Do not edit.
+ (Contents under pressure - qualified personel only 8-)
+
+PORTING contains useful information for porting to unexplored
+ new systems.
+
+RELNOTES instructions for compiling and installing the daemon and
+ supporting programs.
+
+README this file.
+
+TODO our current problems where we could need help.
+
+adjtime directory containing the sources for the adjtime daemon
+ for HP/UX systems.
+
+authstuff directory containing sources for miscellaneous programs
+ to test, calibrate and certify the cryptographic
+ mechanisms for DES and MD5 based authentication. These
+ programs do not include the cryptographic routines
+ themselves, so are free of U.S. export restrictions.
+
+clockstuff directory containing sources for miscellaneous programs
+ to test certain auxilliary programs used with some
+ kernel configurations, together with a program to
+ calculate propagation delays for use with radio clocks
+ and national time dissemination services such as
+ WWV/WWVH, WWVB and CHU.
+
+compilers directory containing configuration scripts for various
+ compilers and operating systems.
+
+conf directory containing a motley collection of
+ configuration files for various systems. For example
+ only.
+
+doc directory containing miscellaneous man pages and memos
+ useful for installation and subnet management.
+
+gadget directory containing instructions and construction data
+ for a mysterious little box used as a CHU radio
+ demodulator and/or a level converter-pulse generator for
+ a precision 1-pps signal.
+
+include directory containing include header files used by most
+ programs in the distribution.
+
+hints directory containing files with hints on particular
+ topics like installation on specific OS variants or
+ general information.
+
+kernel directory containing sources for kernel programs such as
+ line disciplines and STREAMS modules used with the CHU
+ decoder and precision 1-pps signals.
+
+lib directory containing sources for the library programs
+ used by most programs in the distribution.
+
+machines directory containing configuration scripts for various
+ operating systems.
+
+ntpdate directory containing sources for a program to set the
+ local machine time from one or more remote machines
+ running NTP. Operates like rdate, but much more
+ accurate.
+
+ntpq directory containing sources for a utility program to
+ query local and remote NTP peers for state variables and
+ related timekeeping information. This program conforms
+ to Appendix A of the NTP Version 3 Specification RFC
+ 1305.
+
+ntptrace directory containing sources for a utility program that
+ can be used to reveal the chain of NTP peers from a
+ designated peer to the primary server at the root of the
+ timekeeping subnet.
+
+parse directory containing file belonging to the generic parse
+ reference clock driver. For reasonably simple clocks it
+ is possible to get away with about 3-4Kb of code.
+ additionally the SunOS 4.x streams module for parse is
+ residing here.
+
+parse/util some goodies for testing parse processing of DCF77 information.
+ (primarily for use on Suns, although others may work
+ also - possibly with a little porting.)
+ one little gem is dcfd.c - DCF77 decoder with ntp loopfilter
+ code for standalone DCF77 synchronisation without the full
+ works of NTP.
+ Dcfd.c has been ported to FreeBSD for the Boeder DCF77 receiver
+ by Vincenzo Capuano.
+
+ppsclock directory containing sources for modifications to the
+ kernel asynchronous serial driver plus a STREAMS module
+ to capture a precision 1-pps signal. Useful on SunOS
+ 4.1.X systems only.
+
+refclocks directory containing reference clock configuration support
+
+scripts directory containing scripts to build the configuration
+ file "config" in this directory and then the makefiles
+ used in various dependent directories.
+ the subdirectories monitoring and support hold various
+ perl and shell scripts for visualising synchronisation
+ and daemon startup.
+
+util directory containing sources for various utility and
+ testing programs.
+
+xntpd directory containing sources for the NTP Version 3 daemon.
+
+xntpdc directory containing sources for a utility program to
+ query local and remote NTP peers for state variables and
+ related timekeeping information. This program is
+ specific to this implmentation of NTP Version 3 and does
+ not conform to Appendix A of the NTP Version 3
+ Specification RFC 1305.
+
+xntpres directory containing sources for a name-resolution
+ program used in some configurations of NTP Version 3.
diff --git a/usr.sbin/xntpd/README.FreeBSD b/usr.sbin/xntpd/README.FreeBSD
new file mode 100644
index 0000000..523d84c
--- /dev/null
+++ b/usr.sbin/xntpd/README.FreeBSD
@@ -0,0 +1,15 @@
+ $Id$
+
+This version of NTP was converted to the BSD-style Makefile system by
+Garrett Wollman (wollman@FreeBSD.org); it is based on version
+3.4e (beta) from the University of Delaware.
+
+Besides the Makefile changes, the DES code has been completely removed
+in order to make this code exportable. If you have a legal copy of
+`authdes.c', you can just add it to the lib/ directory and add `-DDES'
+to the AUTHDEFS in Makefile.inc.
+
+You can change CLOCKDEFS in the same file to add other reference clocks.
+
+This port should work under either FreeBSD 1.1 or FreeBSD 2.0. For
+1.1, change the `-DSYS_44BSD' in Makefile.inc to `-DSYS_386BSD'.
diff --git a/usr.sbin/xntpd/RELNOTES b/usr.sbin/xntpd/RELNOTES
new file mode 100644
index 0000000..ab3aebe
--- /dev/null
+++ b/usr.sbin/xntpd/RELNOTES
@@ -0,0 +1,216 @@
+For special hints on setup/compilation/installation and other general
+topics you may persue the files in the hints directory.
+
+This file contains the usual instructions to compile and install the programs in
+this distribution. To make these programs:
+
+(0) Make sure that you have all necessary tools for building executables.
+ These tools include cc/gcc, make, awk, sed, tr, sh, grep, egrep and
+ a few others. Not all of these tools exist in the standard distribution
+ of todays UNIX versions (compilers are likely to be an extra product).
+ For a successful build all of these tools should be accessible via the
+ current path.
+
+(1) By default, if there is no Config.local, the system will generate one
+ to support a local ref clock (i.e. run off the system clock).
+ Greenhorns can skip on to (2).
+
+ HACKers can create a Config.local and choose the compilation options,
+ install destination directory and clock drivers.
+ A template for Config.local can be found in Config.local.dist.
+ There are two Configurations that can be auto-generated:
+ make Config.local.local # network configuration plus local
+ # reference clock (the default)
+ make Config.local.NO.clock # network only configuration
+
+ To set up for a radio clock, type "make refconf" and answer the questions
+ about PLL, PPS and radio clock type.
+ If this is the first use of the ref clock, don't forget to make suitable
+ files in /dev/.
+
+ For custom tailored configuration copying Config.local.dist to Config.local
+ and editing Config.local to suit the local needs is neccessary (at most
+ 3 lines to change), or use one of the make's above and then tweak it.
+ Config.local can also be used to override common settings from the
+ machines/* files like the AUTHDEFS= to select very specific configurations.
+ Please use this feature with care and don't be disappointed if it doesn't
+ work the way you expect.
+
+(2) Type "make" to compile everything of general interest. Expect few or
+ no warnings using cc and a moderate level of warnings using gcc.
+ Note: On some Unix platforms the use of gcc can result in quite a few
+ complaints about system header files and type problems within xntp
+ code. This is usually the case when the OS header files are not up
+ up to ANSI standards or GCCISMs. (There may, however, be still some
+ inconsistencies in the code)
+
+ Other known problems stem from bugs/features/... in utility programs
+ of some vendors.
+
+ See section "build problems" for known problems and possible work-
+ arounds.
+
+ Each time you change the configuration a script that pokes your hard- and
+ software will be run to build the actual configuration files.
+ If the script fails, it will give you a list of machines it knows about.
+ You can override the automatic choice by cd to the ../machines directory
+ and typing "make makeconfig OS=<machine>", where <machine> is one of the
+ file names in the ../machine directory.
+
+ The shell script will attempt to find the gcc compiler and, if
+ found, will use it instead of the cc compiler. You can override
+ this automatic choice by cd to the ../machines directory and typing
+ "make makeconfig COMP=<compiler>", where <compiler> is one of the file
+ names in the ../compilers directory. This can be combined with
+ the OS argument above.
+
+ The configuration step can be separatly invoked by "make makeconfig".
+
+ Note that any reconfiguration will result in cleaning the old
+ program and object files.
+
+(3) Assuming you have write permission on the install destination directory,
+ type "make install" to install the binaries in the destination directory.
+ At the time of writing this includes
+ the programs xntpd (the daemon), xntpdc (an xntpd-dependent query
+ program), ntpq (a standard query program), ntpdate (an rdate
+ replacement for boot time date setting and sloppy time keeping)
+ and xntpres (a program which provides name resolver support for
+ some xntpd configurations).
+
+(4) You are now ready to configure the daemon and start it. At this
+ point it might be useful to format and print the file doc/notes.me
+ and read a little bit. The sections on configuration and on the
+ tickadj program will be immediately useful.
+
+Additional "make" target you might find useful are:
+
+clean cleans out object files, programs and temporary files
+
+dist makes a new distribution file (also cleans current binaries)
+ All usual scratch and backup files (*.rej, *.orig, *.o, *~
+ core, lint*.errs, executables, tags, Makefile.bak, make.log)
+ will be removed. The distribution is created in a tar file
+ (file name: <prefix><version>.tar.<compression suffix> - with
+ the prefix usually being ../xntp- and a compression suffix
+ of .Z (compress))
+ Note: the file Config.local will never be included in the
+ distribution tar file. For configuration hints to propagate
+ in in distribution changes must be made to Config.local.dist.
+
+depend possible maker of hazardous waste
+
+refconf a target to interactively configure reference clock support.
+ This should work for you, but has not yet been tested on
+ the more exotic Unix ports (mostly the supercomputer ones).
+
+Bug reports of a general nature can be sent to David Mills (mills@udel.edu).
+Reports concerning specific hardware or software systems mentioned in the
+COPYRIGHT file should be sent to the author, with copy to David Mills for
+archive.
+
+The distribution has been compiled and run on at least the following
+machines, operating systems and compilers. In all known cases, if
+the gcc compiler eats it with some success, the cc compiler also enjoys
+the meal. The converse is not always true. See the conf directory for
+test suites known to compile with various radio clocks; however, not all
+the combinations that compile have been tested.
+
+ VAX-11/785 4.3 tahoe cc no REFCLOCK (dm 93/11/20)
+ Sun3 SunOS 4.1.1 gcc no REFCLOCK (pb 93/10/25)
+ Sun4 SunOS 4.1.1 gcc all REFCLOCK drivers (dm 93/10/25)
+ Sun4 SunOS 4.1.3 gcc all REFLCOCK drivers
+ Sun4 SunOS 5.1 gcc no REFCLOCK (pb 93/10/25)
+ Sun4 SunOS 5.2 gcc no REFCLOCK (dm 93/11/20)
+ Sun4 SunOS 5.2 gcc PARSE REFCLOCK (kd 93/11/10)
+ Sun4 SunOS 5.3 gcc local (pb 93/11/10)
+ HP700 HPUX 9.0 cc no REFCLOCK
+ hp7xx HPUX 9.01 cc local + PARSE (kd 93/10/26)
+ HP3xx HPUX 9.01 cc no REFCLOCK (pb 93/10/25)
+ HP3xx HPUX 8.0 cc no REFCLOCK (pb 93/10/25)
+ MIPS Ultrix 4.3a gcc WWVB clock (dm 93/11/20)
+ MIPS Ultrix 3a gcc green (pb 93/10/26)
+ ALPHA OSF 1.2a gcc no REFCLOCK (dm 93/11/20)
+ ALPHA OSF 1.3 gcc no REFCLOCK (pb 93/10/25)
+ ALPHA OSF1 1.3 gcc green (pb 93/10/26)
+ Convex Convex OS 10.1 ? ?
+ SGI IRIX 4.0.5F gcc no REFCLOCK (pb 93/11/10)
+ AIX 3.2 ? ?
+ A/UX 2.0.1, 3.x.x gcc LOCAL_CLOCK (jmj (94/01/26 see hints)
+ RS6000 AIX 3.2 gcc no REFCLOCK
+ MX500 Sinix-m V5.40 cc PARSE REFCLOCK
+ S2000 Sequent PTX 1.4 cc LOCAL_CLOCK (kd 93/11/10)
+ S2000 Sequent PTX 1.4 gcc LOCAL_CLOCK (kd 93/11/10)
+ PC FreeBSD gcc LOCAL_CLOCK see "build problems"
+ PC NetBSD? gcc LOCAL_CLOCK possibly see "build problems"
+ PC BSD/386 1.0 gcc LOCAL_CLOCK possibly see "build problems"
+ PC Linux (pl14) gcc LOCAL_CLOCK (dw 93/10/30)
+ PC Dell SVR4 v2.2 gcc ? (tl 93/12/30)
+ PC Unixware1/SVR4 cc no tickadj, ? (ras 93/04/11)
+ NCR3445 NCR SVR4 cc LOCAL_CLOCK (tm 93/11/29)
+
+ pb: Piete Brooks
+ kd: Frank Kardel
+ dw: Torsten Duwe (duwe@informatik.uni-erlangen.de)
+ dm: David Mills (mills@udel.edu)
+ tl: Tony Lill <ajlill@tlill.hookup.net>
+ tm: Tom Moore <Tom.Moore@DaytonOH.NCR.COM>
+ jmj: Jim Jagielski <jim@jagubox.gsfc.nasa.gov>
+ ras: Ray Schnitzler <schnitz@unipress.com>
+
+Build Problems (and workaround):
+
+During testing/porting we have found some
+of "make" and "sh" and "awk" features in different implementations.
+If you have problems other tha the one listed below please check for
+usualy things like the latest sh compatible pd shell in your own
+environment. Things like this are known to hinder compilation if
+they are not fully compatible with sh or are buggy.
+
+Current build problem on (Mac) NetBSD, possibly BSDI and 386BSD:
+ pmake (e. g. NetBSD on MAC, possible other BNR2+pmake systems)
+ Following Makefile construction fails for no
+ apparent reason (at least to me)
+ doit:
+ $(MAKE) MAKE=\"$(MAKE)\" all
+
+ all:
+ @echo all done.
+
+ for the "make MAKE=make" call but not for "make" or
+ "make -e MAKE=make". Use the last form if you suffer
+ from that kind of make problems. (Easily detected
+ by failure to build with the message:
+ "don't know how to make make".
+
+ On BSD/386 the solution is to get GNU make and run build as:
+ % gnumake MAKE=gnumake
+ Note that BSD/386 1.0's "sed" goes into an infinite loop if
+ you try to make the "refconf" target -- so edit Config.local
+ by hand if you have a reference clock. (BSD/386 1.1 will fix
+ this "sed" bug.)
+
+ The NetBSD people claim that this problem goes away
+ when you compile make with POSIX compilation options.
+
+The known sh and some make pecularities have already been taken care of.
+
+Usually the vendor should fix these bugs in vital utilities.
+We try to circumvent these bugs in a hopefully portable way.
+If you can reproduce these bugs on your system please bug your
+vendor/developer group to fix them. We are not trying anything fancy
+in here (except for starting sub-makes) and we are shocked that even
+the most common tools fail so miserably. By the time you get this
+code the above utilities may already have been fixed. Hopefully one
+day we do not have to cope with this kind of broken utilities.
+ Frank Kardel
+
+William L. Jones <jones@chpc.utexas.edu>
+Dennis Ferguson (Advanced Network Systems) <dennis@ans.net>
+Lars Mathiesen (University of Copenhagen) <thorinn@diku.dk>
+David Mills <mills@udel.edu>
+Frank Kardel <Frank.Kardel@informatik.uni-erlangen.de>
+Piete Brooks <Piete.Brooks@cl.cam.ac.uk>
+
+-- and a cast of thousands -- see the COPYRIGHT file
+16 November 1993
diff --git a/usr.sbin/xntpd/TODO b/usr.sbin/xntpd/TODO
new file mode 100644
index 0000000..e4bbe47
--- /dev/null
+++ b/usr.sbin/xntpd/TODO
@@ -0,0 +1,26 @@
+#
+# TODO,v 3.5 1994/01/25 19:03:55 kardel Exp
+#
+This file contains problems known to the authors that still need to be done.
+We would appreciate if you could spare some of your time to look through
+these topics and help us with some open questions. Most of the topics
+pertain to specific architectures where we have no direct access or not
+the time or expertise to currently track down the problem further.
+If you don't know what we are talking about in the topics don't bother
+with finding out - somebody else will probably solve that problem.
+
+Before you try to send a solution to mills@udel.edu please check whether
+this problem still exists in the distribution on louie.udel.edu.
+
+Thank you for your help !
+ Dave Mills
+ Frank Kardel
+ Piete Brooks
+
+Open issues:
+
+Apollo:
+ - terminal affiliation
+ Check whether thing are still correct in respect to breaking
+ terminal affiliation - horrible stories are told in the code.
+ File affected: xntpd/ntpd.c
diff --git a/usr.sbin/xntpd/VERSION b/usr.sbin/xntpd/VERSION
new file mode 100644
index 0000000..a51bf774
--- /dev/null
+++ b/usr.sbin/xntpd/VERSION
@@ -0,0 +1 @@
+version=3.4e (beta multicast)
diff --git a/usr.sbin/xntpd/authstuff/Makefile b/usr.sbin/xntpd/authstuff/Makefile
new file mode 100644
index 0000000..13d86f5
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/Makefile
@@ -0,0 +1,20 @@
+#
+# $Id: Makefile,v 1.5 1997/02/22 16:14:15 peter Exp $
+#
+# Most of the programs in this directory are completely useless for the
+# NTP configuration that we provide by default.
+# We provide the `md5' program as a public service.
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= md5
+
+SRCS= md5driver.c
+NOMAN=
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/authstuff/README b/usr.sbin/xntpd/authstuff/README
new file mode 100644
index 0000000..2985751
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/README
@@ -0,0 +1,13 @@
+README file for directory ./authstuff of the NTP Version 3 distribution
+
+This directory contains the sources for miscellaneous programs to test,
+validate and calibreate cryptographic routines used by NTP. These include
+
+authcert.c used to certify the DES and MD5 message digest algorithms
+ work properly. See the source for directions for use.
+
+authspeed.c used to determing the running time for DES and MD5
+ messge digest algorithms. See the source for directions
+ for use.
+
+For other programs, see the source files.
diff --git a/usr.sbin/xntpd/authstuff/auth.samplekeys b/usr.sbin/xntpd/authstuff/auth.samplekeys
new file mode 100644
index 0000000..761c7c2
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/auth.samplekeys
@@ -0,0 +1,44 @@
+#
+# Sample key file, also used for testing.
+#
+# Note that there are three formats for keys. Standard format is a
+# hex format with the low order bit of each byte being a parity
+# bit, a la the NBS standard. NTP format is also hex, but uses the
+# high order bit of each byte for parity. Ascii format simply encodes
+# a 1-8 character ascii string as a key. Note that because of the
+# simple tokenizing routine, the characters ' ', '#', '\t', '\n' and
+# '\0' can't be used in an ascii key. Everything else is fair game, though.
+#
+
+1 S 0101010101010101 # odd parity 0 key
+2 N 8080808080808080 # and again
+3 A ugosnod
+4 A BigbOObs
+5 S f1f1f1f1f1f1f1f1
+6 N f8f8f8f8f8f8f8f8 # same as key 5
+7 S f8f8f8f8f8f8f8f8 # not same as key 6
+8 A a # short ascii keys are zero padded
+9 A &^%$@!*(
+10 S 01020407080bf1f1
+11 N 4040404040404040
+12 A more
+13 A random
+14 A keys
+15 A password # 15 used as password by runtime configuration
+#
+16 M password # MD5 key
+17 M secret
+18 M key1
+19 M key2
+20 M foobar
+21 M tick
+22 M tock
+23 M key23
+24 M key24
+25 M key25
+26 M a
+27 M few
+28 M more
+29 M random
+30 M md5
+31 M keys!
diff --git a/usr.sbin/xntpd/authstuff/auth.speed b/usr.sbin/xntpd/authstuff/auth.speed
new file mode 100644
index 0000000..ccf8993
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/auth.speed
@@ -0,0 +1,33 @@
+Authentication delays (us) DES MD5
+-------------------------------------------
+IBM RS6000/990 10 28
+IBM RS6000/590 10 29
+HP 9000/735 hpux9.03 snavely 10 33
+DEC 3000/400 OSF/1 bunnylou 14 35
+HP 9000/730 hpux8.07(+OV) 16 55
+SGI Indigo R4000 19 48
+HP 9000/720 hpux8.07 21 66
+IBM RS6000/250 20 39
+IBM RS6000/370 21 43
+IBM RS6000/580 22 43
+SGI 4/35 38 110
+DECstation 5000/240 cowbird 39 81
+IBM RS6000/530H 40 83
+Sun4c/75 SS2 43 96
+Sun4c/50 IPX malarky 47 94
+DECstation 5000/33 sundeck 49 106
+IBM RS6000/530 51 107
+SGI Indigo 54 115
+DECstation 5000/125 herald 63 136
+IBM RS6000/320 69 139
+Sun4c/65 SS1+ pogo 72 159
+Sun4c/40 IPC grundoon 73 163
+Sun4c/60 SS1 albert 95 199
+Sun4c/20 SLC 95 203
+DECstation 3100 sheol 98 214
+DECstation 2100 circus 126 278
+VAX 780 985 ?
+
+Updated 26 April 1994
+David L. Mills
+
diff --git a/usr.sbin/xntpd/authstuff/authcert.c b/usr.sbin/xntpd/authstuff/authcert.c
new file mode 100644
index 0000000..19ade8c
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/authcert.c
@@ -0,0 +1,95 @@
+/*
+ * This file, and the certdata file, shamelessly stolen
+ * from Phil Karn's DES implementation.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#define DES
+#include "ntp_stdlib.h"
+
+u_char ekeys[128];
+u_char dkeys[128];
+
+static void get8 P((U_LONG *));
+static void put8 P((U_LONG *));
+
+void
+main()
+{
+ U_LONG key[2], plain[2], cipher[2], answer[2], temp;
+ int i;
+ int test;
+ int fail;
+
+ for(test = 0; !feof(stdin); test++){
+ get8(key);
+ DESauth_subkeys(key, ekeys, dkeys);
+ printf(" K: "); put8(key);
+ get8(plain);
+ printf(" P: "); put8(plain);
+ get8(answer);
+ printf(" C: "); put8(answer);
+ for (i = 0; i < 2; i++)
+ cipher[i] = htonl(plain[i]);
+ DESauth_des(cipher, ekeys);
+ for (i = 0; i < 2; i++) {
+ temp = ntohl(cipher[i]);
+ if (temp != answer[i])
+ break;
+ }
+
+ fail = 0;
+ if (i != 2) {
+ printf(" Encrypt FAIL");
+ fail++;
+ }
+ DESauth_des(cipher, dkeys);
+ for (i = 0; i < 2; i++) {
+ temp = ntohl(cipher[i]);
+ if (temp != plain[i])
+ break;
+ }
+ if (i != 2) {
+ printf(" Decrypt FAIL");
+ fail++;
+ }
+ if (fail == 0)
+ printf(" OK");
+ printf("\n");
+ }
+}
+
+static void
+get8(lp)
+ U_LONG *lp;
+{
+ int t;
+ U_LONG l[2];
+ int i;
+
+ l[0] = l[1] = 0;
+ for (i = 0; i < 8; i++) {
+ scanf("%2x", &t);
+ if (feof(stdin))
+ exit(0);
+ l[i / 4] <<= 8;
+ l[i / 4] |= (U_LONG)(t & 0xff);
+ }
+ *lp = l[0];
+ *(lp + 1) = l[1];
+}
+
+static void
+put8(lp)
+ U_LONG *lp;
+{
+ int i;
+
+
+ for(i = 0; i < 2; i++)
+ printf("%08lx", (u_long)(*lp++));
+}
diff --git a/usr.sbin/xntpd/authstuff/authspeed.c b/usr.sbin/xntpd/authstuff/authspeed.c
new file mode 100644
index 0000000..93965d2
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/authspeed.c
@@ -0,0 +1,315 @@
+/*
+ * authspeed - figure out how long it takes to do an NTP encryption
+ */
+
+#if defined(SYS_HPUX) || defined(SYS_AUX3) || defined(SYS_AUX2) || defined(SOLARIS) || defined(SYS_SVR4) || defined(SYS_PTX) || defined(SYS_UNIXWARE1)
+#define FAKE_RUSAGE
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef FAKE_RUSAGE
+#include <sys/param.h>
+#include <sys/times.h>
+#endif
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#define DEFLOOPS -1
+
+#define DEFDELAYLOOPS 20000
+#define DEFCOSTLOOPS 2000
+
+char *progname;
+int debug;
+
+struct timeval tstart, tend;
+#ifdef FAKE_RUSAGE
+struct tms rstart, rend;
+#define getrusage(foo, t) times(t)
+#define RUSAGE_SELF 0
+#else
+struct rusage rstart, rend;
+#endif
+
+l_fp dummy1, dummy2;
+u_long dummy3;
+
+U_LONG pkt[15];
+
+int totalcost = 0;
+double rtime;
+double vtime;
+
+int domd5 = 0;
+
+static void dodelay P((int));
+static void docheap P((int));
+static void docost P((int));
+static void subtime P((struct timeval *, struct timeval *, double *));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int loops;
+ int i;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ loops = DEFLOOPS;
+ while ((c = ntp_getopt(argc, argv, "cdmn:")) != EOF)
+ switch (c) {
+ case 'c':
+ totalcost++;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'm':
+ domd5 = 16; /* offset into list of keys */
+ break;
+ case 'n':
+ loops = atoi(ntp_optarg);
+ if (loops <= 0) {
+ (void) fprintf(stderr,
+ "%s: %s is unlikely to be a useful number of loops\n",
+ progname, ntp_optarg);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s [-d] [-n loops] [ -c ] auth.samplekeys\n",
+ progname);
+ exit(2);
+ }
+ printf("Compute timing for ");
+ if (domd5)
+ printf("MD5");
+ else
+ printf("DES");
+ printf(" based authentication.\n");
+
+ init_auth();
+ authreadkeys(argv[ntp_optind]);
+ for (i = 0; i < 16; i++) {
+ if (!auth_havekey(i + domd5)) {
+ errflg++;
+ (void) fprintf(stderr, "%s: key %d missing\n",
+ progname, i + domd5);
+ }
+ }
+
+ if (errflg) {
+ (void) fprintf(stderr,
+ "%s: check syslog for errors, or use file with complete set of keys\n",
+ progname);
+ exit(1);
+ }
+
+ if (loops == DEFLOOPS) {
+ if (totalcost)
+ loops = DEFCOSTLOOPS;
+ else
+ loops = DEFDELAYLOOPS;
+ }
+
+ dummy1.l_ui = 0x80808080;
+ dummy1.l_uf = 0xffffff00;
+ dummy3 = 0x0aaaaaaa;
+
+ for (i = 0; i < 12; i++)
+ pkt[i] = i * 0x22222;
+
+ if (totalcost) {
+ if (totalcost > 1)
+ docheap(loops);
+ else
+ docost(loops);
+ } else {
+ dodelay(loops);
+ }
+
+ printf("total real time: %.3f\n", rtime);
+ printf("total CPU time: %.3f\n", vtime);
+ if (totalcost) {
+ printf("real cost (in seconds): %.6f\n",
+ rtime/(double)loops);
+ printf("CPU cost (in seconds): %.6f\n",
+ vtime/(double)loops);
+ printf("\nThis includes the cost of a decryption plus the\n");
+ printf("the cost of an encryption, i.e. the cost to process\n");
+ printf("a single authenticated packet.\n");
+ } else {
+ printf("authdelay in the configuration file\n");
+ printf("real authentication delay: %.6f\n",
+ rtime/(double)loops);
+ printf("authentication delay in CPU time: %.6f\n",
+ vtime/(double)loops);
+ printf("\nThe CPU delay is probably the best bet for\n");
+ printf("authdelay in the configuration file\n");
+ }
+ exit(0);
+}
+
+
+/*
+ * dodelay - do the delay measurement
+ */
+static void
+dodelay(loops)
+ int loops;
+{
+ double vtime1, rtime1, vtime2, rtime2;
+ register int loopcount;
+ /*
+ * If we're attempting to compute the cost of an auth2crypt()
+ * for first compute the total cost, then compute the
+ * cost of only doing the first step, auth1crypt(). What
+ * remains is the cost of auth2crypt.
+ */
+ loopcount = loops;
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loopcount-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt((loops & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime1);
+#ifdef FAKE_RUSAGE
+ vtime1 = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime1);
+#endif
+printf("Time for full encryptions is %f rusage %f real\n", vtime1, rtime1);
+ loopcount = loops;
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loopcount-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime2);
+#ifdef FAKE_RUSAGE
+ vtime2 = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime2);
+#endif
+
+printf("Time for auth1crypt is %f rusage %f real\n", vtime2, rtime2);
+ vtime = vtime1 - vtime2;
+ rtime = rtime1 - rtime2;
+}
+
+
+/*
+ * docheap - do the cost measurement the cheap way
+ */
+static void
+docheap(loops)
+ register int loops;
+{
+
+ (void) authhavekey(3 + domd5);
+
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loops-- > 0) {
+ auth1crypt(3 + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt(3 + domd5, pkt, 48);
+ (void) authdecrypt(3 + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime);
+#ifdef FAKE_RUSAGE
+ vtime = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime);
+#endif
+}
+
+
+/*
+ * docost - do the cost measurement
+ */
+static void
+docost(loops)
+ register int loops;
+{
+
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loops-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt((loops & 0xf) + domd5, pkt, 48);
+ (void) authdecrypt(((loops+1) & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime);
+#ifdef FAKE_RUSAGE
+ vtime = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime);
+#endif
+}
+
+
+/*
+ * subtime - subtract two struct timevals, return double result
+ */
+static void
+subtime(tvs, tve, res)
+ struct timeval *tvs, *tve;
+ double *res;
+{
+ long sec;
+ long usec;
+
+ sec = tve->tv_sec - tvs->tv_sec;
+ usec = tve->tv_usec - tvs->tv_usec;
+
+ if (usec < 0) {
+ usec += 1000000;
+ sec--;
+ }
+
+ *res = (double)sec + (double)usec/1000000.;
+ return;
+}
diff --git a/usr.sbin/xntpd/authstuff/certdata b/usr.sbin/xntpd/authstuff/certdata
new file mode 100644
index 0000000..f9a818e
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/certdata
@@ -0,0 +1,34 @@
+0000000000000000 0000000000000000 8CA64DE9C1B123A7
+FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 7359B2163E4EDC58
+3000000000000000 1000000000000001 958E6E627A05557B
+1111111111111111 1111111111111111 F40379AB9E0EC533
+0123456789ABCDEF 1111111111111111 17668DFC7292532D
+1111111111111111 0123456789ABCDEF 8A5AE1F81AB8F2DD
+0000000000000000 0000000000000000 8CA64DE9C1B123A7
+FEDCBA9876543210 0123456789ABCDEF ED39D950FA74BCC4
+7CA110454A1A6E57 01A1D6D039776742 690F5B0D9A26939B
+0131D9619DC1376E 5CD54CA83DEF57DA 7A389D10354BD271
+07A1133E4A0B2686 0248D43806F67172 868EBB51CAB4599A
+3849674C2602319E 51454B582DDF440A 7178876E01F19B2A
+04B915BA43FEB5B6 42FD443059577FA2 AF37FB421F8C4095
+0113B970FD34F2CE 059B5E0851CF143A 86A560F10EC6D85B
+0170F175468FB5E6 0756D8E0774761D2 0CD3DA020021DC09
+43297FAD38E373FE 762514B829BF486A EA676B2CB7DB2B7A
+07A7137045DA2A16 3BDD119049372802 DFD64A815CAF1A0F
+04689104C2FD3B2F 26955F6835AF609A 5C513C9C4886C088
+37D06BB516CB7546 164D5E404F275232 0A2AEEAE3FF4AB77
+1F08260D1AC2465E 6B056E18759F5CCA EF1BF03E5DFA575A
+584023641ABA6176 004BD6EF09176062 88BF0DB6D70DEE56
+025816164629B007 480D39006EE762F2 A1F9915541020B56
+49793EBC79B3258F 437540C8698F3CFA 6FBF1CAFCFFD0556
+4FB05E1515AB73A7 072D43A077075292 2F22E49BAB7CA1AC
+49E95D6D4CA229BF 02FE55778117F12A 5A6B612CC26CCE4A
+018310DC409B26D6 1D9D5C5018F728C2 5F4C038ED12B2E41
+1C587F1C13924FEF 305532286D6F295A 63FAC0D034D9F793
+0101010101010101 0123456789ABCDEF 617B3A0CE8F07100
+1F1F1F1F0E0E0E0E 0123456789ABCDEF DB958605F8C8C606
+E0FEE0FEF1FEF1FE 0123456789ABCDEF EDBFD1C66C29CCC7
+0000000000000000 FFFFFFFFFFFFFFFF 355550B2150E2451
+FFFFFFFFFFFFFFFF 0000000000000000 CAAAAF4DEAF1DBAE
+0123456789ABCDEF 0000000000000000 D5D44FF720683D0D
+FEDCBA9876543210 FFFFFFFFFFFFFFFF 2A2BB008DF97C2F2
diff --git a/usr.sbin/xntpd/authstuff/keyparity.c b/usr.sbin/xntpd/authstuff/keyparity.c
new file mode 100644
index 0000000..8a12c13
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/keyparity.c
@@ -0,0 +1,279 @@
+/*
+ * keyparity - add parity bits to key and/or change an ascii key to binary
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+
+#define STD_PARITY_BITS 0x01010101
+
+char *progname;
+int debug;
+
+int ntpflag = 0;
+int stdflag = 0;
+int asciiflag = 0;
+int ntpoutflag = 0;
+int gotoopt = 0;
+
+static int parity P((u_long *));
+static int decodekey P((int, char *, u_long *));
+static void output P((u_long *, int));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ int keytype;
+ u_long key[2];
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "adno:s")) != EOF)
+ switch (c) {
+ case 'a':
+ asciiflag = 1;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ ntpflag = 1;
+ break;
+ case 's':
+ stdflag = 1;
+ break;
+ case 'o':
+ if (*ntp_optarg == 'n') {
+ ntpoutflag = 1;
+ gotoopt = 1;
+ } else if (*ntp_optarg == 's') {
+ ntpoutflag = 0;
+ gotoopt = 1;
+ } else {
+ (void) fprintf(stderr,
+ "%s: output format must be `n' or `s'\n",
+ progname);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s -n|-s [-a] [-o n|s] key [...]\n",
+ progname);
+ exit(2);
+ }
+
+ if (!ntpflag && !stdflag) {
+ (void) fprintf(stderr,
+ "%s: one of either the -n or -s flags must be specified\n",
+ progname);
+ exit(2);
+ }
+
+ if (ntpflag && stdflag) {
+ (void) fprintf(stderr,
+ "%s: only one of the -n and -s flags may be specified\n",
+ progname);
+ exit(2);
+ }
+
+ if (!gotoopt) {
+ if (ntpflag)
+ ntpoutflag = 1;
+ }
+
+ if (asciiflag)
+ keytype = KEY_TYPE_ASCII;
+ else if (ntpflag)
+ keytype = KEY_TYPE_NTP;
+ else
+ keytype = KEY_TYPE_STD;
+
+ for (; ntp_optind < argc; ntp_optind++) {
+ if (!decodekey(keytype, argv[ntp_optind], key)) {
+ (void) fprintf(stderr,
+ "%s: format of key %s invalid\n",
+ progname, argv[ntp_optind]);
+ exit(1);
+ }
+ (void) parity(key);
+ output(key, ntpoutflag);
+ }
+ exit(0);
+}
+
+
+
+/*
+ * parity - set parity on a key/check for odd parity
+ */
+static int
+parity(key)
+ u_long *key;
+{
+ u_long mask;
+ int parity_err;
+ int bitcount;
+ int half;
+ int byte;
+ int i;
+
+ /*
+ * Go through counting bits in each byte. Check to see if
+ * each parity bit was set correctly. If not, note the error
+ * and set it right.
+ */
+ parity_err = 0;
+ for (half = 0; half < 2; half++) { /* two halves of key */
+ mask = 0x80000000;
+ for (byte = 0; byte < 4; byte++) { /* 4 bytes per half */
+ bitcount = 0;
+ for (i = 0; i < 7; i++) { /* 7 data bits / byte */
+ if (key[half] & mask)
+ bitcount++;
+ mask >>= 1;
+ }
+
+ /*
+ * If bitcount is even, parity must be set. If
+ * bitcount is odd, parity must be clear.
+ */
+ if ((bitcount & 0x1) == 0) {
+ if (!(key[half] & mask)) {
+ parity_err++;
+ key[half] |= mask;
+ }
+ } else {
+ if (key[half] & mask) {
+ parity_err++;
+ key[half] &= ~mask;
+ }
+ }
+ mask >>= 1;
+ }
+ }
+
+ /*
+ * Return the result of the parity check.
+ */
+ return (parity_err == 0);
+}
+
+
+static int
+decodekey(keytype, str, key)
+ int keytype;
+ char *str;
+ u_long *key;
+{
+ u_char keybytes[8];
+ char *cp;
+ char *xdigit;
+ int len;
+ int i;
+ static char *hex = "0123456789abcdef";
+
+ cp = str;
+ len = strlen(cp);
+ if (len == 0)
+ return 0;
+
+ switch(keytype) {
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ if (len != 16) /* Lazy. Should define constant */
+ return 0;
+ /*
+ * Decode hex key.
+ */
+ key[0] = 0;
+ key[1] = 0;
+ for (i = 0; i < 16; i++) {
+ if (!isascii(*cp))
+ return 0;
+ xdigit = strchr(hex, isupper(*cp) ? tolower(*cp) : *cp);
+ cp++;
+ if (xdigit == 0)
+ return 0;
+ key[i>>3] <<= 4;
+ key[i>>3] |= (u_long)(xdigit - hex) & 0xf;
+ }
+
+ /*
+ * If this is an NTP format key, put it into NBS format
+ */
+ if (keytype == KEY_TYPE_NTP) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] << 1) & ~STD_PARITY_BITS)
+ | ((key[i] >> 7) & STD_PARITY_BITS);
+ }
+ break;
+
+ case KEY_TYPE_ASCII:
+ /*
+ * Make up key from ascii representation
+ */
+ memset(keybytes, 0, sizeof(keybytes));
+ for (i = 0; i < 8 && i < len; i++)
+ keybytes[i] = *cp++ << 1;
+ key[0] = keybytes[0] << 24 | keybytes[1] << 16
+ | keybytes[2] << 8 | keybytes[3];
+ key[1] = keybytes[4] << 24 | keybytes[5] << 16
+ | keybytes[6] << 8 | keybytes[7];
+ break;
+
+ default:
+ /* Oh, well */
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * output - print a hex key on the standard output
+ */
+static void
+output(key, ntpformat)
+ u_long *key;
+ int ntpformat;
+{
+ int i;
+
+ if (ntpformat) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] & ~STD_PARITY_BITS) >> 1)
+ | ((key[i] & STD_PARITY_BITS) << 7);
+ }
+ (void) printf("%08x%08x\n", key[0], key[1]);
+}
diff --git a/usr.sbin/xntpd/authstuff/makeIPFP.c b/usr.sbin/xntpd/authstuff/makeIPFP.c
new file mode 100644
index 0000000..8fabdb7
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makeIPFP.c
@@ -0,0 +1,345 @@
+/*
+ * makeIPFP - make fast DES IP and FP tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+u_long IPL[256];
+u_long FPL[256];
+
+char *progname;
+int debug;
+
+static void perm P((u_char *, u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * Initial permutation table
+ */
+u_char IP[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+/*
+ * Inverse initial permutation table
+ */
+u_char FP[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x55555555) << 1) | (right & 0x55555555)
+ */
+u_char IPLbits[32] = {
+ 2, 34, 4, 36, 6, 38, 8, 40,
+ 10, 42, 12, 44, 14, 46, 16, 48,
+ 18, 50, 20, 52, 22, 54, 24, 56,
+ 26, 58, 28, 60, 30, 62, 32, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xaaaaaaaa) | ((right & 0xaaaaaaaa) >> 1)
+ */
+u_char IPRbits[32] = {
+ 1, 33, 3, 35, 5, 37, 7, 39,
+ 9, 41, 11, 43, 13, 45, 15, 47,
+ 17, 49, 19, 51, 21, 53, 23, 55,
+ 25, 57, 27, 59, 29, 61, 31, 63
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x0f0f0f0f) << 4) | (right & 0x0f0f0f0f)
+ */
+u_char FPLbits[32] = {
+ 5, 6, 7, 8, 37, 38, 39, 40,
+ 13, 14, 15, 16, 45, 46, 47, 48,
+ 21, 22, 23, 24, 53, 54, 55, 56,
+ 29, 30, 31, 32, 61, 62, 63, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xf0f0f0f0) | ((right & 0xf0f0f0f0) >> 4)
+ */
+u_char FPRbits[32] = {
+ 1, 2, 3, 4, 33, 34, 35, 36,
+ 9, 10, 11, 12, 41, 42, 43, 44,
+ 17, 18, 19, 20, 49, 50, 51, 52,
+ 25, 26, 27, 28, 57, 58, 59, 60
+};
+
+
+/*
+ * perm - do a permutation with the given table
+ */
+static void
+perm(databits, permtab, leftp, rightp)
+ u_char *databits;
+ u_char *permtab;
+ u_long *leftp;
+ u_long *rightp;
+{
+ register u_long left;
+ register u_long right;
+ register u_char *PT;
+ register u_char *bits;
+ register int i;
+
+ left = right = 0;
+ PT = permtab;
+ bits = databits;
+
+ for (i = 0; i < 32; i++) {
+ left <<= 1;
+ if (bits[PT[i]-1])
+ left |= 1;
+ }
+
+ for (i = 32; i < 64; i++) {
+ right <<= 1;
+ if (bits[PT[i]-1])
+ right |= 1;
+ }
+
+ *leftp = left;
+ *rightp = right;
+}
+
+
+/*
+ * doit - make up the tables
+ */
+static void
+doit()
+{
+ u_char bits[64];
+ u_long left;
+ u_long right;
+ int tabno;
+ int i;
+ int ind0, ind1, ind2, ind3;
+ int ind4, ind5, ind6, ind7;
+ int octbits;
+
+ memset((char *)bits, 0, sizeof bits);
+
+ /*
+ * Do the rounds for the IP table. We save the results of
+ * this as well as printing them. Note that this is the
+ * left-half table, the right half table will be identical.
+ */
+ printf("static u_long IP[256] = {");
+ for (tabno = 0; tabno < 4; tabno++) {
+ i = tabno * 8;
+ ind7 = IPLbits[i] - 1;
+ ind6 = IPLbits[i+1] - 1;
+ ind5 = IPLbits[i+2] - 1;
+ ind4 = IPLbits[i+3] - 1;
+ ind3 = IPLbits[i+4] - 1;
+ ind2 = IPLbits[i+5] - 1;
+ ind1 = IPLbits[i+6] - 1;
+ ind0 = IPLbits[i+7] - 1;
+ for (octbits = 0; octbits < 256; octbits++) {
+ if (octbits & (1 << 7))
+ bits[ind7] = 1;
+ if (octbits & (1 << 6))
+ bits[ind6] = 1;
+ if (octbits & (1 << 5))
+ bits[ind5] = 1;
+ if (octbits & (1 << 4))
+ bits[ind4] = 1;
+ if (octbits & (1 << 3))
+ bits[ind3] = 1;
+ if (octbits & (1 << 2))
+ bits[ind2] = 1;
+ if (octbits & (1 << 1))
+ bits[ind1] = 1;
+ if (octbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind7] = 0;
+ bits[ind6] = 0;
+ bits[ind5] = 0;
+ bits[ind4] = 0;
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "IP tabno %d oct %d right not zero\n",
+ tabno, octbits);
+ exit(1);
+ }
+ if (tabno > 0) {
+ if ((IPL[octbits] << tabno) != left) {
+ fprintf(stderr,
+ "IP tabno %d oct %d IP %d left %d, IP != left\n",
+ tabno, octbits, IPL[octbits], left);
+ exit (1);
+ }
+ } else {
+ IPL[octbits] = left;
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ }
+ if (tabno == 0)
+ printf("\n};\n\n");
+ }
+
+ /*
+ * Next is the FP table, in big endian order
+ */
+ printf("#if BYTE_ORDER == LITTLE_ENDIAN\nstatic u_long FP[256] = {");
+ for (tabno = 3; tabno >= 0; tabno--) {
+ i = tabno * 8;
+ ind7 = FPLbits[i] - 1;
+ ind6 = FPLbits[i+1] - 1;
+ ind5 = FPLbits[i+2] - 1;
+ ind4 = FPLbits[i+3] - 1;
+ ind3 = FPLbits[i+4] - 1;
+ ind2 = FPLbits[i+5] - 1;
+ ind1 = FPLbits[i+6] - 1;
+ ind0 = FPLbits[i+7] - 1;
+ for (octbits = 0; octbits < 256; octbits++) {
+ if (octbits & (1 << 7))
+ bits[ind7] = 1;
+ if (octbits & (1 << 6))
+ bits[ind6] = 1;
+ if (octbits & (1 << 5))
+ bits[ind5] = 1;
+ if (octbits & (1 << 4))
+ bits[ind4] = 1;
+ if (octbits & (1 << 3))
+ bits[ind3] = 1;
+ if (octbits & (1 << 2))
+ bits[ind2] = 1;
+ if (octbits & (1 << 1))
+ bits[ind1] = 1;
+ if (octbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind7] = 0;
+ bits[ind6] = 0;
+ bits[ind5] = 0;
+ bits[ind4] = 0;
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "FP tabno %d oct %d right not zero\n",
+ tabno, octbits);
+ exit(1);
+ }
+ if (tabno != 3) {
+ if ((FPL[octbits] << ((3-tabno)<<1)) != left) {
+ fprintf(stderr,
+ "FP tabno %d oct %d FP %x left %x, FP != left\n",
+ tabno, octbits, FPL[octbits], left);
+ exit (1);
+ }
+ } else {
+ FPL[octbits] = left;
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ }
+ if (tabno == 3)
+ printf("\n};\n");
+ }
+
+ /*
+ * Now reouput the FP table in order appropriate for little
+ * endian machines
+ */
+ printf("#else\nstatic u_long FP[256] = {");
+ for (octbits = 0; octbits < 256; octbits++) {
+ left = ((FPL[octbits] >> 24) & 0x000000ff)
+ | ((FPL[octbits] >> 8) & 0x0000ff00)
+ | ((FPL[octbits] << 8) & 0x00ff0000)
+ | ((FPL[octbits] << 24) & 0xff000000);
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ printf("\n};\n#endif\n");
+}
diff --git a/usr.sbin/xntpd/authstuff/makePC1.c b/usr.sbin/xntpd/authstuff/makePC1.c
new file mode 100644
index 0000000..8a49ec9
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makePC1.c
@@ -0,0 +1,286 @@
+/*
+ * makePC1 - build custom permutted choice 1 tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void permute P((u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+/*
+ * Permuted choice 1 table, to produce the initial C. This table
+ * has had 1 subtracted from it to give it a zero base.
+ */
+static u_char PC1_C[28] = {
+ 56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35
+};
+
+/*
+ * Permuted choice 1 table, to produce the initial D. Again, 1 has
+ * been subtracted to match C language zero base arrays.
+ */
+static u_char PC1_D[28] = {
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3
+};
+
+/*
+ * permute - produce c and d for the given bits
+ */
+static void
+permute(bits, cp, dp)
+ u_char *bits;
+ u_long *cp;
+ u_long *dp;
+{
+ register int i;
+ register u_long mask;
+ u_char c[28];
+ u_char d[28];
+
+ memset((char *)c, 0, sizeof c);
+ memset((char *)d, 0, sizeof d);
+
+ for (i = 0; i < 28; i++) {
+ c[i] = bits[PC1_C[i]];
+ d[i] = bits[PC1_D[i]];
+ }
+
+ mask = 0x10000000;
+ *cp = *dp = 0;
+ for (i = 0; i < 28; i++) {
+ mask >>= 1;
+ if (c[i])
+ *cp |= mask;
+ if (d[i])
+ *dp |= mask;
+ }
+}
+
+
+/*
+ * bits from the left part of the key used to form the C subkey
+ */
+static int lc3[4] = { 0, 8, 16, 24 };
+
+/*
+ * bits from the left part of the key used to form the D subkey
+ */
+static int ld4[4] = { 3, 11, 19, 27 };
+
+/*
+ * bits from the right part of the key used to form the C subkey
+ */
+static int rc4[4] = { 32, 40, 48, 56 };
+
+/*
+ * bits from the right part of the key used to form the D subkey
+ */
+static int rd3[4] = { 36, 44, 52, 60 };
+
+static u_long PC_CL[8];
+static u_long PC_DL[16];
+static u_long PC_CR[16];
+static u_long PC_DR[8];
+
+
+/*
+ * doit - compute and print the four PC1 tables
+ */
+static void
+doit()
+{
+ int i;
+ int comb;
+ u_long c;
+ u_long d;
+ u_char bits[64];
+
+ memset((char *)bits, 0, sizeof bits);
+
+ printf("static u_long PC1_CL[8] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 8; comb++) {
+ if (comb & 0x4)
+ bits[lc3[i]] = 1;
+ if (comb & 0x2)
+ bits[lc3[i]+1] = 1;
+ if (comb & 0x1)
+ bits[lc3[i]+2] = 1;
+ permute(bits, &c, &d);
+ bits[lc3[i]] = 0;
+ bits[lc3[i]+1] = 0;
+ bits[lc3[i]+2] = 0;
+ if (d != 0) {
+ (void) fprintf(stderr,
+ "Error PC_CL i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_CL[comb] = c;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", c);
+ else if (comb == 7)
+ printf(" 0x%08x\n};\n\n", c);
+ else
+ printf(" 0x%08x,", c);
+ } else {
+ if (c != PC_CL[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_CL 0x%08x c 0x%08x\n",
+ PC_CL[comb], c);
+ }
+ }
+ }
+
+ printf("static u_long PC1_DL[16] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 16; comb++) {
+ if (comb & 0x8)
+ bits[ld4[i]] = 1;
+ if (comb & 0x4)
+ bits[ld4[i]+1] = 1;
+ if (comb & 0x2)
+ bits[ld4[i]+2] = 1;
+ if (comb & 0x1)
+ bits[ld4[i]+3] = 1;
+ permute(bits, &c, &d);
+ bits[ld4[i]] = 0;
+ bits[ld4[i]+1] = 0;
+ bits[ld4[i]+2] = 0;
+ bits[ld4[i]+3] = 0;
+ if (c != 0) {
+ (void) fprintf(stderr,
+ "Error PC_DL i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_DL[comb] = d;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", d);
+ else if (comb == 15)
+ printf(" 0x%08x\n};\n\n", d);
+ else
+ printf(" 0x%08x,", d);
+ } else {
+ if (d != PC_DL[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_DL 0x%08x c 0x%08x\n",
+ PC_DL[comb], d);
+ }
+ }
+ }
+
+ printf("static u_long PC1_CR[16] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 16; comb++) {
+ if (comb & 0x8)
+ bits[rc4[i]] = 1;
+ if (comb & 0x4)
+ bits[rc4[i]+1] = 1;
+ if (comb & 0x2)
+ bits[rc4[i]+2] = 1;
+ if (comb & 0x1)
+ bits[rc4[i]+3] = 1;
+ permute(bits, &c, &d);
+ bits[rc4[i]] = 0;
+ bits[rc4[i]+1] = 0;
+ bits[rc4[i]+2] = 0;
+ bits[rc4[i]+3] = 0;
+ if (d != 0) {
+ (void) fprintf(stderr,
+ "Error PC_CR i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_CR[comb] = c;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", c);
+ else if (comb == 15)
+ printf(" 0x%08x\n};\n\n", c);
+ else
+ printf(" 0x%08x,", c);
+ } else {
+ if (c != PC_CR[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_CR 0x%08x c 0x%08x\n",
+ PC_CR[comb], c);
+ }
+ }
+ }
+
+ printf("static u_long PC1_DR[8] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 8; comb++) {
+ if (comb & 0x4)
+ bits[rd3[i]] = 1;
+ if (comb & 0x2)
+ bits[rd3[i]+1] = 1;
+ if (comb & 0x1)
+ bits[rd3[i]+2] = 1;
+ permute(bits, &c, &d);
+ bits[rd3[i]] = 0;
+ bits[rd3[i]+1] = 0;
+ bits[rd3[i]+2] = 0;
+ if (c != 0) {
+ (void) fprintf(stderr,
+ "Error PC_DR i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_DR[comb] = d;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", d);
+ else if (comb == 7)
+ printf(" 0x%08x\n};\n\n", d);
+ else
+ printf(" 0x%08x,", d);
+ } else {
+ if (d != PC_DR[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_DR 0x%08x c 0x%08x\n",
+ PC_DR[comb], d);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/makePC2.c b/usr.sbin/xntpd/authstuff/makePC2.c
new file mode 100644
index 0000000..e5121fd
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makePC2.c
@@ -0,0 +1,238 @@
+/*
+ * makePC2 - build custom permutted choice 2 tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void permc P((u_char *, u_long *));
+static void permd P((u_char *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+/*
+ * Permuted choice 2 table. This actually produces the low order 24
+ * bits of the subkey Ki from the 28 bit value of Ci. This has had
+ * 1 subtracted from it to give a zero base.
+ */
+static u_char PC2_C[24] = {
+ 13, 16, 10, 23, 0, 4,
+ 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7,
+ 15, 6, 26, 19, 12, 1
+};
+
+/*
+ * Permuted choice 2 table, operating on the 28 Di bits to produce the
+ * high order 24 bits of subkey Ki. This has had 29 subtracted from
+ * it to give it a zero base into our D bit array.
+ */
+static u_char PC2_D[24] = {
+ 12, 23, 2, 8, 18, 26,
+ 1, 11, 22, 16, 4, 19,
+ 15, 20, 10, 27, 5, 24,
+ 17, 13, 21, 7, 0, 3
+};
+
+u_long masks[4] = { 0x40000000, 0x400000, 0x4000, 0x40 };
+
+
+/*
+ * permc - permute C, producing a four byte result
+ */
+static void
+permc(bits, resp)
+ u_char *bits;
+ u_long *resp;
+{
+ register int part;
+ register int i;
+ register u_long mask;
+ u_char res[24];
+
+ memset((char *)res, 0, sizeof res);
+
+ for (i = 0; i < 24; i++) {
+ res[i] = bits[PC2_C[i]];
+ }
+
+ *resp = 0;
+ for (part = 0; part < 4; part++) {
+ mask = masks[part];
+ for (i = part*6; i < (part+1)*6; i++) {
+ mask >>= 1;
+ if (res[i])
+ *resp |= mask;
+ }
+ }
+}
+
+/*
+ * permd - permute D, producing a four byte result
+ */
+static void
+permd(bits, resp)
+ u_char *bits;
+ u_long *resp;
+{
+ register int part;
+ register int i;
+ register u_long mask;
+ u_char res[24];
+
+ memset((char *)res, 0, sizeof res);
+
+ for (i = 0; i < 24; i++) {
+ res[i] = bits[PC2_D[i]];
+ }
+
+ *resp = 0;
+ for (part = 0; part < 4; part++) {
+ mask = masks[part];
+ for (i = part*6; i < (part+1)*6; i++) {
+ mask >>= 1;
+ if (res[i])
+ *resp |= mask;
+ }
+ }
+}
+
+
+/*
+ * bits used for each round in C
+ */
+static int cbits[4][6] = {
+ 0, 1, 2, 3, 4, 5,
+ 6, 7, 9, 10, 11, 12,
+ 13, 14, 15, 16, 22, 23,
+ 18, 19, 20, 25, 26, 27
+};
+
+
+/*
+ * bits used for each round in D
+ */
+static int dbits[4][6] = {
+ 0, 1, 2, 3, 4, 5,
+ 7, 8, 10, 11, 12, 13,
+ 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 26, 27
+};
+
+
+/*
+ * doit - compute and print the four PC1 tables
+ */
+static void
+doit()
+{
+ int i;
+ int comb;
+ u_long res;
+ u_char bits[28];
+
+ memset((char *)bits, 0, sizeof bits);
+
+ printf("static u_long PC2_C[4][64] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 64; comb++) {
+ if (comb & 0x20)
+ bits[cbits[i][0]] = 1;
+ if (comb & 0x10)
+ bits[cbits[i][1]] = 1;
+ if (comb & 0x8)
+ bits[cbits[i][2]] = 1;
+ if (comb & 0x4)
+ bits[cbits[i][3]] = 1;
+ if (comb & 0x2)
+ bits[cbits[i][4]] = 1;
+ if (comb & 0x1)
+ bits[cbits[i][5]] = 1;
+ permc(bits, &res);
+ bits[cbits[i][0]] = 0;
+ bits[cbits[i][1]] = 0;
+ bits[cbits[i][2]] = 0;
+ bits[cbits[i][3]] = 0;
+ bits[cbits[i][4]] = 0;
+ bits[cbits[i][5]] = 0;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", res);
+ else if (comb == 63 && i == 3)
+ printf(" 0x%08x\n};\n\n", res);
+ else if (comb == 63)
+ printf(" 0x%08x,\n", res);
+ else
+ printf(" 0x%08x,", res);
+ }
+ }
+
+ printf("static u_long PC2_D[4][64] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 64; comb++) {
+ if (comb & 0x20)
+ bits[dbits[i][0]] = 1;
+ if (comb & 0x10)
+ bits[dbits[i][1]] = 1;
+ if (comb & 0x8)
+ bits[dbits[i][2]] = 1;
+ if (comb & 0x4)
+ bits[dbits[i][3]] = 1;
+ if (comb & 0x2)
+ bits[dbits[i][4]] = 1;
+ if (comb & 0x1)
+ bits[dbits[i][5]] = 1;
+ permd(bits, &res);
+ bits[dbits[i][0]] = 0;
+ bits[dbits[i][1]] = 0;
+ bits[dbits[i][2]] = 0;
+ bits[dbits[i][3]] = 0;
+ bits[dbits[i][4]] = 0;
+ bits[dbits[i][5]] = 0;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", res);
+ else if (comb == 63 && i == 3)
+ printf(" 0x%08x\n};\n\n", res);
+ else if (comb == 63)
+ printf(" 0x%08x,\n", res);
+ else
+ printf(" 0x%08x,", res);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/makeSP.c b/usr.sbin/xntpd/authstuff/makeSP.c
new file mode 100644
index 0000000..7daefa6
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makeSP.c
@@ -0,0 +1,183 @@
+/*
+ * makeSP - build combination S and P tables for quick DES computation
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void selperm P((int, int, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * The cipher selection function tables. These turn 6 bit data back
+ * into 4 bit data.
+ */
+u_char S[8][64] = {
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
+
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
+
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
+
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
+
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
+
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
+
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
+
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+};
+
+/*
+ * Cipher function permutation table
+ */
+u_char PT[32] = {
+ 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25
+};
+
+
+/*
+ * Bits array. We keep this zeroed.
+ */
+u_char bits[32];
+
+
+/*
+ * selperm - run six bit data through the given selection table, then
+ * through the PT table to produce a long output.
+ */
+static void
+selperm(selnumber, sixbits, resp)
+ int selnumber;
+ int sixbits;
+ u_long *resp;
+{
+ register u_long res;
+ register int selno;
+ register int i;
+ register int ind;
+
+ selno = selnumber;
+ i = sixbits;
+ ind = (i & 0x20) | ((i >> 1) & 0xf) | ((i & 0x1) << 4);
+ i = S[selno][ind];
+
+ for (ind = 0; ind < 4; ind++) {
+ if (i & 0x8)
+ bits[4*selno + ind] = 1;
+ i <<= 1;
+ }
+
+ res = 0;
+ for (i = 0; i < 32; i++) {
+ res <<= 1;
+ if (bits[PT[i]-1])
+ res |= 1;
+ }
+
+ *resp = res;
+ bits[4*selno] = 0;
+ bits[4*selno + 1] = 0;
+ bits[4*selno + 2] = 0;
+ bits[4*selno + 3] = 0;
+}
+
+
+/*
+ * doit - compute and print the 8 SP tables
+ */
+static void
+doit()
+{
+ int selno;
+ u_long result;
+ int sixbits;
+
+ memset((char *)bits, 0, sizeof bits);
+ printf("static u_long SP[8][64] = {");
+ for (selno = 0; selno < 8; selno++) {
+ for (sixbits = 0; sixbits < 64; sixbits++) {
+ selperm(selno, sixbits, &result);
+ if ((sixbits & 0x3) == 0)
+ printf("\n\t0x%08x,", result);
+ else if (sixbits == 63 && selno == 7)
+ printf(" 0x%08x\n};\n", result);
+ else if (sixbits == 63)
+ printf(" 0x%08x,\n", result);
+ else
+ printf(" 0x%08x,", result);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/md5_sample_output b/usr.sbin/xntpd/authstuff/md5_sample_output
new file mode 100644
index 0000000..a7d4282
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/md5_sample_output
@@ -0,0 +1,8 @@
+MD5 test suite results:
+d41d8cd98f00b204e9800998ecf8427e ""
+0cc175b9c0f1b6a831c399e269772661 "a"
+900150983cd24fb0d6963f7d28e17f72 "abc"
+f96b697d7cb7938d525a2f31aaf161d0 "message digest"
+c3fcd3d76192e4007dfb496cca67e13b "abcdefghijklmnopqrstuvwxyz"
+d174ab98d277d9f5a5611c2c9f419d9f "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+57edf4a22be3c955ac49da2e2107b67a "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
diff --git a/usr.sbin/xntpd/authstuff/md5driver.c b/usr.sbin/xntpd/authstuff/md5driver.c
new file mode 100644
index 0000000..0d7e132
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/md5driver.c
@@ -0,0 +1,211 @@
+/*
+ ***********************************************************************
+ ** md5driver.c -- sample test routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/16/90 RLR **
+ ** Updated: 1/91 SRD **
+ ** Updated: 7/91 SRD Removed file "foo" from test suite **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#if defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/time.h>
+#endif /* SYS_BSDI */
+#include "md5.h"
+
+#ifndef MD5
+#define MD5
+#endif
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+/* Prints message digest buffer in mdContext as 32 hexadecimal digits.
+ Order is from low-order byte to high-order byte of digest.
+ Each byte is printed with high-order hexadecimal digit first.
+ */
+static void
+MDPrint (mdContext)
+MD5_CTX *mdContext;
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ printf ("%02x", mdContext->digest[i]);
+}
+
+/* size of test block */
+#define TEST_BLOCK_SIZE 1000
+
+/* number of blocks to process */
+#define TEST_BLOCKS 10000
+
+/* number of test bytes = TEST_BLOCK_SIZE * TEST_BLOCKS */
+static long TEST_BYTES = (long)TEST_BLOCK_SIZE * (long)TEST_BLOCKS;
+
+/* A time trial routine, to measure the speed of MD5.
+ Measures wall time required to digest TEST_BLOCKS * TEST_BLOCK_SIZE
+ characters.
+ */
+static void
+MDTimeTrial ()
+{
+ MD5_CTX mdContext;
+ time_t endTime, startTime;
+ unsigned char data[TEST_BLOCK_SIZE];
+ unsigned int i;
+
+ /* initialize test data */
+ for (i = 0; i < TEST_BLOCK_SIZE; i++)
+ data[i] = (unsigned char)(i & 0xFF);
+
+ /* start timer */
+ printf ("MD5 time trial. Processing %ld characters...\n", (long)TEST_BYTES);
+ time (&startTime);
+
+ /* digest data in TEST_BLOCK_SIZE byte blocks */
+ MD5Init (&mdContext);
+ for (i = TEST_BLOCKS; i > 0; i--)
+ MD5Update (&mdContext, data, TEST_BLOCK_SIZE);
+ MD5Final (&mdContext);
+
+ /* stop timer, get time difference */
+ time (&endTime);
+ MDPrint (&mdContext);
+ printf (" is digest of test input.\n");
+ printf
+ ("Seconds to process test input: %ld\n", (long)endTime-startTime);
+ printf
+ ("Characters processed per second: %ld\n",
+ (long)(TEST_BYTES/(endTime-startTime)));
+}
+
+/* Computes the message digest for string inString.
+ Prints out message digest, a space, the string (in quotes) and a
+ carriage return.
+ */
+static void
+MDString (inString)
+char *inString;
+{
+ MD5_CTX mdContext;
+ unsigned int len = strlen (inString);
+
+ MD5Init (&mdContext);
+ MD5Update (&mdContext, inString, len);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf (" \"%s\"\n", inString);
+}
+
+/* Computes the message digest for a specified file.
+ Prints out message digest, a space, the file name, and a carriage
+ return.
+ */
+static void
+MDFile (filename)
+char *filename;
+{
+ FILE *inFile = fopen (filename, "rb");
+ MD5_CTX mdContext;
+ int bytes;
+ unsigned char data[1024];
+
+ if (inFile == NULL) {
+ printf ("%s can't be opened.\n", filename);
+ return;
+ }
+
+ MD5Init (&mdContext);
+ while ((bytes = fread (data, 1, 1024, inFile)) != 0)
+ MD5Update (&mdContext, data, bytes);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf (" %s\n", filename);
+ fclose (inFile);
+}
+
+/* Writes the message digest of the data from stdin onto stdout,
+ followed by a carriage return.
+ */
+static void
+MDFilter ()
+{
+ MD5_CTX mdContext;
+ int bytes;
+ unsigned char data[16];
+
+ MD5Init (&mdContext);
+ while ((bytes = fread (data, 1, 16, stdin)) != 0)
+ MD5Update (&mdContext, data, bytes);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf ("\n");
+}
+
+/* Runs a standard suite of test data.
+ */
+static void
+MDTestSuite ()
+{
+ printf ("MD5 test suite results:\n");
+ MDString ("");
+ MDString ("a");
+ MDString ("abc");
+ MDString ("message digest");
+ MDString ("abcdefghijklmnopqrstuvwxyz");
+ MDString
+ ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ MDString
+ ("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
+}
+
+void
+main (argc, argv)
+int argc;
+char *argv[];
+{
+ int i;
+
+ /* For each command line argument in turn:
+ ** filename -- prints message digest and name of file
+ ** -sstring -- prints message digest and contents of string
+ ** -t -- prints time trial statistics for 10M
+ characters
+ ** -x -- execute a standard suite of test data
+ ** (no args) -- writes messages digest of stdin onto stdout
+ */
+ if (argc == 1)
+ MDFilter ();
+ else
+ for (i = 1; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == 's')
+ MDString (argv[i] + 2);
+ else if (strcmp (argv[i], "-t") == 0)
+ MDTimeTrial ();
+ else if (strcmp (argv[i], "-x") == 0)
+ MDTestSuite ();
+ else MDFile (argv[i]);
+}
+
+/*
+ ***********************************************************************
+ ** End of md5driver.c **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/authstuff/mkrandkeys.c b/usr.sbin/xntpd/authstuff/mkrandkeys.c
new file mode 100644
index 0000000..0edf733
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/mkrandkeys.c
@@ -0,0 +1,167 @@
+/*
+ * mkrandkeys - make a key file for xntpd with some quite random keys
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+u_long keydata[2];
+
+int std = 1; /* DES standard key format */
+u_char dokey[16] = { 0 };
+
+static void rand_data P((u_long *));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int i;
+ int j;
+ int errflg = 0;
+ int numkeys;
+ u_long tmp;
+ char *passwd;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ extern char *getpass();
+
+ numkeys = 0;
+ progname = argv[0];
+ passwd = NULL;
+ while ((c = ntp_getopt(argc, argv, "dnp:s")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ std = 0;
+ break;
+ case 'p':
+ passwd = ntp_optarg;
+ break;
+ case 's':
+ std = 1;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+
+ numkeys = 0;
+ for (; !errflg && ntp_optind < argc; ntp_optind++) {
+ c = atoi(argv[ntp_optind]);
+ if (c <= 0 || c > 15) {
+ (void) fprintf(stderr, "%s: invalid key number `%s'\n",
+ progname, argv[ntp_optind]);
+ exit(2);
+ }
+ dokey[c] = 1;
+ numkeys++;
+ }
+
+ if (errflg || numkeys == 0) {
+ (void) fprintf(stderr,
+ "usage: %s [-ns] [-p seed] key# [key# ...]\n",
+ progname);
+ exit(2);
+ }
+
+ while (passwd == 0 || *passwd == '\0') {
+ passwd = getpass("Seed: ");
+ if (*passwd == '\0') {
+ (void) fprintf(stderr,
+ "better use a better seed than that\n");
+ }
+ }
+
+ keydata[0] = keydata[1] = 0;
+ for (i = 0; i < 8 && *passwd != '\0'; i++) {
+ keydata[i/4] |= ((((u_long)(*passwd))&0xff)<<(1+((3-(i%4))*8)));
+ passwd++;
+ }
+
+ for (i = 1; i <= 15; i++) {
+ if (dokey[i]) {
+ for (c = 0, tmp = 0; c < 32; c += 4)
+ tmp |= (i << c);
+ keydata[0] ^= tmp;
+ keydata[1] ^= tmp;
+ rand_data(keydata);
+ DESauth_parity(keydata);
+
+ if (std) {
+ (void)printf("%-2d S\t%08x%08x\n",
+ i, keydata[0], keydata[1]);
+ } else {
+ for (j = 0; j < 2; j++) {
+ keydata[j]
+ = ((keydata[j] & 0xfefefefe) >> 1)
+ | ((keydata[j] & 0x01010101) << 7);
+ }
+ (void)printf("%-2d N\t%08x%08x\n",
+ i, keydata[0], keydata[1]);
+ }
+ }
+ }
+ exit(0);
+}
+
+char *volatile_file[] = {
+ "/bin/echo",
+ "/bin/sh",
+ "/bin/cat",
+ "/bin/ls",
+ "/bin/stty",
+ "/bin/date",
+ "/bin/cat",
+ "/bin/cc",
+ "/etc/motd",
+ "/etc/utmp",
+ "/dev/kmem",
+ "/dev/null",
+ "",
+};
+
+#define NEXT(X) (0x1e1f2f2d*(X) + 0x361962e9)
+
+static void
+rand_data(data)
+ u_long *data;
+{
+ register i;
+ struct stat buf;
+ extern long time();
+ char ekeys[128], dkeys[128];
+
+ *data ^= 0x9662f394;
+ *(data+1) ^= 0x9f17c55f;
+ DESauth_subkeys(data, ekeys, dkeys);
+ *data ^= NEXT(getpid() + (getuid() << 16));
+ *(data+1) ^= NEXT(time((long *)0));
+ DESauth_des(data, ekeys);
+ for (i = 0; strlen(volatile_file[i]); i++) {
+ if (stat(volatile_file[i], &buf) == -1)
+ continue;
+ if (i & 1) {
+ *data ^= NEXT(buf.st_atime);
+ *(data+1) ^= NEXT(buf.st_mtime);
+ } else {
+ *data ^= NEXT(buf.st_mtime);
+ *(data+1) ^= NEXT(buf.st_atime);
+ }
+ DESauth_des(data, ekeys);
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/omakeIPFP.c b/usr.sbin/xntpd/authstuff/omakeIPFP.c
new file mode 100644
index 0000000..92d87be
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/omakeIPFP.c
@@ -0,0 +1,361 @@
+/*
+ * makeIPFP - make fast DES IP and FP tables
+ *
+ * This is an older version which generated tables half the size of
+ * the current version, but which took about double the CPU time to
+ * compute permutations from these tables. Since the CPU spent on the
+ * permutations is small compared to the CPU spent in the cipher code,
+ * I may go back to the smaller tables to save the space some day.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+u_long IPL[8][16];
+u_long FPL[8][16];
+
+char *progname;
+int debug;
+
+static void perm P((u_char *, u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * Initial permutation table
+ */
+u_char IP[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+/*
+ * Inverse initial permutation table
+ */
+u_char FP[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x55555555) << 1) | (right & 0x55555555)
+ */
+u_char IPLbits[32] = {
+ 2, 34, 4, 36, 6, 38, 8, 40,
+ 10, 42, 12, 44, 14, 46, 16, 48,
+ 18, 50, 20, 52, 22, 54, 24, 56,
+ 26, 58, 28, 60, 30, 62, 32, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xaaaaaaaa) | ((right & 0xaaaaaaaa) >> 1)
+ */
+u_char IPRbits[32] = {
+ 1, 33, 3, 35, 5, 37, 7, 39,
+ 9, 41, 11, 43, 13, 45, 15, 47,
+ 17, 49, 19, 51, 21, 53, 23, 55,
+ 25, 57, 27, 59, 29, 61, 31, 63
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x0f0f0f0f) << 4) | (right & 0x0f0f0f0f)
+ */
+u_char FPLbits[32] = {
+ 5, 6, 7, 8, 37, 38, 39, 40,
+ 13, 14, 15, 16, 45, 46, 47, 48,
+ 21, 22, 23, 24, 53, 54, 55, 56,
+ 29, 30, 31, 32, 61, 62, 63, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xf0f0f0f0) | ((right & 0xf0f0f0f0) >> 4)
+ */
+u_char FPRbits[32] = {
+ 1, 2, 3, 4, 33, 34, 35, 36,
+ 9, 10, 11, 12, 41, 42, 43, 44,
+ 17, 18, 19, 20, 49, 50, 51, 52,
+ 25, 26, 27, 28, 57, 58, 59, 60
+};
+
+
+/*
+ * perm - do a permutation with the given table
+ */
+static void
+perm(databits, permtab, leftp, rightp)
+ u_char *databits;
+ u_char *permtab;
+ u_long *leftp;
+ u_long *rightp;
+{
+ register u_long left;
+ register u_long right;
+ register u_char *PT;
+ register u_char *bits;
+ register int i;
+
+ left = right = 0;
+ PT = permtab;
+ bits = databits;
+
+ for (i = 0; i < 32; i++) {
+ left <<= 1;
+ if (bits[PT[i]-1])
+ left |= 1;
+ }
+
+ for (i = 32; i < 64; i++) {
+ right <<= 1;
+ if (bits[PT[i]-1])
+ right |= 1;
+ }
+
+ *leftp = left;
+ *rightp = right;
+}
+
+
+/*
+ * doit - make up the tables
+ */
+static void
+doit()
+{
+ u_char bits[64];
+ u_long left;
+ u_long right;
+ int tabno;
+ int i;
+ int ind0, ind1, ind2, ind3;
+ int quadbits;
+
+ memset((char *)bits, 0, sizeof bits);
+
+ /*
+ * Do the rounds for the IPL table. We save the results of
+ * this as well as printing them. Note that this is the
+ * left-half table.
+ */
+ printf("static u_long IP[8][16] = {");
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = IPLbits[i] - 1;
+ ind2 = IPLbits[i+1] - 1;
+ ind1 = IPLbits[i+2] - 1;
+ ind0 = IPLbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "IPL tabno %d quad %d right not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ IPL[tabno][quadbits] = left;
+ if (quadbits == 15 && tabno == 7) {
+ printf(" 0x%08x", left);
+ } else if (quadbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ if (tabno == 7)
+ printf("\n};\n");
+ printf("\n");
+ }
+
+ /*
+ * Compute the right half of the same table. I noticed this table
+ * was the same as the previous one, just by luck, so we don't
+ * actually have to do this. Do it anyway just for a check.
+ */
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = IPRbits[i] - 1;
+ ind2 = IPRbits[i+1] - 1;
+ ind1 = IPRbits[i+2] - 1;
+ ind0 = IPRbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (left != 0) {
+ fprintf(stderr,
+ "IPR tabno %d quad %d left not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ if (right != IPL[tabno][quadbits]) {
+ fprintf(stderr,
+ "IPR tabno %d quad %d: 0x%08x not same as 0x%08x\n",
+ tabno, quadbits, right,IPL[tabno][quadbits]);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Next are the FP tables
+ */
+ printf("static u_long FP[8][16] = {");
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = FPLbits[i] - 1;
+ ind2 = FPLbits[i+1] - 1;
+ ind1 = FPLbits[i+2] - 1;
+ ind0 = FPLbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "FPL tabno %d quad %d right not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ FPL[tabno][quadbits] = left;
+ if (quadbits == 15 && tabno == 7) {
+ printf(" 0x%08x", left);
+ } else if (quadbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ if (tabno == 7)
+ printf("\n};");
+ printf("\n");
+ }
+
+ /*
+ * Right half of same set of tables. This was symmetric too.
+ * Amazing!
+ */
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = FPRbits[i] - 1;
+ ind2 = FPRbits[i+1] - 1;
+ ind1 = FPRbits[i+2] - 1;
+ ind0 = FPRbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (left != 0) {
+ fprintf(stderr,
+ "FPR tabno %d quad %d left not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ if (right != FPL[tabno][quadbits]) {
+ fprintf(stderr,
+ "FPR tabno %d quad %d: 0x%08x not same as 0x%08x\n",
+ tabno, quadbits, right,FPL[tabno][quadbits]);
+ exit(1);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/results b/usr.sbin/xntpd/authstuff/results
new file mode 100644
index 0000000..305a179
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/results
@@ -0,0 +1,2 @@
+odin/1000000: 0.000145
+idavolde/1000000: 0.000451
diff --git a/usr.sbin/xntpd/authstuff/unixcert.c b/usr.sbin/xntpd/authstuff/unixcert.c
new file mode 100644
index 0000000..9ec2c98
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/unixcert.c
@@ -0,0 +1,156 @@
+/*
+ * This file, and the certdata file, shamelessly stolen
+ * from Phil Karn's DES implementation.
+ *
+ * This version uses the standard Unix setkey() and encrypt()
+ * routines to do the encryption.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+static void get8 P((U_LONG *));
+static void put8 P((U_LONG *));
+static void do_setkey P((U_LONG *));
+static void do_crypt P((U_LONG *, int));
+
+void
+main()
+{
+ U_LONG key[2], plain[2], cipher[2], answer[2];
+ int i;
+ int test;
+ int fail;
+
+ for(test=0;!feof(stdin);test++){
+ get8(key);
+ do_setkey(key);
+ printf(" K: "); put8(key);
+
+ get8(plain);
+ printf(" P: "); put8(plain);
+
+ get8(answer);
+ printf(" C: "); put8(answer);
+
+
+ for(i=0;i<2;i++)
+ cipher[i] = plain[i];
+ do_crypt(cipher, 0);
+
+ for(i=0;i<2;i++)
+ if(cipher[i] != answer[i])
+ break;
+ fail = 0;
+ if(i != 2){
+ printf(" Encrypt FAIL");
+ fail++;
+ }
+ do_crypt(cipher, 1);
+ for(i=0;i<2;i++)
+ if(cipher[i] != plain[i])
+ break;
+ if(i != 2){
+ printf(" Decrypt FAIL");
+ fail++;
+ }
+ if(fail == 0)
+ printf(" OK");
+ printf("\n");
+ }
+}
+
+static void
+get8(lp)
+U_LONG *lp;
+{
+ int t;
+ U_LONG l[2];
+ int i;
+
+ l[0] = l[1] = 0L;
+ for(i=0;i<8;i++){
+ scanf("%2x",&t);
+ if(feof(stdin))
+ exit(0);
+ l[i/4] <<= 8;
+ l[i/4] |= (U_LONG)(t & 0xff);
+ }
+ *lp = l[0];
+ *(lp+1) = l[1];
+}
+
+static void
+put8(lp)
+U_LONG *lp;
+{
+ int i;
+
+
+ for(i=0;i<2;i++){
+ printf("%08x",*lp++);
+ }
+}
+
+static void
+do_setkey(key)
+ U_LONG *key;
+{
+ int j;
+ register int i;
+ register char *kb;
+ register U_LONG *kp;
+ char keybits[64];
+
+ kb = keybits;
+ kp = key;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 32; i++) {
+ if (*kp & (1<<(31-i)))
+ *kb++ = 1;
+ else
+ *kb++ = 0;
+ }
+ kp++;
+ }
+ setkey(keybits);
+}
+
+static void
+do_crypt(data, edflag)
+ U_LONG *data;
+ int edflag;
+{
+ int j;
+ register int i;
+ register char *bp;
+ register U_LONG *dp;
+ char block[64];
+
+ bp = block;
+ dp = data;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 32; i++) {
+ if (*dp & (1<<(31-i)))
+ *bp++ = 1;
+ else
+ *bp++ = 0;
+ }
+ dp++;
+ }
+
+ encrypt(block, edflag);
+
+ bp = block;
+ dp = data;
+ for (j = 0; j < 2; j++) {
+ *dp = 0;
+ for (i = 0; i < 32; i++) {
+ if (*bp++)
+ *dp |= 1<<(31-i);
+ }
+ dp++;
+ }
+}
diff --git a/usr.sbin/xntpd/clockstuff/Makefile b/usr.sbin/xntpd/clockstuff/Makefile
new file mode 100644
index 0000000..5dfc4ca
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/Makefile
@@ -0,0 +1,16 @@
+#
+# $Id: Makefile,v 1.4 1997/02/22 16:14:17 peter Exp $
+#
+
+PROG= propdelay
+DPADD= ${LIBNTP} ${LIBM}
+LDADD= ${LIBNTP} -lm
+
+SRCS= propdelay.c
+NOMAN=
+
+install:
+
+CLEANFILES+= chutest clktest chutest.o clktest.o
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/clockstuff/README b/usr.sbin/xntpd/clockstuff/README
new file mode 100644
index 0000000..3714ab3
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/README
@@ -0,0 +1,31 @@
+README file for directory ./clockstuff of the NTP Version 3 distribution
+
+This directory contains the sources for utility programs designed to
+support radio clocks. The chutest.c and clktest.c are desgined to
+test the chu_clk and tty_clk line disciplines and STREAMS modules in
+the ../kernel directory.
+
+These files have been modified to work with either the line disciplines
+or the STREAMS modules. Be sure to define -DSTREAM if appropriate.
+
+These are random bits of things written to help with clocks. You can
+make things in here by typing one or more of:
+
+ make propdelay (or `make')
+ make chutest
+ make clktest
+
+Propdelay computes high frequency propagation delays, given the
+longitude and latitude of the transmitter and receiver. Use
+this for WWV/H and CHU. Don't use it for WWVB (the computation
+is easier for that).
+
+Chutest can be used to input and process data from a CHU modem
+attached to a serial port. It will use the CHU line discipline
+(if installed), or raw mode otherwise. This was used to test
+out the initial reduction algorithms, and may not be up to date.
+
+Clktest can be used to test the clock line discipline (CLKLDISC,
+it must be available), and to take a look at radio clocks attached to a
+serial port.
+
diff --git a/usr.sbin/xntpd/clockstuff/chutest.c b/usr.sbin/xntpd/clockstuff/chutest.c
new file mode 100644
index 0000000..d8b804a
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/chutest.c
@@ -0,0 +1,798 @@
+/* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
+ * chutest - test the CHU clock
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sgtty.h>
+
+#include "../include/ntp_fp.h"
+#include "../include/ntp.h"
+#include "../include/ntp_unixtime.h"
+
+#ifdef STREAM
+#include <sys/chudefs.h>
+#include <stropts.h>
+#endif
+
+#ifdef CHULDISC
+#include <sys/chudefs.h>
+#endif
+
+#ifndef CHULDISC
+#ifndef STREAM
+#define NCHUCHARS (10)
+
+struct chucode {
+ u_char codechars[NCHUCHARS]; /* code characters */
+ u_char ncodechars; /* number of code characters */
+ u_char chustatus; /* not used currently */
+ struct timeval codetimes[NCHUCHARS]; /* arrival times */
+};
+#endif
+#endif
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+int dofilter = 0; /* set to 1 when we should run filter algorithm */
+int showtimes = 0; /* set to 1 when we should show char arrival times */
+int doprocess = 0; /* set to 1 when we do processing analogous to driver */
+#ifdef CHULDISC
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+#ifdef STREAM
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+
+struct timeval lasttv;
+struct chucode chudata;
+
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+/*
+ * main - parse arguments and handle options
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ void init_chu();
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
+ switch (c) {
+ case 'c':
+#ifdef STREAM
+ usechuldisc = 1;
+ break;
+#endif
+#ifdef CHULDISC
+ usechuldisc = 1;
+ break;
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr,
+ "%s: CHU line discipline not available on this machine\n",
+ progname);
+ exit(2);
+#endif
+#endif
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ dofilter = 1;
+ break;
+ case 'p':
+ doprocess = 1;
+ case 't':
+ showtimes = 1;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind+1 != argc) {
+#ifdef STREAM
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifdef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
+ progname);
+#endif
+#endif
+ exit(2);
+ }
+
+ (void) gettimeofday(&lasttv, (struct timezone *)0);
+ c = openterm(argv[ntp_optind]);
+ init_chu();
+#ifdef STREAM
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+#ifdef CHULDISC
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+ process_raw(c);
+ /*NOTREACHED*/
+}
+
+
+/*
+ * openterm - open a port to the CHU clock
+ */
+int
+openterm(dev)
+ char *dev;
+{
+ int s;
+ struct sgttyb ttyb;
+
+ if (debug)
+ (void) fprintf(stderr, "Doing open...");
+ if ((s = open(dev, O_RDONLY, 0777)) < 0)
+ error("open(%s)", dev, "");
+ if (debug)
+ (void) fprintf(stderr, "open okay\n");
+
+ if (debug)
+ (void) fprintf(stderr, "Setting exclusive use...");
+ if (ioctl(s, TIOCEXCL, (char *)0) < 0)
+ error("ioctl(TIOCEXCL)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+ ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
+ ttyb.sg_erase = ttyb.sg_kill = 0;
+ ttyb.sg_flags = EVENP|ODDP|RAW;
+ if (debug)
+ (void) fprintf(stderr, "Setting baud rate et al...");
+ if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
+ error("ioctl(TIOCSETP, raw)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+#ifdef CHULDISC
+ if (usechuldisc) {
+ int ldisc;
+
+ if (debug)
+ (void) fprintf(stderr, "Switching to CHU ldisc...");
+ ldisc = CHULDISC;
+ if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
+ error("ioctl(TIOCSETD, CHULDISC)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+#ifdef STREAM
+ if (usechuldisc) {
+
+ if (debug)
+ (void) fprintf(stderr, "Poping off streams...");
+ while (ioctl(s, I_POP, 0) >=0) ;
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ if (debug)
+ (void) fprintf(stderr, "Pushing CHU stream...");
+ if (ioctl(s, I_PUSH, "chu") < 0)
+ error("ioctl(I_PUSH, \"chu\")", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+ return s;
+}
+
+
+/*
+ * process_raw - process characters in raw mode
+ */
+process_raw(s)
+ int s;
+{
+ u_char c;
+ int n;
+ struct timeval tv;
+ struct timeval difftv;
+
+ while ((n = read(s, &c, sizeof(char))) > 0) {
+ (void) gettimeofday(&tv, (struct timezone *)0);
+ if (dofilter)
+ raw_filter((unsigned int)c, &tv);
+ else {
+ difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
+ difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
+ if (difftv.tv_usec < 0) {
+ difftv.tv_sec--;
+ difftv.tv_usec += 1000000;
+ }
+ (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
+ c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
+ difftv.tv_usec);
+ lasttv = tv;
+ }
+ }
+
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+
+
+/*
+ * raw_filter - run the line discipline filter over raw data
+ */
+raw_filter(c, tv)
+ unsigned int c;
+ struct timeval *tv;
+{
+ static struct timeval diffs[10] = { 0 };
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed BCD test\n");
+ chudata.ncodechars = 0;
+ return;
+ }
+
+ if (chudata.ncodechars > 0) {
+ diff.tv_sec = tv->tv_sec
+ - chudata.codetimes[chudata.ncodechars].tv_sec;
+ diff.tv_usec = tv->tv_usec
+ - chudata.codetimes[chudata.ncodechars].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ } /*
+ if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed time test\n");
+ chudata.ncodechars = 0;
+ return;
+ } */
+ }
+
+ chudata.codechars[chudata.ncodechars] = c;
+ chudata.codetimes[chudata.ncodechars] = *tv;
+ if (chudata.ncodechars > 0)
+ diffs[chudata.ncodechars] = diff;
+ if (++chudata.ncodechars == 10) {
+ if (doprocess) {
+ TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
+ } else {
+ register int i;
+
+ for (i = 0; i < chudata.ncodechars; i++) {
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chudata.codechars[i] & 0xf,
+ (chudata.codechars[i] >>4 ) & 0xf,
+ chudata.codetimes[i].tv_sec,
+ chudata.codetimes[i].tv_usec,
+ diffs[i].tv_sec, diffs[i].tv_usec);
+ }
+ }
+ chudata.ncodechars = 0;
+ }
+}
+
+
+/* #ifdef CHULDISC*/
+/*
+ * process_ldisc - process line discipline
+ */
+process_ldisc(s)
+ int s;
+{
+ struct chucode chu;
+ int n;
+ register int i;
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
+ if (n != sizeof chu) {
+ (void) fprintf(stderr, "Expected %d, got %d\n",
+ sizeof chu, n);
+ continue;
+ }
+
+ if (doprocess) {
+ TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chu, &ts);
+ } else {
+ for (i = 0; i < NCHUCHARS; i++) {
+ if (i == 0)
+ diff.tv_sec = diff.tv_usec = 0;
+ else {
+ diff.tv_sec = chu.codetimes[i].tv_sec
+ - chu.codetimes[i-1].tv_sec;
+ diff.tv_usec = chu.codetimes[i].tv_usec
+ - chu.codetimes[i-1].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ }
+ }
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
+ chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
+ diff.tv_sec, diff.tv_usec);
+ }
+ }
+ }
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+/*#endif*/
+
+
+/*
+ * error - print an error message
+ */
+error(fmt, s1, s2)
+ char *fmt;
+ char *s1;
+ char *s2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, s1, s2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+ exit(1);
+}
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of CHU units permitted */
+#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
+#define NCHUCODES 9 /* expect 9 CHU codes per minute */
+
+/*
+ * When CHU is operating optimally we want the primary clock distance
+ * to come out at 300 ms. Thus, peer.distance in the CHU peer structure
+ * is set to 290 ms and we compute delays which are at least 10 ms long.
+ * The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define CHUDISTANCE 0x00004a3d
+#define CHUBASEDELAY 0x0000028f
+
+/*
+ * To compute a quality for the estimate (a pseudo delay) we add a
+ * fixed 10 ms for each missing code in the minute and add to this
+ * the sum of the differences between the remaining offsets and the
+ * estimated sample offset.
+ */
+#define CHUDELAYPENALTY 0x0000028f
+
+/*
+ * Other constant stuff
+ */
+#define CHUPRECISION (-9) /* what the heck */
+#define CHUREFID "CHU\0"
+
+/*
+ * Default fudge factors
+ */
+#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
+#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
+ * as an l_fp fraction, NZPOBITS is the number of significant bits
+ * in ZEROPTONE.
+ */
+#define ZEROPTONE 0x1999999a
+#define NZPOBITS 29
+
+/*
+ * The CHU table. This gives the expected time of arrival of each
+ * character after the on-time second and is computed as follows:
+ * The CHU time code is sent at 300 bps. Your average UART will
+ * synchronize at the edge of the start bit and will consider the
+ * character complete at the center of the first stop bit, i.e.
+ * 0.031667 ms later. Thus the expected time of each interrupt
+ * is the start bit time plus 0.031667 seconds. These times are
+ * in chutable[]. To this we add such things as propagation delay
+ * and delay fudge factor.
+ */
+#define CHARDELAY 0x081b4e80
+
+static u_long chutable[NCHUCHARS] = {
+ 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */
+ 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */
+ 0x34395810 + CHARDELAY, /* 0.204 (exactly) */
+ 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */
+ 0x472b020c + CHARDELAY, /* 0.278 (exactly) */
+ 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */
+ 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */
+ 0x63958106 + CHARDELAY, /* 0.389 (exactly) */
+ 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */
+ 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */
+};
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp propagation_delay;
+static l_fp fudgefactor;
+static l_fp offset_fudge;
+
+/*
+ * We keep track of the start of the year, watching for changes.
+ * We also keep track of whether the year is a leap year or not.
+ * All because stupid CHU doesn't include the year in the time code.
+ */
+static u_long yearstart;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Time conversion tables imported from the library
+ */
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+
+/*
+ * init_chu - initialize internal chu driver data
+ */
+void
+init_chu()
+{
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ propagation_delay.l_ui = 0;
+ propagation_delay.l_uf = DEFPROPDELAY;
+ fudgefactor.l_ui = 0;
+ fudgefactor.l_uf = DEFFILTFUDGE;
+ offset_fudge = propagation_delay;
+ L_ADD(&offset_fudge, &fudgefactor);
+
+ yearstart = 0;
+}
+
+
+void
+chufilter(chuc, rtime)
+ struct chucode *chuc;
+ l_fp *rtime;
+{
+ register int i;
+ register u_long date_ui;
+ register u_long tmp;
+ register u_char *code;
+ int isneg;
+ int imin;
+ int imax;
+ u_long reftime;
+ l_fp off[NCHUCHARS];
+ l_fp ts;
+ int day, hour, minute, second;
+ static u_char lastcode[NCHUCHARS];
+ extern u_long calyearstart();
+ extern char *mfptoa();
+ void chu_process();
+ extern char *prettydate();
+
+ /*
+ * We'll skip the checks made in the kernel, but assume they've
+ * been done. This means that all characters are BCD and
+ * the intercharacter spacing isn't unreasonable.
+ */
+
+ /*
+ * print the code
+ */
+ for (i = 0; i < NCHUCHARS; i++)
+ printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
+ ((chuc->codechars[i]>>4) & 0xf) + '0');
+ printf("\n");
+
+ /*
+ * Format check. Make sure the two halves match.
+ */
+ for (i = 0; i < NCHUCHARS/2; i++)
+ if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
+ (void) printf("Bad format, halves don't match\n");
+ return;
+ }
+
+ /*
+ * Break out the code into the BCD nibbles. Only need to fiddle
+ * with the first half since both are identical. Note the first
+ * BCD character is the low order nibble, the second the high order.
+ */
+ code = lastcode;
+ for (i = 0; i < NCHUCHARS/2; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ /*
+ * If the first nibble isn't a 6, we're up the creek
+ */
+ code = lastcode;
+ if (*code++ != 6) {
+ (void) printf("Bad format, no 6 at start\n");
+ return;
+ }
+
+ /*
+ * Collect the day, the hour, the minute and the second.
+ */
+ day = *code++;
+ day = MULBY10(day) + *code++;
+ day = MULBY10(day) + *code++;
+ hour = *code++;
+ hour = MULBY10(hour) + *code++;
+ minute = *code++;
+ minute = MULBY10(minute) + *code++;
+ second = *code++;
+ second = MULBY10(second) + *code++;
+
+ /*
+ * Sanity check the day and time. Note that this
+ * only occurs on the 31st through the 39th second
+ * of the minute.
+ */
+ if (day < 1 || day > 366
+ || hour > 23 || minute > 59
+ || second < 31 || second > 39) {
+ (void) printf("Failed date sanity check: %d %d %d %d\n",
+ day, hour, minute, second);
+ return;
+ }
+
+ /*
+ * Compute seconds into the year.
+ */
+ tmp = (u_long)(MULBY24((day-1)) + hour); /* hours */
+ tmp = MULBY60(tmp) + (u_long)minute; /* minutes */
+ tmp = MULBY60(tmp) + (u_long)second; /* seconds */
+
+ /*
+ * Now the fun begins. We demand that the received time code
+ * be within CLOCK_WAYTOOBIG of the receive timestamp, but
+ * there is uncertainty about the year the timestamp is in.
+ * Use the current year start for the first check, this should
+ * work most of the time.
+ */
+ date_ui = tmp + yearstart;
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* looks good */
+
+ /*
+ * Trouble. Next check is to see if the year rolled over and, if
+ * so, try again with the new year's start.
+ */
+ date_ui = calyearstart(rtime->l_ui);
+ if (date_ui != yearstart) {
+ yearstart = date_ui;
+ date_ui += tmp;
+ (void) printf("time %u, code %u, difference %d\n",
+ date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* okay this time */
+ }
+
+ ts.l_uf = 0;
+ ts.l_ui = yearstart;
+ printf("yearstart %s\n", prettydate(&ts));
+ printf("received %s\n", prettydate(rtime));
+ ts.l_ui = date_ui;
+ printf("date_ui %s\n", prettydate(&ts));
+
+ /*
+ * Here we know the year start matches the current system
+ * time. One remaining possibility is that the time code
+ * is in the year previous to that of the system time. This
+ * is only worth checking if the receive timestamp is less
+ * than CLOCK_WAYTOOBIG seconds into the new year.
+ */
+ if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
+ date_ui = tmp + calyearstart(yearstart - CLOCK_WAYTOOBIG);
+ if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
+ goto codeokay;
+ }
+
+ /*
+ * One last possibility is that the time stamp is in the year
+ * following the year the system is in. Try this one before
+ * giving up.
+ */
+ date_ui = tmp + calyearstart(yearstart + (400*24*60*60)); /* 400 days */
+ if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
+ printf("Date hopelessly off\n");
+ return; /* hopeless, let it sync to other peers */
+ }
+
+codeokay:
+ reftime = date_ui;
+ /*
+ * We've now got the integral seconds part of the time code (we hope).
+ * The fractional part comes from the table. We next compute
+ * the offsets for each character.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ register u_long tmp2;
+
+ off[i].l_ui = date_ui;
+ off[i].l_uf = chutable[i];
+ tmp = chuc->codetimes[i].tv_sec + JAN_1970;
+ TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
+ M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
+ }
+
+ /*
+ * Here is a *big* problem. What one would normally
+ * do here on a machine with lots of clock bits (say
+ * a Vax or the gizmo board) is pick the most positive
+ * offset and the estimate, since this is the one that
+ * is most likely suffered the smallest interrupt delay.
+ * The trouble is that the low order clock bit on an IBM
+ * RT, which is the machine I had in mind when doing this,
+ * ticks at just under the millisecond mark. This isn't
+ * precise enough. What we can do to improve this is to
+ * average all 10 samples and rely on the second level
+ * filtering to pick the least delayed estimate. Trouble
+ * is, this means we have to divide a 64 bit fixed point
+ * number by 10, a procedure which really sucks. Oh, well.
+ * First compute the sum.
+ */
+ date_ui = 0;
+ tmp = 0;
+ for (i = 0; i < NCHUCHARS; i++)
+ M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
+ if (M_ISNEG(date_ui, tmp))
+ isneg = 1;
+ else
+ isneg = 0;
+
+ /*
+ * Here is a multiply-by-0.1 optimization that should apply
+ * just about everywhere. If the magnitude of the sum
+ * is less than 9 we don't have to worry about overflow
+ * out of a 64 bit product, even after rounding.
+ */
+ if (date_ui < 9 || date_ui > 0xfffffff7) {
+ register u_long prod_ui;
+ register u_long prod_uf;
+
+ prod_ui = prod_uf = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT(date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD(prod_ui, prod_uf, date_ui, tmp);
+ }
+
+ /*
+ * Done, round it correctly. Prod_ui contains the
+ * fraction.
+ */
+ if (prod_uf & 0x80000000)
+ prod_ui++;
+ if (isneg)
+ date_ui = 0xffffffff;
+ else
+ date_ui = 0;
+ tmp = prod_ui;
+ /*
+ * date_ui is integral part, tmp is fraction.
+ */
+ } else {
+ register u_long prod_ovr;
+ register u_long prod_ui;
+ register u_long prod_uf;
+ register u_long highbits;
+
+ prod_ovr = prod_ui = prod_uf = 0;
+ if (isneg)
+ highbits = 0xffffffff; /* sign extend */
+ else
+ highbits = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT3(highbits, date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD3(prod_ovr, prod_uf, prod_ui,
+ highbits, date_ui, tmp);
+ }
+
+ if (prod_uf & 0x80000000)
+ M_ADDUF(prod_ovr, prod_ui, (u_long)1);
+ date_ui = prod_ovr;
+ tmp = prod_ui;
+ }
+
+ /*
+ * At this point we have the mean offset, with the integral
+ * part in date_ui and the fractional part in tmp. Store
+ * it in the structure.
+ */
+ /*
+ * Add in fudge factor.
+ */
+ M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
+
+ /*
+ * Find the minimun and maximum offset
+ */
+ imin = imax = 0;
+ for (i = 1; i < NCHUCHARS; i++) {
+ if (L_ISGEQ(&off[i], &off[imax])) {
+ imax = i;
+ } else if (L_ISGEQ(&off[imin], &off[i])) {
+ imin = i;
+ }
+ }
+
+ L_ADD(&off[imin], &offset_fudge);
+ if (imin != imax)
+ L_ADD(&off[imax], &offset_fudge);
+ (void) printf("mean %s, min %s, max %s\n",
+ mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
+ lfptoa(&off[imax], 8));
+}
diff --git a/usr.sbin/xntpd/clockstuff/clktest.c b/usr.sbin/xntpd/clockstuff/clktest.c
new file mode 100644
index 0000000..b540485
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/clktest.c
@@ -0,0 +1,511 @@
+/* clktest.c,v 3.1 1993/07/06 01:05:23 jbj Exp
+ * clktest - test the clock line discipline
+ *
+ * usage: clktest -b bps -f -t timeo -s cmd -c char1 -a char2 /dev/whatever
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sgtty.h>
+
+#include "../include/ntp_fp.h"
+#include "../include/ntp.h"
+#include "../include/ntp_unixtime.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#if defined(ULT_2_0_SUCKS)
+#ifndef sigmask
+#define sigmask(m) (1<<(m))
+#endif
+#endif
+
+#ifndef STREAM
+#ifndef CLKLDISC
+ CLOCK_LINE_DISCIPLINE_NEEDED_BY_THIS_PROGRAM;
+#endif
+#endif
+
+/*
+ * Mask for blocking SIGIO and SIGALRM
+ */
+#define BLOCKSIGMASK (sigmask(SIGIO)|sigmask(SIGALRM))
+
+/*
+ * speed table
+ */
+struct speeds {
+ int bps;
+ int rate;
+} speedtab[] = {
+ { 300, B300 },
+ { 1200, B1200 },
+ { 2400, B2400 },
+ { 4800, B4800 },
+ { 9600, B9600 },
+ { 19200, EXTA },
+ { 38400, EXTB },
+ { 0, 0 }
+};
+
+char *progname;
+int debug;
+
+#ifdef CLKLDISC
+#define DEFMAGIC '\r'
+#endif
+
+#ifdef STREAM
+#include <stropts.h>
+#include <sys/clkdefs.h>
+#define DEFMAGIC "\r"
+#endif
+
+struct timeval timeout = { 0 };
+char *cmd = NULL;
+int cmdlen;
+int docmd = 0;
+#ifdef CLKLDISC
+u_long magic1 = DEFMAGIC;
+u_long magic2 = DEFMAGIC;
+#endif
+#ifdef STREAM
+char magic[32];
+#endif
+int speed = B9600;
+int ttflags = RAW|EVENP|ODDP;
+
+int wasalarmed;
+int iosig;
+
+struct timeval lasttv;
+
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+/*
+ * main - parse arguments and handle options
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ struct speeds *spd;
+ u_long tmp;
+ int fd;
+ struct sgttyb ttyb;
+ struct itimerval itimer;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ int alarming();
+ int ioready();
+
+ progname = argv[0];
+#ifdef STREAM
+ magic[0] = 0;
+#endif
+ while ((c = ntp_getopt(argc, argv, "a:b:c:dfs:t:")) != EOF)
+ switch (c) {
+#ifdef CLKLDISC
+ case 'a':
+#endif
+ case 'c':
+ if (!atouint(ntp_optarg, &tmp)) {
+ (void) fprintf(stderr,
+ "%s: argument for -%c must be integer\n",
+ progname, c);
+ errflg++;
+ break;
+ }
+#ifdef CLKLDISC
+ if (c == 'c')
+ magic1 = tmp;
+ else
+ magic2 = tmp;
+#endif
+#ifdef STREAM
+ magic[strlen(magic)+1] = '\0';
+ magic[strlen(magic)] = tmp;
+#endif
+ break;
+ case 'b':
+ if (!atouint(ntp_optarg, &tmp)) {
+ errflg++;
+ break;
+ }
+ spd = speedtab;
+ while (spd->bps != 0)
+ if ((int)tmp == spd->bps)
+ break;
+ if (spd->bps == 0) {
+ (void) fprintf(stderr,
+ "%s: speed %lu is unsupported\n",
+ progname, tmp);
+ errflg++;
+ } else {
+ speed = spd->rate;
+ }
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ ttflags |= CRMOD;
+ break;
+ case 's':
+ cmdlen = strlen(ntp_optarg);
+ if (cmdlen == 0)
+ errflg++;
+ else
+ cmd = ntp_optarg;
+ break;
+ case 't':
+ if (!atouint(ntp_optarg, &tmp))
+ errflg++;
+ else {
+ timeout.tv_sec = (long)tmp;
+ docmd = 1;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind+1 != argc) {
+ (void) fprintf(stderr,
+#ifdef CLKLDISC
+"usage: %s [-b bps] [-c magic1] [-a magic2] [-f] [-s cmd] [-t timeo] tty_device\n",
+#endif
+#ifdef STREAM
+"usage: %s [-b bps] [-c magic1] [-c magic2]... [-f] [-s cmd] [-t timeo] tty_device\n",
+#endif
+ progname);
+ exit(2);
+ }
+
+#ifdef STREAM
+ if (!strlen(magic))
+ strcpy(magic,DEFMAGIC);
+#endif
+
+ if (docmd)
+ fd = open(argv[ntp_optind], O_RDWR, 0777);
+ else
+ fd = open(argv[ntp_optind], O_RDONLY, 0777);
+ if (fd == -1) {
+ (void) fprintf(stderr, "%s: open(%s): ", progname,
+ argv[ntp_optind]);
+ perror("");
+ exit(1);
+ }
+
+ if (ioctl(fd, TIOCEXCL, (char *)0) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCEXCL): ", progname);
+ perror("");
+ exit(1);
+ }
+
+ /*
+ * If we have the clock discipline, set the port to raw. Otherwise
+ * we run cooked.
+ */
+ ttyb.sg_ispeed = ttyb.sg_ospeed = speed;
+#ifdef CLKLDISC
+ ttyb.sg_erase = (char)magic1;
+ ttyb.sg_kill = (char)magic2;
+#endif
+ ttyb.sg_flags = (short)ttflags;
+ if (ioctl(fd, TIOCSETP, (char *)&ttyb) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCSETP): ", progname);
+ perror("");
+ exit(1);
+ }
+
+ if (fcntl(fd, F_SETOWN, getpid()) == -1) {
+ (void) fprintf(stderr, "%s: fcntl(F_SETOWN): ", progname);
+ perror("");
+ exit(1);
+ }
+
+#ifdef CLKLDISC
+ {
+ int ldisc;
+ ldisc = CLKLDISC;
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCSETD): ", progname);
+ perror("");
+ exit(1);
+ }
+ }
+#endif
+#ifdef STREAM
+ if (ioctl(fd, I_POP, 0) >=0 ) ;
+ if (ioctl(fd, I_PUSH, "clk") < 0) {
+ (void) fprintf(stderr, "%s: ioctl(I_PUSH): ", progname);
+ perror("");
+ exit(1);
+ }
+ if (ioctl(fd, CLK_SETSTR, magic) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(CLK_SETSTR): ", progname);
+ perror("");
+ exit(1);
+ }
+#endif
+
+
+ (void) gettimeofday(&lasttv, (struct timezone *)0);
+ if (docmd) {
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ iosig = 0;
+ (void) signal(SIGIO, ioready);
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ (void) fprintf(stderr, "%s: fcntl(F_SETFL): ",
+ progname);
+ perror("");
+ exit(1);
+ }
+
+ /*
+ * Set up the alarm interrupt.
+ */
+ wasalarmed = 0;
+ (void) signal(SIGALRM, alarming);
+ itimer.it_interval = itimer.it_value = timeout;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+ doboth(fd);
+ }
+ doioonly(fd);
+}
+
+
+/*
+ * doboth - handle both I/O and alarms via SIGIO
+ */
+doboth(fd)
+ int fd;
+{
+ int n;
+ int sawalarm;
+ int sawiosig;
+ int omask;
+ fd_set fds;
+ struct timeval tvzero;
+
+ sawalarm = 0;
+ sawiosig = 0;
+ FD_ZERO(&fds);
+ for (;;) {
+ omask = sigblock(BLOCKSIGMASK);
+ if (wasalarmed) { /* alarmed? */
+ sawalarm = 1;
+ wasalarmed = 0;
+ }
+ if (iosig) {
+ sawiosig = 1;
+ iosig = 0;
+ }
+
+ if (!sawalarm && !sawiosig) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+ sigpause(omask);
+ if (wasalarmed) { /* alarmed? */
+ sawalarm = 1;
+ wasalarmed = 0;
+ }
+ if (iosig) {
+ sawiosig = 1;
+ iosig = 0;
+ }
+ }
+ (void)sigsetmask(omask);
+
+ if (sawiosig) {
+
+ do {
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ FD_SET(fd, &fds);
+ n = select(fd+1, &fds, (fd_set *)0,
+ (fd_set *)0, &tvzero);
+ if (n > 0)
+ doio(fd);
+ } while (n > 0);
+
+ if (n == -1) {
+ (void) fprintf(stderr, "%s: select: ",
+ progname);
+ perror("");
+ exit(1);
+ }
+ sawiosig = 0;
+ }
+ if (sawalarm) {
+ doalarm(fd);
+ sawalarm = 0;
+ }
+ }
+}
+
+
+/*
+ * doioonly - do I/O. This avoids the use of signals
+ */
+doioonly(fd)
+ int fd;
+{
+ int n;
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ for (;;) {
+ FD_SET(fd, &fds);
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0,
+ (struct timeval *)0);
+ if (n > 0)
+ doio(fd);
+ }
+}
+
+
+/*
+ * doio - read a buffer full of stuff and print it out
+ */
+doio(fd)
+ int fd;
+{
+ register char *rp, *rpend;
+ register char *cp;
+ register int i;
+ char raw[512];
+ struct timeval tv, tvd;
+ int rlen;
+ int ind;
+ char cooked[2049];
+ static char *digits = "0123456789abcdef";
+
+ rlen = read(fd, raw, sizeof(raw));
+ if (rlen < 0) {
+ (void) fprintf(stderr, "%s: read(): ", progname);
+ perror("");
+ return;
+ }
+ if (rlen == 0) {
+ (void) printf("Zero length read\n");
+ return;
+ }
+
+ cp = cooked;
+ rp = raw;
+ rpend = &raw[rlen];
+ ind = 0;
+
+ while (rp < rpend) {
+ ind = 1;
+ if (isprint(*rp))
+ *cp++ = *rp;
+ else {
+ *cp++ = '<';
+ *cp++ = digits[((*rp)>>4) & 0xf];
+ *cp++ = digits[*rp & 0xf];
+ *cp++ = '>';
+ }
+#ifdef CLKLDISC
+ if (*rp == (char)magic1 || *rp == (char)magic2) {
+#else
+ if ( strchr( magic, *rp) != NULL ) {
+#endif
+ rp++;
+ ind = 0;
+ *cp = '\0';
+ if ((rpend - rp) < sizeof(struct timeval)) {
+ (void)printf(
+ "Too little data (%d): %s\n",
+ rpend-rp, cooked);
+ return;
+ }
+
+ tv.tv_sec = 0;
+ for (i = 0; i < 4; i++) {
+ tv.tv_sec <<= 8;
+ tv.tv_sec |= ((long)*rp++) & 0xff;
+ }
+ tv.tv_usec = 0;
+ for (i = 0; i < 4; i++) {
+ tv.tv_usec <<= 8;
+ tv.tv_usec |= ((long)*rp++) & 0xff;
+ }
+
+ tvd.tv_sec = tv.tv_sec - lasttv.tv_sec;
+ tvd.tv_usec = tv.tv_usec - lasttv.tv_usec;
+ if (tvd.tv_usec < 0) {
+ tvd.tv_usec += 1000000;
+ tvd.tv_sec--;
+ }
+
+ (void)printf("%lu.%06lu %lu.%06lu %s\n",
+ tv.tv_sec, tv.tv_usec, tvd.tv_sec, tvd.tv_usec,
+ cooked);
+ lasttv = tv;
+ } else {
+ rp++;
+ }
+ }
+
+ if (ind) {
+ *cp = '\0';
+ (void)printf("Incomplete data: %s\n", cooked);
+ }
+}
+
+
+/*
+ * doalarm - send a string out the port, if we have one.
+ */
+doalarm(fd)
+ int fd;
+{
+ int n;
+
+ if (cmd == NULL || cmdlen <= 0)
+ return;
+
+ n = write(fd, cmd, cmdlen);
+
+ if (n < 0) {
+ (void) fprintf(stderr, "%s: write(): ", progname);
+ perror("");
+ } else if (n < cmdlen) {
+ (void) printf("Short write (%d bytes, should be %d)\n",
+ n, cmdlen);
+ }
+}
+
+
+/*
+ * alarming - receive alarm interupt
+ */
+alarming()
+{
+ wasalarmed = 1;
+}
+
+/*
+ * ioready - handle SIGIO interrupt
+ */
+ioready()
+{
+ iosig = 1;
+}
diff --git a/usr.sbin/xntpd/clockstuff/propdelay.c b/usr.sbin/xntpd/clockstuff/propdelay.c
new file mode 100644
index 0000000..507bc08
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/propdelay.c
@@ -0,0 +1,536 @@
+/* propdelay.c,v 3.1 1993/07/06 01:05:24 jbj Exp
+ * propdelay - compute propagation delays
+ *
+ * cc -o propdelay propdelay.c -lm
+ *
+ * "Time and Frequency Users' Manual", NBS Technical Note 695 (1977).
+ */
+
+/*
+ * This can be used to get a rough idea of the HF propagation delay
+ * between two points (usually between you and the radio station).
+ * The usage is
+ *
+ * propdelay latitudeA longitudeA latitudeB longitudeB
+ *
+ * where points A and B are the locations in question. You obviously
+ * need to know the latitude and longitude of each of the places.
+ * The program expects the latitude to be preceded by an 'n' or 's'
+ * and the longitude to be preceded by an 'e' or 'w'. It understands
+ * either decimal degrees or degrees:minutes:seconds. Thus to compute
+ * the delay between the WWVH (21:59:26N, 159:46:00W) and WWV (40:40:49N,
+ * 105:02:27W) you could use:
+ *
+ * propdelay n21:59:26 w159:46 n40:40:49 w105:02:27
+ *
+ * By default it prints out a summer (F2 average virtual height 350 km) and
+ * winter (F2 average virtual height 250 km) number. The results will be
+ * quite approximate but are about as good as you can do with HF time anyway.
+ * You might pick a number between the values to use, or use the summer
+ * value in the summer and switch to the winter value when the static
+ * above 10 MHz starts to drop off in the fall. You can also use the
+ * -h switch if you want to specify your own virtual height.
+ *
+ * You can also do a
+ *
+ * propdelay -W n45:17:47 w75:45:22
+ *
+ * to find the propagation delays to WWV and WWVH (from CHU in this
+ * case), a
+ *
+ * propdelay -C n40:40:49 w105:02:27
+ *
+ * to find the delays to CHU, and a
+ *
+ * propdelay -G n52:03:17 w98:34:18
+ *
+ * to find delays to GOES via each of the three satellites.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ntp_stdlib.h"
+
+extern double sin P((double));
+extern double cos P((double));
+extern double acos P((double));
+extern double tan P((double));
+extern double atan P((double));
+extern double sqrt P((double));
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Program constants
+ */
+#define EARTHRADIUS (6370.0) /* raduis of earth (km) */
+#define LIGHTSPEED (299800.0) /* speed of light, km/s */
+#define PI (3.1415926536)
+#define RADPERDEG (PI/180.0) /* radians per degree */
+#define MILE (1.609344) /* km in a mile */
+
+#define SUMMERHEIGHT (350.0) /* summer height in km */
+#define WINTERHEIGHT (250.0) /* winter height in km */
+
+#define SATHEIGHT (6.6110 * 6378.0) /* geosync satellite height in km
+ from centre of earth */
+
+#define WWVLAT "n40:40:49"
+#define WWVLONG "w105:02:27"
+
+#define WWVHLAT "n21:59:26"
+#define WWVHLONG "w159:46:00"
+
+#define CHULAT "n45:17:47"
+#define CHULONG "w75:45:22"
+
+#define GOES_UP_LAT "n37:52:00"
+#define GOES_UP_LONG "w75:27:00"
+#define GOES_EAST_LONG "w75:00:00"
+#define GOES_STBY_LONG "w105:00:00"
+#define GOES_WEST_LONG "w135:00:00"
+#define GOES_SAT_LAT "n00:00:00"
+
+char *wwvlat = WWVLAT;
+char *wwvlong = WWVLONG;
+
+char *wwvhlat = WWVHLAT;
+char *wwvhlong = WWVHLONG;
+
+char *chulat = CHULAT;
+char *chulong = CHULONG;
+
+char *goes_up_lat = GOES_UP_LAT;
+char *goes_up_long = GOES_UP_LONG;
+char *goes_east_long = GOES_EAST_LONG;
+char *goes_stby_long = GOES_STBY_LONG;
+char *goes_west_long = GOES_WEST_LONG;
+char *goes_sat_lat = GOES_SAT_LAT;
+
+int hflag = 0;
+int Wflag = 0;
+int Cflag = 0;
+int Gflag = 0;
+int height;
+
+char *progname;
+int debug;
+
+static void doit P((double, double, double, double, double, char *));
+static double latlong P((char *, int));
+static double greatcircle P((double, double, double, double));
+static double waveangle P((double, double, int));
+static double propdelay P((double, double, int));
+static int finddelay P((double, double, double, double, double, double *));
+static void satdoit P((double, double, double, double, double, double, char *));
+static void satfinddelay P((double, double, double, double, double *));
+static double satpropdelay P((double));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ double lat1, long1;
+ double lat2, long2;
+ double lat3, long3;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "dh:CWG")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'h':
+ hflag++;
+ height = atof(ntp_optarg);
+ if (height <= 0.0) {
+ (void) fprintf(stderr, "height %s unlikely\n",
+ ntp_optarg);
+ errflg++;
+ }
+ break;
+ case 'C':
+ Cflag++;
+ break;
+ case 'W':
+ Wflag++;
+ break;
+ case 'G':
+ Gflag++;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || (!(Cflag || Wflag || Gflag) && ntp_optind+4 != argc) ||
+ ((Cflag || Wflag || Gflag) && ntp_optind+2 != argc)) {
+ (void) fprintf(stderr,
+ "usage: %s [-d] [-h height] lat1 long1 lat2 long2\n",
+ progname);
+ (void) fprintf(stderr," - or -\n");
+ (void) fprintf(stderr,
+ "usage: %s -CWG [-d] lat long\n",
+ progname);
+ exit(2);
+ }
+
+
+ if (!(Cflag || Wflag || Gflag)) {
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(argv[ntp_optind + 2], 1);
+ long2 = latlong(argv[ntp_optind + 3], 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "winter propagation, ");
+ }
+ } else if (Wflag) {
+ /*
+ * Compute delay from WWV
+ */
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(wwvlat, 1);
+ long2 = latlong(wwvlong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "WWV ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "WWV summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "WWV winter propagation, ");
+ }
+
+ /*
+ * Compute delay from WWVH
+ */
+ lat2 = latlong(wwvhlat, 1);
+ long2 = latlong(wwvhlong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "WWVH ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "WWVH summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "WWVH winter propagation, ");
+ }
+ } else if (Cflag) {
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(chulat, 1);
+ long2 = latlong(chulong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "CHU ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "CHU summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "CHU winter propagation, ");
+ }
+ } else if (Gflag) {
+ lat1 = latlong(goes_up_lat, 1);
+ long1 = latlong(goes_up_long, 0);
+ lat3 = latlong(argv[ntp_optind], 1);
+ long3 = latlong(argv[ntp_optind + 1], 0);
+
+ lat2 = latlong(goes_sat_lat, 1);
+
+ long2 = latlong(goes_west_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via WEST");
+
+ long2 = latlong(goes_stby_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via STBY");
+
+ long2 = latlong(goes_east_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via EAST");
+
+ }
+ exit(0);
+}
+
+
+/*
+ * doit - compute a delay and print it
+ */
+static void
+doit(lat1, long1, lat2, long2, h, str)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double h;
+ char *str;
+{
+ int hops;
+ double delay;
+
+ hops = finddelay(lat1, long1, lat2, long2, h, &delay);
+ printf("%sheight %g km, hops %d, delay %g seconds\n",
+ str, h, hops, delay);
+}
+
+
+/*
+ * latlong - decode a latitude/longitude value
+ */
+static double
+latlong(str, islat)
+ char *str;
+ int islat;
+{
+ register char *cp;
+ register char *bp;
+ double arg;
+ double div;
+ int isneg;
+ char buf[32];
+ char *colon;
+
+ if (islat) {
+ /*
+ * Must be north or south
+ */
+ if (*str == 'N' || *str == 'n')
+ isneg = 0;
+ else if (*str == 'S' || *str == 's')
+ isneg = 1;
+ else
+ isneg = -1;
+ } else {
+ /*
+ * East is positive, west is negative
+ */
+ if (*str == 'E' || *str == 'e')
+ isneg = 0;
+ else if (*str == 'W' || *str == 'w')
+ isneg = 1;
+ else
+ isneg = -1;
+ }
+
+ if (isneg >= 0)
+ str++;
+
+ colon = strchr(str, ':');
+ if (colon != NULL) {
+ /*
+ * in hhh:mm:ss form
+ */
+ cp = str;
+ bp = buf;
+ while (cp < colon)
+ *bp++ = *cp++;
+ *bp = '\0';
+ cp++;
+ arg = atof(buf);
+ div = 60.0;
+ colon = strchr(cp, ':');
+ if (colon != NULL) {
+ bp = buf;
+ while (cp < colon)
+ *bp++ = *cp++;
+ *bp = '\0';
+ cp++;
+ arg += atof(buf) / div;
+ div = 3600.0;
+ }
+ if (*cp != '\0')
+ arg += atof(cp) / div;
+ } else {
+ arg = atof(str);
+ }
+
+ if (isneg == 1)
+ arg = -arg;
+
+ if (debug > 2)
+ (void) printf("latitude/longitude %s = %g\n", str, arg);
+
+ return arg;
+}
+
+
+/*
+ * greatcircle - compute the great circle distance in kilometers
+ */
+static double
+greatcircle(lat1, long1, lat2, long2)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+{
+ double dg;
+ double l1r, l2r;
+
+ l1r = lat1 * RADPERDEG;
+ l2r = lat2 * RADPERDEG;
+ dg = EARTHRADIUS * acos(
+ (cos(l1r) * cos(l2r) * cos((long2-long1)*RADPERDEG))
+ + (sin(l1r) * sin(l2r)));
+ if (debug >= 2)
+ printf(
+ "greatcircle lat1 %g long1 %g lat2 %g long2 %g dist %g\n",
+ lat1, long1, lat2, long2, dg);
+ return dg;
+}
+
+
+/*
+ * waveangle - compute the wave angle for the given distance, virtual
+ * height and number of hops.
+ */
+static double
+waveangle(dg, h, n)
+ double dg;
+ double h;
+ int n;
+{
+ double theta;
+ double delta;
+
+ theta = dg / (EARTHRADIUS * (double)(2 * n));
+ delta = atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2)) - theta;
+ if (debug >= 2)
+ printf("waveangle dist %g height %g hops %d angle %g\n",
+ dg, h, n, delta / RADPERDEG);
+ return delta;
+}
+
+
+/*
+ * propdelay - compute the propagation delay
+ */
+static double
+propdelay(dg, h, n)
+ double dg;
+ double h;
+ int n;
+{
+ double phi;
+ double theta;
+ double td;
+
+ theta = dg / (EARTHRADIUS * (double)(2 * n));
+ phi = (PI/2.0) - atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2));
+ td = dg / (LIGHTSPEED * sin(phi));
+ if (debug >= 2)
+ printf("propdelay dist %g height %g hops %d time %g\n",
+ dg, h, n, td);
+ return td;
+}
+
+
+/*
+ * finddelay - find the propagation delay
+ */
+static int
+finddelay(lat1, long1, lat2, long2, h, delay)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double h;
+ double *delay;
+{
+ double dg; /* great circle distance */
+ double delta; /* wave angle */
+ int n; /* number of hops */
+
+ dg = greatcircle(lat1, long1, lat2, long2);
+ if (debug)
+ printf("great circle distance %g km %g miles\n", dg, dg/MILE);
+
+ n = 1;
+ while ((delta = waveangle(dg, h, n)) < 0.0) {
+ if (debug)
+ printf("tried %d hop%s, no good\n", n, n>1?"s":"");
+ n++;
+ }
+ if (debug)
+ printf("%d hop%s okay, wave angle is %g\n", n, n>1?"s":"",
+ delta / RADPERDEG);
+
+ *delay = propdelay(dg, h, n);
+ return n;
+}
+
+/*
+ * satdoit - compute a delay and print it
+ */
+static void
+satdoit(lat1, long1, lat2, long2, lat3, long3, str)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double lat3;
+ double long3;
+ char *str;
+{
+ double up_delay,down_delay;
+
+ satfinddelay(lat1, long1, lat2, long2, &up_delay);
+ satfinddelay(lat3, long3, lat2, long2, &down_delay);
+
+ printf("%s, delay %g seconds\n", str, up_delay + down_delay);
+}
+
+/*
+ * satfinddelay - calculate the one-way delay time between a ground station
+ * and a satellite
+ */
+static void
+satfinddelay(lat1, long1, lat2, long2, delay)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double *delay;
+{
+ double dg; /* great circle distance */
+
+ dg = greatcircle(lat1, long1, lat2, long2);
+
+ *delay = satpropdelay(dg);
+}
+
+/*
+ * satpropdelay - calculate the one-way delay time between a ground station
+ * and a satellite
+ */
+static double
+satpropdelay(dg)
+ double dg;
+{
+ double k1, k2, dist;
+ double theta;
+ double td;
+
+ theta = dg / (EARTHRADIUS);
+ k1 = EARTHRADIUS * sin(theta);
+ k2 = SATHEIGHT - (EARTHRADIUS * cos(theta));
+ if (debug >= 2)
+ printf("Theta %g k1 %g k2 %g\n", theta, k1, k2);
+ dist = sqrt(k1*k1 + k2*k2);
+ td = dist / LIGHTSPEED;
+ if (debug >= 2)
+ printf("propdelay dist %g height %g time %g\n", dg, dist, td);
+ return td;
+}
diff --git a/usr.sbin/xntpd/conf/Config.CHATHAM b/usr.sbin/xntpd/conf/Config.CHATHAM
new file mode 100644
index 0000000..b1f980b
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.CHATHAM
@@ -0,0 +1,211 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.CHATHAM,v 3.1 1993/07/06 01:03:42 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DWORDS_BIGENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# For some machines, settimeofday does not set the sub-second component
+# of the time correctly. For these machines add -DSETTIMEOFDAY_BROKEN.
+# If xntpd keeps STEPPING the clock by small amounts, then it is
+# possible that you are suffering from this problem.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# Adding -DLOCK_PROCESS to the compilation flags will prevent
+# xntpd from being swapped out on systems where the plock(3) call
+# is available.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# There are three ways to utilize external 1-pps signals. Define -DPPS to
+# include just the pps routine, such as used by the DCF77 reference clock
+# driver. Define -DPPSDEV ito include a serial device driver. This
+# requires a serial port and either a line discipline or STREAMS module.
+# Define -DPPSCD to include the driver and a special kernal hack
+# (for SunOS 4.1.1) that intercepts carrier-detect transitions
+# generated by the pps signal. Only one of these flags should be defined.
+#
+DEFS= -DUSELIBKVM -DDEBUG -DSTREAM -DREFCLOCK -DNO_SIGNED_CHAR_DECL -DPPS -DPPSDEV -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+# Define -DWWVBPPS for PPS support via the WWVB receiver; also,
+# define -DPPSCD in the DEFS above. This requires the ppsclock
+# streams module under SunOS 4.2.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver; also,
+# devine -DPPS in the DEFS above.
+#
+# Define -DMX4200 to support a Magnavox 4200 GPS receiver. Define -DPPSCD
+# in the DEFS above for PPS support via this receiver. This requires
+# the ppsclock streams module under SunOS 4.2.
+#
+# Define -DAS2201 to include support for the Austron 2201 GPS Timing
+# Receiver. Define -DPPSCD in the DEFS above for PPS support via this
+# receiver. This requires the ppsclock streams module under SunOS 4.2.
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver. This
+# driver may work with other True-Time products as well.
+#
+# Define -DOMEGA to support a Kinemetrics TrueTime OM-DC OMEGA receiver.
+#
+# Define -DTPRO to support a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the Sun interface driver available from KSI.
+#
+# Define -DLEITCH to support a Leitch CSD 5300 Master Clock System Driver
+# for the HP 5061B Cesium Clock.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DWWVBPPS -DCHU -DDCF -DMX4200 -DAS2201 -DGOES -DOMEGA -DTPRO -DLEITCH -DIRIG
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS= -lkvm
+#DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -pipe -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc -pipe
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.HP-UX b/usr.sbin/xntpd/conf/Config.HP-UX
new file mode 100644
index 0000000..ef4fa30
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.HP-UX
@@ -0,0 +1,7 @@
+#
+# Test suite for HPUX 9 (no multicast, kernel mods, disciplines, modem control)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK
+CLOCKDEFS= -DATOM -DAS2201 -DCHU -DGOES -DGPSTM -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.MONOMOY b/usr.sbin/xntpd/conf/Config.MONOMOY
new file mode 100644
index 0000000..18dddff
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.MONOMOY
@@ -0,0 +1,186 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.MONOMOY,v 3.1 1993/07/06 01:03:43 jbj Exp
+
+# Config.bsdi by Bdale Garbee, N3EUA, bdale@gag.com
+#
+# Tested with the BSDI BSD/386 0.9.3 "gamma 4" revision. It should
+# work fine with this or later revs of BSD/386.
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DXNTP_LITTLE_ENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# Define -DPPS to include support for a 1-pps signal. Define -DPPSDEV
+# to include a device driver for it. The latter requires a
+# serial port and either a line discipline or STREAMS module.
+# The PPS signal may also be generated via a reference clock
+# module like DCF77. In that case a special define is required for
+# the reference clock module (only one source of PPS signal should
+# be used)
+#
+DEFS= -DBSDI -DUSELIBKVM -DDEBUG -DREFCLOCK -DPPS -DCONFIG_FILE=\\"/usr/local/etc/xntp.conf\\" -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver
+# (see also: -DPPS)
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DCHU -DGOES # -DMX4200 -DAS2201
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS= -lkvm
+#DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -pipe -Wall -g -O -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc -pipe -g
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.OSF1 b/usr.sbin/xntpd/conf/Config.OSF1
new file mode 100644
index 0000000..f460e9f
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.OSF1
@@ -0,0 +1,7 @@
+#
+# Test suite for DEC OSF/1 V1.x (no disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.SunOS b/usr.sbin/xntpd/conf/Config.SunOS
new file mode 100644
index 0000000..42fd1a5
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.SunOS
@@ -0,0 +1,7 @@
+#
+# Test suite for SunOS 4.1.x (kitchen sink)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DMCAST -DKERNEL_PLL -DCLK -DCHU -DPPS
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.TIGER b/usr.sbin/xntpd/conf/Config.TIGER
new file mode 100644
index 0000000..29c6cbd
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.TIGER
@@ -0,0 +1,182 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.TIGER,v 3.1 1993/07/06 01:03:45 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DXNTP_LITTLE_ENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# Define -DPPS to include support for a 1-pps signal. Define -DPPSDEV
+# to include a device driver for it. The latter requires a
+# serial port and either a line discipline or STREAMS module.
+# The PPS signal may also be generated via a reference clock
+# module like DCF77. In that case a special define is required for
+# the reference clock module (only one source of PPS signal should
+# be used)
+#
+DEFS= -DREFCLOCK -DS_CHAR_DEFINED -DREADKMEM -DDEBUG -DPLL -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver
+# (see also: -DPPS)
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DGOES -DCHU
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+#DAEMONLIBS= -lkvm
+DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.TRURO b/usr.sbin/xntpd/conf/Config.TRURO
new file mode 100644
index 0000000..2fc2580
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.TRURO
@@ -0,0 +1,202 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.TRURO,v 3.1 1993/07/06 01:03:46 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DWORDS_BIGENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# For some machines, settimeofday does not set the sub-second component
+# of the time correctly. For these machines add -DSETTIMEOFDAY_BROKEN.
+# If xntpd keeps STEPPING the clock by small amounts, then it is
+# possible that you are suffering from this problem.
+#
+# There are four ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns, if they are not running Solaris,
+# and recent BSD should use -DUSELIBKVM; others should use
+# -DREADKMEM. Soalris 2.1 should use -DSOLARIS.
+# If -DUSELIBKVM, use the DAEMONLIBS below to get the
+# kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# Under Solaris 2.1, you must use -DSOLARIS and -DSLEWALWAYS.
+# Don't define USELIBKVM, NOKMEM or READKMEM.
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# There are three ways to utilize external 1-pps signals. Define -DPPS to
+# include just the pps routine, such as used by the DCF77 reference clock
+# driver. Define -DPPSDEV ito include a serial device driver. This
+# requires a serial port and either a line discipline or STREAMS module.
+# Define -DPPSCD to include the driver and a special kernal hack
+# (for SunOS 4.1.1) that intercepts carrier-detect transitions
+# generated by the pps signal. Only one of these flags should be defined.
+#
+DEFS= -DDEBUG -DSTREAM -DREFCLOCK -DNO_SIGNED_CHAR_DECL -DSLEWALWAYS -DSOLARIS -DPPS -DSTUPID_SIGNAL -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+# Define -DWWVBPPS for PPS support via the WWVB receiver; also,
+# define -DPPSCD in the DEFS above. This requires the ppsclock
+# streams module under SunOS 4.2.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver; also,
+# devine -DPPS in the DEFS above.
+#
+# Define -DMX4200 to support a Magnavox 4200 GPS receiver. Define -DPPSCD
+# in the DEFS above for PPS support via this receiver. This requires
+# the ppsclock streams module under SunOS 4.2.
+#
+# Define -DAS2201 to include support for the Austron 2201 GPS Timing
+# Receiver. Define -DPPSCD in the DEFS above for PPS support via this
+# receiver. This requires the ppsclock streams module under SunOS 4.2.
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver. This
+# driver may work with other True-Time products as well.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DWWVBPPS -DGOES -DCHU -DMX4200 -DAS2201 -DOMEGA -DTPRO -DLEITCH -DIRIG
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB= -lsocket -lnsl -lelf
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+#COMPILER= gcc -traditional
+COMPILER= gcc -pipe -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.ULTRIX b/usr.sbin/xntpd/conf/Config.ULTRIX
new file mode 100644
index 0000000..4ead1be
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.ULTRIX
@@ -0,0 +1,7 @@
+#
+# Test suite for Ultrix 4.x (no disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.VAX b/usr.sbin/xntpd/conf/Config.VAX
new file mode 100644
index 0000000..66b9f91
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.VAX
@@ -0,0 +1,7 @@
+#
+# Test suite for 4.3bsd VAX tahoe (no multicast, kernel mods, disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.dartnet b/usr.sbin/xntpd/conf/Config.dartnet
new file mode 100644
index 0000000..b591db3
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.dartnet
@@ -0,0 +1,187 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" or "make Config.local.local" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DPPSPPS -DREFCLOCK -DKERNEL_PLL
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS=-DAS2201PPS -DCHU -DGOES -DIRIG -DLEITCH -DLOCAL_CLOCK -DMX4200PPS -DOMEGA -DPSTCLK -DTPRO -DWWVBCLK
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.local b/usr.sbin/xntpd/conf/Config.local
new file mode 100644
index 0000000..22c12a3
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.local
@@ -0,0 +1,190 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DAS2201PPS -DCHUPPS -DGOES -DIRIG -DMX4200PPS -DOMEGA -DPSTCLK -DTPRO -DWWVBCLK -DLEITCH
+
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.plain b/usr.sbin/xntpd/conf/Config.plain
new file mode 100644
index 0000000..67dd70a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.plain
@@ -0,0 +1,190 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DCHU -DGOES -DOMEGA -DPST -DWWVB -DLEITCH
+
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.solaris b/usr.sbin/xntpd/conf/Config.solaris
new file mode 100644
index 0000000..5db3cd0
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.solaris
@@ -0,0 +1,7 @@
+#
+# Test suite for SunOS 5.x (no kernel mods, disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.svr4 b/usr.sbin/xntpd/conf/Config.svr4
new file mode 100644
index 0000000..d6d0661
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.svr4
@@ -0,0 +1,167 @@
+#
+# This is the local configure file. Modify it to fit your particular
+# configuration.
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry, set the
+# alternate defines as shown.
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Ordinarily, the correct define is sniffed by the "make makeconfig"
+# script and automatically included.
+#
+# The flag KERNEL_PLL is a temporary hack to use when the phase-lock loop
+# is implmented in the kernel. Do not use unless you have modified
+# kernel routines (see doc/README.kern).
+#
+#DEFS_LOCAL= -DDEBUG -DPPSPPS -DKERNEL_PLL
+DEFS_LOCAL= -DDEBUG
+#DEFS_LOCAL= # for greenhorns
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./kernel directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define PARSESTREAM for utilising the STREAMS module for improved
+# precision (currently only SunOS4.x)
+#
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSF for a EES M201 MSF receiver. It should work in all systems
+# with a serial port. The driver does not support the CLK mode, but does
+# support the # PPS mode.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG deleted, all of the rest compile under
+# Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+#CLOCKDEFS= -DAS2201PPS -DCHU -DGOES -DIRIG -DMX4200PPS -DOMEGA -DPST -DPSTCLK -DTPRO -DWWVBCLK
+CLOCKDEFS= # for greenhorns
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/etc
diff --git a/usr.sbin/xntpd/conf/README b/usr.sbin/xntpd/conf/README
new file mode 100644
index 0000000..8d07591
--- /dev/null
+++ b/usr.sbin/xntpd/conf/README
@@ -0,0 +1,11 @@
+README file for directory ./conf of the NTP Version 3 distribution
+
+This directory contains example run-time configuration files for the
+NTP Version 3 daemon xntpd. These files illustrate some of the more
+obtuse configurations you may run into. They are not likely to do
+anything good if run on machines other than their native spot, so don't
+just blindly copy something and put it up. Additional information can
+be found in the ./doc directory of the base directory.
+
+See the Config.local.dist file in the base directory for an explanation
+of the defines used.
diff --git a/usr.sbin/xntpd/conf/baldwin.conf b/usr.sbin/xntpd/conf/baldwin.conf
new file mode 100644
index 0000000..baaac95
--- /dev/null
+++ b/usr.sbin/xntpd/conf/baldwin.conf
@@ -0,0 +1,40 @@
+#
+# NTP configuration file (ntp.conf)
+# baldwin.udel.edu (128.4.1.24)
+#
+# This illustrates the use of an external clock with the local clock
+# driver, as well as a multicast server. The prefer keyword on the
+# local clock driver declares an external clock and that the time of
+# this server should not be wiggled by an NTP peer, unless the
+# external clock comes unstuck. Note the use of the multicast group
+# ID assigned to NTP, 224.0.1.1, which identifies this as a multicast
+# server rather than a broadcast one. The other NTP peers are known
+# stratum-1 chimes intended as backup should the external clock croak.
+#
+peer 127.127.1.0 prefer # KSI/Odetics TPRO IRIG interface
+fudge 127.127.1.0 stratum 0 refid GPS
+broadcast 224.0.1.1 key 6 ttl 127
+peer 128.4.1.1 # rackety.udel.edu (Sun4c/40 IPC)
+peer 128.4.1.4 # barnstable.udel.edu (Sun4c/65 SS1+)
+peer 128.4.1.2 # mizbeaver.udel.edu (Bancomm bc700LAN)
+peer 128.4.1.20 # pogo.udel.edu (Sun4c/65 SS1+)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /baldwin/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/dewey.conf b/usr.sbin/xntpd/conf/dewey.conf
new file mode 100644
index 0000000..2a7548c
--- /dev/null
+++ b/usr.sbin/xntpd/conf/dewey.conf
@@ -0,0 +1,46 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# Generic configuration file for UDel NTP stratum-2 time servers. Don't
+# forget each server should have a /etc/ntp.drift and /etc/ntp.keys file.
+#
+# Stratum-1 peers. Each server should chime two different stratum-1
+# servers from the following list. Each stratum-1 server should be used
+# only once.
+#
+#peer 128.8.10.1 # umd1.umd.edu
+#peer 18.72.0.3 version 2 # bitsy.mit.edu
+peer 132.249.16.1 # fuzz.sdsc.edu
+peer 128.118.46.3 version 2 # otc1.psu.edu
+#peer 128.9.2.129 # wwvb.isi.edu
+#peer 130.43.2.2 version 2 # apple.com
+#peer 16.1.0.22 # clepsydra.dec.com
+#peer 130.105.1.156 version 2 # clock.osf.orga
+#peer 128.96.60.5 version 2 # pi.bellcore.com
+#peer 128.4.1.1 # rackety.udel.edu
+#peer 129.116.3.5 # shorty.chpc.utexas.edu
+#
+# Stratum-2 peers. Each server should chime all of the others in this
+# list except itself.
+#
+peer 128.175.1.1 # huey.udel.edu (VAX)
+#peer 128.175.1.2 # dewey.udel.edu (VAX)
+peer 128.175.1.3 # louie.udel.edu (SPARC)
+peer 128.175.2.15 # snow-white.ee.udel.edu (SPARC)
+peer 128.175.7.4 # sol.cis.udel.edu (SPARC)
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+#
+# Authentication stuff. Note the different authentication delay on
+# VAX and SPARC.
+#
+authenticate yes # enable authentication
+keys /etc/ntp.keys # path for key file
+trustedkey 1 2 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.001501 # authentication delay (VAX)
+#authdelay 0.000073 # authentication delay (SPARC)
diff --git a/usr.sbin/xntpd/conf/grundoon.conf b/usr.sbin/xntpd/conf/grundoon.conf
new file mode 100644
index 0000000..16003d5
--- /dev/null
+++ b/usr.sbin/xntpd/conf/grundoon.conf
@@ -0,0 +1,157 @@
+#
+# NTP configuration file (ntp.conf)
+# grundoon.udel.edu (128.4.2.7)
+#
+# This machine can best be described as the kitchen sink. It has, in
+# addition to the baseboard tty ports ttya and ttyb, an 8-line
+# Serial/Parallel Interface (SPIF) with ports ttyz00 through ttyz07. The
+# configuration includes the following drivers, clock addresses and Unix
+# device names.
+#
+# Local Clock 127.127.1.0 /dev/audio
+# PST 1020 WWV/WWVH Receiver 127.127.3.1 /dev/pst1
+# Spectracom 8170 WWVB Receiver 127.127.4.1 /dev/wwvb1
+# IRIG Audio Decoder 127.127.6.0 /dev/audio
+# Scratchbuilt CHU Receiver 127.127.7.1 /dev/chu1
+# NIST ACTS modem 127.127.18.1 /dev/acts1
+# Heath GC-1000 WWV Receiver 127.127.19.1 /dev/pst1
+# PPS Clock 127.127.22.1 none
+#
+# This machine has the kernel modifications described in the README.kern
+# file, as well as the tty_clk, tty_chu and ppsclock streams modules.
+#
+# Spectracom 8170/Netclock-2 WWVB receiver. This receiver is equipped
+# with a 1-pps and IRIG outputs. The 1-pps signal is connected via the
+# ppsclock streams module and the carrier detect line of the CHU
+# receiver below (ttyb). The IRIG signal is connected via an attenuator
+# to the audio port (/dev/audio). The propagation delay computed from
+# geographical coordinates is 8.8 ms, while the receiver delay
+# calibrated at the factory is 17.3 ms, for a total delay of 26.1 ms.
+# This is confirmed within 0.1 ms at the 1-pps signal output using a
+# portable cesium clock. We add a fudge time1 of 3.5 ms so the driver
+# time agrees with the 1-pps signal to within 1 ms. The fudge flag4 is
+# set to cause the receiver to dump the quality table once each day to
+# the clockstats file.
+
+#
+#server 127.127.4.1 # /dev/wwvb1 -> /dev/ttyz03
+#fudge 127.127.4.1 time1 0.0035 flag4 1
+#
+# IRIG Audio Decoder. The IRGI signal of the Spectracom WWVB receiver is
+# connected to the audio codec via a resistor attenuator. We add a fudge
+# time1 of 3.5 ms so the driver agrees with the calibrated 1-pps signal
+# to within 0.1 ms. We also specify a reference ID of WWVB to indicate
+# the signal origin. Note the prefer keyword in the server line, which
+# favors this driver over all others that survive the clock selection
+# algorithm. See README.refclock for further insight on this feature.
+#
+server 127.127.6.0 prefer # /dev/audio
+fudge 127.127.6.0 time1 0.0005 refid WWVB
+
+#
+# PST/Traconex 1020 WWV/WWVH Receier. The internal DIPswitches are set
+# as near as possible to the delays to WWV (8.8 ms) and WWVH (28.1 ms),
+# as computed from geographical coordinates. We add a fudge time1 of 5.9
+# ms so the driver time agrees with the 1-pps signal to within 1 ms for
+# WWV. We also set the stratum to 1, so this receiver will not normally
+# be selected, unless the primary WWVB receiver comes unstuck.
+#
+server 127.127.3.1 # /dev/pst1 -> ttyz05
+fudge 127.127.3.1 time1 0.0059 stratum 1
+
+#
+# Scratchbuilt CHU Receiver. The audio signal from a computer controlled
+# CHU receiver is connected to a gadget box, which contains a 103A modem
+# chip and level converter operating at 300 bps. The propagation delay
+# computed from geographical coordinates is 3.0 ms, which is the value
+# of the fudge time1 parameter. We add a fudge time2 of 9.9 ms so that
+# the driver time agrees with the 1-pps signal to within a few ms,
+# ordinarily the best possible with this receiver. The fudge flag3 is
+# set because the 1-pps signal happens to be connected vit the carrier
+# detect line on this port (ttyb). We also set the stratum to 1, so this
+# receiver will not normally be selected, unless the primary WWVB
+# receiver comes unstuck.
+#
+server 127.127.7.1 # /dev/chu1 -> /dev/ttyb
+fudge 127.127.7.1 time1 0.0030 time2 0.0099 flag3 1 stratum 1
+
+#
+# NIST Automated Computer Time Service. This driver calls a special
+# telephone number in Boulder, CO, to fetch the time directly from the
+# NIST cesium farm. The details of the complicated calling program are
+# in the README.refclock file. The Practical Peripherals 9600SA modem
+# does not work correctly with the ACTS echo-delay scheme for
+# automatically calculating the propagation delay, so the fudge flag2 is
+# set to disable the feature. Instead, we add a fudge time1 of 65.0 ms
+# so that the driver time agrees with th e1-pps signal to within 1 ms.
+# The phone command specifies three alternate telephone numbers,
+# including AT modem command prefix, which will be tried one after the
+# other at each measurement attempt. In this case, a cron job is used to
+# set fudge flag1, causing a measurement attempt, every six hours.
+#
+server 127.127.18.1 # /dev/acts1 -> /dev/ttyz00
+fudge 127.127.18.1 time1 0.0650 flag2 1
+phone atdt13034944774 atdt13034944785 atdt13034944774
+
+#
+# Heath GC-1000 Most Accurate Clock. This is a WWV receiver with a
+# claimed accuracy better than 100 ms under "hi spec" conditions, but
+# such conditions are not frequent. The propagation delay DIPswitchs are
+# set to 9 ms, as close as possible to the 8.8 ms computed from
+# geographical coordinates. We add a fudge time2 of 40.0 ms so that the
+# driver time agrees with the 1-pps signal to within 50 ms, ordinarily
+# the best possible with this receiver. We also set the stratum to 1, so
+# this receiver will not normally be selected, unless the primary WWVB
+# receiver comes unstuck.
+#
+server 127.127.19.1 # /dev/heath1 -> ttyz07
+fudge 127.127.19.1 time1 0.040 stratum 1
+
+#
+# Undisciplined Local Clock. This is a fake driver intended for backup
+# and when no outside source of synchronized time is available. The
+# default stratum is usually 3, but in this case we elect to use stratum
+# 0. Since the server line does not have the prefer keyword, this driver
+# is never used for synchronization, unless no other other
+# synchronization source is available. In case the local host is
+# controlled by some external source, such as an external oscillator or
+# another protocol, the prefer keyword would cause the local host to
+# disregard all other synchronization sources, unless the kernel
+# modifications are in use and declare an unsynchronized condition.
+#
+server 127.127.1.0 # local clock
+fudge 127.127.1.0 stratum 0
+
+#
+# PPS Clock. This driver is used to capture a 1-pps signal when the PPS
+# kernel modifications are not in use. It can be configured for the
+# tty_clk or ppsclock streams module or no module at all, assuming the
+# RS232 connector is properly wired. Normally, the 1-pps signal is
+# generated by a radio clock, in this cast the Spectracom clock
+# 127.127.4.1 also configured for this host. When used this way, the
+# associated radio clock normally has the prefer keyword in the serve
+# command line. The PPS driver then will be selected only if the prefer
+# peer is operating within nominal error bounds. See the README.refclock
+# file for further details.
+#
+#server 127.127.22.1 # pps clock
+
+#
+# Miscellaneous stuff. We enable authentication in order to prevent
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /grundoon/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/ntp.keys # path for keys file
+trustedkey 1 2 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/maccarony.conf b/usr.sbin/xntpd/conf/maccarony.conf
new file mode 100644
index 0000000..6bd25c7
--- /dev/null
+++ b/usr.sbin/xntpd/conf/maccarony.conf
@@ -0,0 +1,33 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# This illustrates a multicast client. All that is really needed
+# here is the multicastclient command and the authentication stuff.
+# If the monitoring option and filgen statistics were not needed,
+# this could be done without a configuration file by including the
+# following snip in the rc.local startup file or equivalent:
+#
+#if [ -f /usr/local/bin/xntpd ]; then
+# /usr/local/bin/xntpd -m -a -k/usr/local/bin/ntp.keys -t3
+#fi
+#
+multicastclient # listen on default 224.0.1.1
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /malarky/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for key file
+trustedkey 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
+
diff --git a/usr.sbin/xntpd/conf/malarky.conf b/usr.sbin/xntpd/conf/malarky.conf
new file mode 100644
index 0000000..9b1d7b2
--- /dev/null
+++ b/usr.sbin/xntpd/conf/malarky.conf
@@ -0,0 +1,27 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# This is for a broadcast/multicast client. Except for the statistics
+# stuff, this can be done with only a commmand line of the form
+#
+# /usr/local/bin/xntpd -a -k /usr/local/bin/ntp.keys -m -t 3
+#
+multicastclient # listen on default 224.0.1.1
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /malarky/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for key file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 14 # key (7) for accessing server variables
+controlkey 14 # key (6) for accessing server variables
+authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
diff --git a/usr.sbin/xntpd/conf/ntp.conf.dcf77 b/usr.sbin/xntpd/conf/ntp.conf.dcf77
new file mode 100644
index 0000000..678d719
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.dcf77
@@ -0,0 +1,19 @@
+#
+# XNTP configuration file (/etc/ntp.conf)
+#
+
+#
+# Server is a Boeder DCF77 receiver
+#
+# Use:
+# 127.127.8.40 for /dev/refclock-0 (/dev/ttyd0)
+# 127.127.8.41 for /dev/refclock-1 (/dev/ttyd1)
+# 127.127.8.42 for /dev/refclock-2 (/dev/ttyd2)
+# 127.127.8.43 for /dev/refclock-3 (/dev/ttyd3)
+#
+server 127.127.8.40
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
diff --git a/usr.sbin/xntpd/conf/ntp.conf.gw b/usr.sbin/xntpd/conf/ntp.conf.gw
new file mode 100644
index 0000000..bd56878
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.gw
@@ -0,0 +1,34 @@
+#
+# peers for gw.ccie.utoronto.ca (128.100.63.2, 128.100.49.104, 128.100.224.224)
+#
+peer 128.4.0.1 key 1 # dcn1.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 128.116.64.3 key 3 # ncarfuzz.ucar.edu
+peer 128.9.2.129 key 4 # wwvb.isi.edu
+#peer 128.4.0.6 key 1 # dcn6.udel.edu
+#
+# Don't configure associations with the other secondaries. This is
+# the only one in a machine room and will hold itself pretty stable
+# when all else fails
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23 24
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.ipl b/usr.sbin/xntpd/conf/ntp.conf.ipl
new file mode 100644
index 0000000..1fd5b7d
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.ipl
@@ -0,0 +1,32 @@
+#
+# peers for ipl.utcs.utoronto.ca (128.100.102.7)
+#
+peer 128.4.0.5 key 1 # dcn5.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 192.12.207.1 key 3 # fuzz.sdsc.edu
+peer 128.9.2.129 key 4 # wwvb.isi.edu
+peer 128.100.63.2 key 21 # gw.ccie
+peer 128.100.49.105 key 22 # suzuki.ccie
+peer 128.100.102.4 key 23 # shiningtree.utcs
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.nsf b/usr.sbin/xntpd/conf/ntp.conf.nsf
new file mode 100644
index 0000000..298bb7a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.nsf
@@ -0,0 +1,156 @@
+#
+# Maybe an alternate xntpd configuration for NSS#17
+#
+
+#
+# precision is supported, but you don't really need it. The code
+# will determine a precision from the kernel's value of _hz which
+# is fine. Note you shouldn't claim too good a precision on a
+# Unix machine even if the clock carries a lot of bits, since
+# precision also depends on things like I/O delays and scheduling
+# latencies, which Unix machines control poorly. If you claim better
+# than -6 or -7 it will make the anti-hop aperture tighter than is
+# reasonable for a Unix machine.
+#
+#precision -7
+
+#
+# peers are ncarfuzz.ucar.edu umd1.umd.edu dcn5.udel.edu fuzz.sdsc.edu
+# syntax is peer addr [ key 1-15 ] [ version 1_or_2 ]
+#
+
+peer 128.116.64.3 # ncarfuzz.ucar.edu
+peer 128.8.10.1 # umd1.umd.edu
+peer 128.4.0.5 # dcn5.udel.edu
+peer 192.12.207.1 # fuzz.sdsc.edu
+
+#
+# Drift file. Put this in a directory which the daemon can write to.
+# No symbolic links allowed, either, since the daemon updates the file
+# by creating a temporary in the same directory and then rename()'ing
+# it to the file.
+#
+# This is a nice feature. Once you've got the drift computed it hardly
+# ever takes more than an hour or so to resync after a restart.
+#
+driftfile /etc/ntp.drift
+
+#
+# The server statement causes polling to be done in client mode rather
+# than symmetric active. It is an alternative to the peer command
+# above. Which you use depends on what you want to achieve. Usually
+# it doesn't matter. Syntax is:
+#
+#server 128.100.49.1 key 4 version 1
+
+#
+# The broadcast statement tells it to start broadcasting time out one
+# of its interfaces. Syntax is
+#
+#broadcast 128.100.49.255 # [ key n ] [ version n ]
+
+#
+# broadcastclient tells the daemon whether it should attempt to sync
+# to broadcasts or not. Defaults to `no'.
+#
+#broadcastclient yes # or no
+
+#
+# broadcastdelay configures in a default round-trip delay to use for
+# broadcast time. It may poll to improve this estimate.
+#
+#broadcastdelay 0.0095 # in seconds
+
+#
+# authenticate configures us into strict authentication mode (or not).
+#
+#authenticate yes # or no. Default is no
+
+#
+# authdelay is the time it takes to do an NTP encryption on this host.
+# The current routine is pretty fast.
+#
+#authdelay 0.000340 # in seconds
+
+#
+# trustedkey are used when authenticate is on. We only trust (and sync to)
+# peers who know these keys.
+#
+#trustedkey 1 3 4 8
+
+#
+# monitor turns on the monitoring facility. See xntpdc's monlist command.
+# This shows a lot of neat stuff, but I'm not fussy about the implementation.
+# Uses up to 20Kb of memory at run time. You could try this.
+#
+#monitor yes # or no. Default is no
+
+#
+# keys points at the file which holds the authentication keys.
+#
+#keys /etc/ntp.keys
+
+#
+# requestkey indicates which key is to be used for validating
+# runtime reconfiguration requests. If this isn't defined, or the
+# key isn't in the keys file, you can't do runtime reconfiguration.
+# controlkey indicates which key is to be used for validating
+# mode 6 write variables commands. If this isn't defined you can't
+# do it. The only thing the latter is used for is to set leap second
+# warnings on machines with radio clocks.
+#
+#requestkey 65535
+#controlkey 65534
+
+#
+# restrict places restrictions on the punters. This is implemented as
+# a sorted address-and-mask list, with each entry including a set of
+# flags which define what a host matching the entry *can't* do (the sort
+# also saves CPU time searching the table since it needn't be searched
+# to the end). The last match in the table defines what the host does.
+# The default entry, which everyone matches, is first, most specific
+# matches are later in the table. The flags are:
+#
+# ignore - ignore all traffic from host
+# noserve - don't give host any time (but let him make queries?)
+# notrust - give host time, let him make queries, but don't sync to him
+# noquery - host can have time, but not make queries
+# nomodify - allow the host to make queries except those which are
+# actually run-time configuration commands.
+# notrap - don't allow matching hosts to set traps. If noquery is
+# set this isn't needed
+# lowpriotrap - if this guy sets a trap make it easy to delete
+# ntpport - a different kind of flag. Makes matches for this entry
+# possible only if the source port is 123.
+#
+# To understand this better, take a look at xntpdc's reslist command when the
+# server is running. This usually prints in the sorted order.
+#
+# This should match the NSS 17 stuff. Default mask is all ones.
+
+restrict default ignore # ignore almost everyone
+
+#
+# These guys can be served time and make non-modifying queries
+#
+restrict 129.140.0.0 mask 255.255.0.0 notrust nomodify
+restrict 35.1.1.42 notrust nomodify
+
+#
+# Rest of 35.1.1 gets to look but not touch
+#
+restrict 35.1.1.0 mask 255.255.255.0 noserve nomodify
+
+#
+# modifications can be made from local NSS only
+#
+restrict 129.140.17.0 mask 255.255.255.0 notrust
+restrict 127.0.0.1 notrust
+
+#
+# take time from the following peers, but don't let them peek or modify
+#
+restrict 128.116.64.3 noquery
+restrict 128.8.10.1 noquery
+restrict 128.4.0.5 noquery
+restrict 192.12.207.1 noquery
diff --git a/usr.sbin/xntpd/conf/ntp.conf.shiningtree b/usr.sbin/xntpd/conf/ntp.conf.shiningtree
new file mode 100644
index 0000000..1576ebb
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.shiningtree
@@ -0,0 +1,32 @@
+#
+# peers for shiningtree.utcs.utoronto.ca (128.100.102.4)
+#
+peer 128.4.0.1 key 1 # dcn1.udel.edu
+peer 130.126.174.40 key 2 # truechimer.cso.uiuc.edu
+peer 192.12.207.1 key 3 # fuzz.sdsc.edu
+peer 128.116.64.3 key 4 # ncarfuzz.ucar.edu
+peer 128.100.63.2 key 21 # gw.ccie
+peer 128.100.49.105 key 22 # suzuki.ccie
+peer 128.100.102.7 key 23 # ipl.utcs
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.suzuki b/usr.sbin/xntpd/conf/ntp.conf.suzuki
new file mode 100644
index 0000000..ee32e7a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.suzuki
@@ -0,0 +1,43 @@
+#
+# peers for suzuki.ccie.utoronto.ca (128.100.49.105, 128.100.224.225)
+#
+
+#
+# the reference clock, /dev/chu1
+#
+server 127.127.7.1 key 4
+# Propagation delay 2.5 ms, sloppy clock flag on
+fudge 127.127.7.1 time1 0.0025 flag1 1
+
+peer 128.4.0.5 key 1 # dcn5.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 128.116.64.34 key 3 # ncarfuzz.ucar.edu
+peer 130.126.174.40 key 4 # truechimer.cso.uiuc.edu
+peer 128.100.49.104 key 24 # gw.ccie
+peer 128.100.102.4 key 22 # shiningtree.utcs
+peer 128.100.102.7 key 22 # ipl.utcs
+
+peer 128.4.0.6 key 1 # dcn6.udel.edu
+
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23 24
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/pogo.conf b/usr.sbin/xntpd/conf/pogo.conf
new file mode 100644
index 0000000..e557e44
--- /dev/null
+++ b/usr.sbin/xntpd/conf/pogo.conf
@@ -0,0 +1,34 @@
+#
+# NTP configuration file (ntp.conf)
+# pogo.udel.edu (128.4.1.20)
+#
+server 127.127.10.1 prefer # austron 2201A gps receiver
+peer 128.4.1.1 # rackety.udel.edu (Sun4c/40 IPC)
+peer 128.4.1.2 # mizbeaver.udel.edu (Bancomm bc700LAN)
+peer 128.4.1.4 # barnstable.udel.edu (Sun4c/65 SS1+)
+peer 128.4.1.5 maxpoll 8 # churchy.udel.edu (cisco IGS router)
+peer 132.163.135.130 maxpoll 8 # time_A.timefreq.bldrdoc.gov (Cesium)
+peer 131.188.1.40 maxpoll 8 # ntps1-0.uni-erlangen.de (DCF77)
+peer 129.132.2.21 maxpoll 8 # swisstime.ethz.ch (DCF77)
+peer 130.155.98.13 maxpoll 8 # terss.ml.csiro.au (Cesium)
+peer 192.36.143.150 maxpoll 8 # Time1.Stupi.SE (Cesium)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+precision -18 # clock reading precision (usec)
+driftfile /etc/ntp.drift # path for drift file
+statsdir /pogo/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000159 # authentication delay (SPARC4c/65 SS1+ MD5)
diff --git a/usr.sbin/xntpd/conf/rackety.conf b/usr.sbin/xntpd/conf/rackety.conf
new file mode 100644
index 0000000..48389dc
--- /dev/null
+++ b/usr.sbin/xntpd/conf/rackety.conf
@@ -0,0 +1,69 @@
+#
+# NTP configuration file (ntp.conf)
+# rackety (128.4.1.1)
+#
+server 127.127.10.1 prefer # austron 2201A gps receiver
+fudge 127.127.10.1 flag4 1 # enable statistics
+server 127.127.4.1 # spectracom 8170/netclock-2 wwvb receiver
+# propagation delay: wwvb 0.0088, receiver delay 0.0173, os delay .0035
+fudge 127.127.4.1 time1 0.0035 stratum 1 flag4 1
+
+#
+# ee vaxen
+#
+peer 128.175.1.1 # huey.udel.edu
+peer 128.175.1.2 # louie.udel.edu
+peer 128.175.1.3 # dewey.udel.edu
+
+#
+# munchkins (stratum-1 only)
+#
+broadcast 224.0.1.1 key 5 ttl 127 # multicast
+broadcast 128.4.1.0 key 3 # local broadcast
+peer 128.4.1.2 # mizbeaver.udel.edu
+peer 128.4.1.4 # barnstable.udel.edu
+peer 128.4.1.20 # pogo.udel.edu
+
+#
+# dartnet
+#
+peer 140.173.112.2 # ames.dart.net
+peer 140.173.128.1 # la.dart.net
+peer 140.173.64.1 # dc.dart.net
+peer 140.173.144.2 # parc.dart.net
+peer 140.173.80.1 # sri.dart.net
+peer 140.173.96.1 # lbl.dart.net
+peer 140.173.128.2 # isi.dart.net
+peer 140.173.16.1 # udel.dart.net
+peer 140.173.32.1 # bbn.dart.net
+peer 140.173.48.2 # mit.dart.net
+
+#
+# nsfnet t3 backbone
+#
+server 140.222.134.1 version 2 # enss134 (cambridge - mit)
+server 140.222.135.1 version 2 # enss135 (san diego - sdsc)
+peer 140.222.136.1 version 2 # enss136 (college park - sura)
+server 140.222.141.1 version 2 # enss141 (boulder - ncar)
+server 140.222.144.1 version 2 # enss144 (sunnyvale - nasa ames)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+precision -18 # clock reading precision (usec)
+driftfile /etc/ntp.drift # path for drift file
+statsdir /rackety/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 14 # key (7) for accessing server variables
+controlkey 14 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/snow-white.conf b/usr.sbin/xntpd/conf/snow-white.conf
new file mode 100644
index 0000000..a86cb4b
--- /dev/null
+++ b/usr.sbin/xntpd/conf/snow-white.conf
@@ -0,0 +1,33 @@
+#
+# NTP configuration file (ntp.conf)
+# snow-white.udel.edu (128.175.2.15)
+#
+# Stratum-2 peers
+#
+peer 128.175.1.1 # huey.udel.edu
+peer 128.175.1.2 # dewey.udel.edu
+#peer 128.175.1.3 # louie.udel.edu
+peer 128.175.2.33 # louie.udel.edu
+#peer 128.175.7.39 # louie.udel.edu
+#
+# Stratum-3 peers
+#
+peer 128.175.7.4 # sol.cis.udel.edu
+peer 128.175.7.18 # ra.cis.udel.edu
+#peer 128.175.2.15 # snow-white.ee.udel.edu
+peer 128.175.2.21 # opus.ee.udel.edu
+#
+# Miscellaneous stuff
+#
+monitor yes # enable monitoring
+precision -18 # clock reading precision (1 usec)
+driftfile /etc/ntp.drift # path for drift file
+#
+# Authentication stuff
+#
+authenticate yes # enable authentication
+keys /etc/ntp.keys # path for key file
+trustedkey 1 2 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000077 # authentication delay (SPARC IPC)
diff --git a/usr.sbin/xntpd/doc/README.irig b/usr.sbin/xntpd/doc/README.irig
new file mode 100644
index 0000000..f293f4c
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.irig
@@ -0,0 +1,306 @@
+ Audio IRIG Receiver for Precision Timekeeping
+
+ Revised 20 September 1993
+
+Note: This information file is included in both the BSD audio driver
+distribution (bsd_audio.tar.Z) and NTP Version 3 distribution
+(xntp3.tar.Z) as the file README.irig. Both distributions can be
+obtained via anonymous ftp from louie.udel.edu in the directory pub/ntp.
+
+1. Introduction
+
+This software distribution includes modifications to the BSD audio
+driver for the Sun SPARCstation written by Van Jacobson and
+collaborators at Lawrence Berkeley National Laboratory. The
+modifications provide for the connection of a standard Inter-Range
+Instrumentation Group (IRIG) timecode signal generator and the decoding
+of the signal to produce data sufficient to synchronize a host clock to
+the IRIG signal. There are several timing receivers now on the market
+that can produce IRIG signals, including those made by Austron,
+TrueTime, Odetics and Spectracom, among others. These data can be used
+to precisely synchronize the host computer clock to within a few
+microseconds without requiring level converters or pulse generators
+necessary with the one-pulse-per-second signals also produced by these
+receivers. The current implementation of the Network Time Protocol
+Version 3 supports the modified BSD driver when installed in the SunOS
+4.1.x kernel.
+
+The specific IRIG signal format supported by the driver is designated
+IRIG-B. It consists of an amplitude-modulated 1000-Hz sinewave, where
+each symbol is encoded as ten full carrier cycles, or 10 ms in duration.
+The symbols are distinguished using a pulse-width code, where 2 ms
+corresponds to logic zero, 5 ms to logic one and 8 ms to a position
+identifier used for symbol synchronization. The complete IRIG-B message
+consists of a frame of ten fields, each field consisting of a nine
+information symbols followed by a position identifier for a total frame
+duration of one second. The first symbol in the frame is also a position
+identifier to facilitate frame synchronization.
+
+The IRIG-B signal encodes the day of year and time of day in binary-
+coded decimal (BCD) format, together with a set of control functions,
+which are not used by the driver, but included in the raw binary
+timecode. Either the BCD timecode or the combined raw timecode and BCD
+timecode can be returned in response to a read() system call. The BCD
+timecode is in handy ASCII format: "ddd hh:mm:ss*" for convenience in
+client programs. In this format the "*" status character is " " when the
+driver is operating normally and "?" when errors may be present (see
+below). In order to reduce residual errors to the greatest extent
+possible, the driver computes a timestamp based on the value of the
+kernel clock at the on-time epoch of the IRIG-B signal. In addition, the
+driver automatically adjusts for slowly varying amplitude levels of the
+IRIG-B signal and suppresses noise transients.
+
+In operation the IRIG driver interprets the IRIG-B signal in real time,
+synchronizes to the signal, demodulates the data bits and prepares the
+data to be read later. At the on-time epoch a timestamp is captured from
+the kernel clock and adjusted for the phase of the IRIG carrier signal
+relative to the 8-kHz codec sample clock. When a client program issues a
+read() request, the most recent timecode data, including a status byte
+and the corrected timestamp, are stored in a structure and returned to
+the caller. Depending on the frequency with which the driver is called,
+this may result in old data or duplicate data or even invalid data,
+should the driver be called before it has computed its first timestamp.
+
+In practice, the resulting ambiguity causes few problems. The caller
+converts the ASCII timecode returned by a read() system call to Unix
+timeval format and subtracts it from the kernel timestamp provided by
+the driver. The result is an adjustment that can be subtracted from the
+kernel time, as returned in a gettimeofday() call, for example, to
+correct for the deviation between IRIG time and kernel time. The result
+can always be relied on to within plus/minus 128 microseconds, the audio
+codec sampling interval, and ordinarily to within a few microseconds, as
+determined by the interpolation algorithm.
+
+2. Programming Interface
+
+The IRIG driver modifications are integrated in the BSD audio driver
+bsd_audio.c without affecting its usual functions in transmitting and
+receiving ordinary speech, except when enabled by specific ioctl()
+system calls. However, the driver cannot be used for both speech and
+IRIG signals at the same time. Once activated by a designated ioctl()
+call, the driver remains active until it is explicitly deactivated by
+another ioctl() call. This allows applications to configure the audio
+device and pass the pre-configured driver to other applications. Since
+the driver is currently only a receiver, it does not affect the
+operation of the BSD audio output driver.
+
+Data are read using the standard read() system call. Since the output
+formats have constant lengths, the application receives the data into a
+fixed-length buffer or structure. The read() call never blocks; it
+simply returns the most recent IRIG data received during the last
+second. It may happen that, due to unavoidable race conditions in the
+kernel, data for other than the most recent second are returned. The
+driver's internal data structure is updated as an atomic unit; thus, the
+entire structure is valid, even if it contains old data. This should
+cause no problems, since in the intended application the driver is
+called at regular intervals by a time-synchronization daemon such as
+NTP. The daemon can determine the validity of the time indication by
+checking the timecode or status byte returned with the data.
+
+The header file bsd_audioirig.h defines the irig_time structure and
+ioctl() codes used by the driver. Following are those codes specific to
+the IRIG function of the driver. Unless indicated otherwise, the (third)
+argument of the ioctl() system call points to an integer or string.
+
+AUDIO_IRIG_OPEN
+
+ This command activates the IRIG receiver. The audio driver must be
+ opened with this command before other commands can be issued. The
+ argument is ignored. When the IRIG receiver is initialized, all
+ internal data are purged and any buffered data are lost.
+
+AUDIO_IRIG_CLOSE
+
+ This command deactivates the IRIG receiver. The argument is
+ ignored. The buffers are purged and any buffered time data are
+ lost. The original BSD audio driver functions are enabled and it
+ resumes operating normally.
+
+AUDIO_IRIG_SETFORMAT
+
+ The argument is a pointer to an integer designating the output
+ format for the IRIG data. There are currently two formats defined,
+ 0 (default) and 1. If an invalid format is selected, the default
+ format is used.
+
+The data returned by a read() system call in format 0 is a character
+string in the format "ddd hh:mm:ss*\n", which consists of 13 ASCII
+characters followed by a newline terminator for a total of 14
+characters. The "*" status character is an ASCII space " " if the status
+byte determined by the driver is zero and "?" if not. This format is
+intended to be used with simple user programs that care only about the
+time to the nearest second.
+The data returned by a read() system call in format 1 is a structure
+defined in the bsd_audioirig.h header file:
+
+ struct irig_time {
+ struct timeval stamp; /* timestamp */
+ u_char bits[13]; /* 100 irig data bits */
+ u_char status; /* status byte */
+ char time[14]; /* time string */
+ };
+
+The irig-time.stamp is a pair of 32-bit longwords in Unix timeval
+format, as defined in the sys/time.h header file. The first word is the
+number of seconds since 1 January 1970, while the second is the number
+of microseconds in the current second. The timestamp is captured at the
+most recent on-time instant of the IRIG timecode and applies to all
+other values returned in the irig_time structure.
+
+The irig_time.bits[13] is a vector of 13 bytes to hold the 100-bit,
+zero-padded raw binary timecode, packed 8 symbols per byte. The symbol
+encoding maps IRIG one to 1 and both IRIG zero and IRIG position
+identifier to 0. The order of encoding is illustrated by the following
+diagram (the padding bits are represented by xxxx, which are set to
+zero):
+
+IRIG symbol number 00000000001111111111 . . . 8888889999999999xxxx
+ 01234567890123456789 . . . 4567890123456789xxxx
+ -----------------------------------------------
+bits byte number <--00--><--01--><---- ----><--11--><--12-->
+bits bit in byte 01234567012345670123 . . . 45670123456701234567
+
+The irig_time.status is a single byte with bits defined in the
+bsd_audioirig.h header file. In ordinary operation all bits of the
+status byte are zero and the " " status character is set in the ASCII
+timecode. If any of these bits are nonzero, the "?" status character is
+set in the ASCII timecode.
+
+AUDIO_IRIG_BADSIGNAL
+
+ The signal amplitude is outside tolerance limits, either in
+ amplitude or modulation depth. The indicated time may or may not be
+ in error. If the signal is too high, it may be clipped by the
+ codec, so that the pulse width cannot be reliably determined. If
+ too low, it may be obscured by noise. The nominal expectation is
+ that the peak amplitude of the signal be maintained by the codec
+ AGC at about 10 dB below the clipping level and that the modulation
+ index be at least 0.5 (6 dB).
+
+AUDIO_IRIG_BADDATA
+
+ An invalid hex code (A through F) has been found where BCD data is
+ expected. The ASCII representation of the invalid code is set to
+ "?". Errors of this type are most likely due to noise on the IRIG
+ signal due to ground loops, coupling to other noise sources, etc.
+
+AUDIO_IRIG_BADSYNC
+
+ A code element has been found where a position identifier should be
+ or a position identifier has been found where a code element should
+ be. The time is meaningless and should be disregarded. Errors of
+ this type can be due to severe noise on the IRIG signal due to
+ ground loops, coupling to other noise sources, etc., or during
+ initial acquisition of the signal.
+
+AUDIO_IRIG_BADCLOCK
+
+ Some IRIG timecode generators can indicate whether or not the
+ generator is operating correctly or synchronized to its source of
+ standard time using a designated field in the raw binary timecode.
+ Where such information is available and the IRIG decoder can detect
+ it, this bit is set when the generator reports anything except
+ normal operating conditions.
+
+AUDIO_IRIG_OLDDATA
+
+ The IRIG time has not changed since the last time it was returned
+ in a read() call. This is not normally considered an error, unless
+ it persists for longer than a few seconds, in which case it
+ probably indicates a hardware problem.
+
+The irig_time.time[14] vector is a character string in the format "ddd
+hh:mm:ss*\0", which consists of 13 ASCII characters followed by a zero
+terminator. The "*" status character is an ASCII space " " if the status
+byte is zero and "?" if not. This format is identical to format 0,
+except that in format 1 the time string is null-terminated.
+
+2.1. Programming Example
+
+The following pseudo-code demonstrates how the IRIG receiver may be used
+by a simple user program. Of course, real code should include error
+checking after each call to ensure the driver is communicating properly.
+It should also verify that the correct fields in the structure are being
+filled by the read() call.
+
+ include "bsd_audioirig.h"
+
+ int format = 1;
+ struct irig_time it;
+
+ Audio_fd = open("/dev/audio", O_RDONLY);
+ ioctl(Audio_fd, AUDIO_IRIG_OPEN, NULL);
+ ioctl(Audio_fd, AUDIO_IRIG_SETFORMAT,&format);
+ while (condition)
+ read(Audio_fd, &it, sizeof(it);
+ printf("%s\n", it.time);
+ ioctl(Audio_fd, AUDIO_IRIG_CLOSE, NULL);
+ close(Audio_fd);
+
+3. Implementation and Configuration Notes
+
+The signal level produced by most IRIG-equipped radios is on the order
+of a few volts peak-peak, which is far larger than the audio codec can
+accept; therefore, an attenuator in the form of a voltage divider is
+needed. The codec can handle IRIG signals at the microphone input from
+4.2mV to 230mV peak-peak. A suitable attenuator conists of a series-
+connected 100K-Ohm resistor at the input and a parallel-connected 1K-Ohm
+resistor at the output, both contained along with suitable connectors in
+a small aluminum box. The exact values of these resistors are not
+critical, since the IRIG driver includes an automatic level-adjustment
+capability.
+
+For the most accurate time using the IRIG signal and a particular radio,
+it may be necessary to adjust the time1 parameter of the fudge command
+to compensate for the codec delay and any additional delay due to IRIG
+processing in the radio itself. Since the codec samples at an 8-kHz
+rate, the average delay is about 62 usec; however, the delays due to the
+radios and IRIG signals themselves can vary. For instance, in the
+Austron recievers the IRIG delay is essentially zero, while in the
+Spectracom receivers the delay is about 240 usec relative to the 1-pps
+signal. In addition, the poll interval can be reduced from the usual 64
+seconds to 16 seconds to reduce wander of the local hardware clock.
+Finally, the prefer parameter can be used to bias the clock-selection
+algorithm to favor the IRIG time, which is ordinarily the best time
+available. For example, the following two lines in the NTP configuration
+file ntp.conf are appropriate for the Spectracom Netclock/1 WWVB
+Synchronized Clock with IRIG Option:
+
+server 127.127.6.0 prefer minpoll 4 maxpoll 4 # irig audio decoder
+fudge 127.127.6.0 time1 0.0005
+
+The time1 value of .0005 s (500 usec) was determined by actual
+measurement. Since the IRIG delay in Austron receivers is essentially
+zero, the fudge command is not necessary with these receivers. The
+correct value in case of other radios may have to be determined by
+actual measurement. A convenient way of doing this is to configure the
+PPSPPS feature in the NTP Version 3 distribution and adjust time1 until
+the 1-pps signal and IRIG signal both show the same offset.
+
+The modified BSD driver includes both the modified driver itself
+bsd_audio.c and the IRIG header file bsd_audioirig.h, as well as
+modified header files bsd_audiovar.h and bsd_audioio.h. The driver is
+installed in the same way as described in the BSD driver documentation,
+with the addition of the following define in the kernel configuration
+file:
+
+options AUDIO_IRIG # IRIG driver
+
+This causes the IRIG code to be included in the BSD driver, as well as a
+C-coded codec interrupt routine which replaces the assembly-coded
+routine and provides the IRIG functionality. While the C-coded routine
+is somewhat slower than the assembly-coded routine, the extra overhead
+is not expected to be significant. Note that the IRIG driver calls the
+kernel routine microtime() as included in the ppsclock directory of the
+NTP Version 3 distribution xntp3. It is highly recommended that this
+routine be installed in the kernel configuration as well. The
+instructions for doing this are contained in the ppsclock directory of
+the xntp3 distribution.
+
+Roy LeCates <lecates@udel.edu> and David Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+24 August 1993
diff --git a/usr.sbin/xntpd/doc/README.kern b/usr.sbin/xntpd/doc/README.kern
new file mode 100644
index 0000000..aac26fa
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.kern
@@ -0,0 +1,1374 @@
+ A Kernel Model for Precision Timekeeping
+
+ Revised 3 April 1994
+
+Note: This memorandum is a substantial revision of RFC-1589, "A Kernel
+Model for Precision Timekeeping," March, 1994. It includes several
+changes to the daemon and user interfaces, as well as a new feature
+which disciplines the CPU clock oscillator in both time and frequency to
+a source of precision time signals. This memorandum is included in the
+distributions for the SunOS, Ultrix and OSF/1 kernels and in the NTP
+Version 3 distribution (xntp3.v.tar.Z) as the file README.kern, where v
+is the version identifier. Availability of the kernel distributions,
+which involve licensed code, will be announced separately. The NTP
+Version 3 distribution can be obtained via anonymous ftp from
+louie.udel.edu in the directory pub/ntp. In order to utilize all
+features of this distribution, the NTP version identifier should be 3q
+or later.
+
+Overview
+
+This memorandum describes an engineering model which implements a
+precision time-of-day function for a generic operating system. The model
+is based on the principles of disciplined oscillators and phase-lock
+loops (PLL) and frequency-lock loops (FLL) often found in the
+engineering literature. It has been implemented in the Unix kernels for
+several workstations, including those made by Sun Microsystems and
+Digital Equipment. The model changes the way the system clock is
+adjusted in time and frequency, as well as provides mechanisms to
+discipline its frequency to an external precision timing source. The
+model incorporates a generic system-call interface for use with the
+Network Time Protocol (NTP) or similar time synchronization protocol.
+The NTP Version 3 daemon xntpd operates with this model to provide
+synchronization limited in principle only by the accuracy and stability
+of the external timing source.
+
+This memorandum does not obsolete or update any RFC. It does not propose
+a standard protocol, specification or algorithm. It is intended to
+provoke comment, refinement and implementations for kernels not
+considered herein. While a working knowledge of NTP is not required for
+an understanding of the design principles or implementation of the
+model, it may be helpful in understanding how the model behaves in a
+fully functional timekeeping system. The architecture and design of NTP
+is described in [MIL91], while the current NTP Version 3 protocol
+specification is given in RFC-1305 [MIL92a] and a subset of the
+protocol, the Simple Network Time Protocol (SNTP), is given in RFC-1361
+[MIL92c].
+
+The model has been implemented in the Unix kernels for three Sun
+Microsystems and Digital Equipment workstations. In addition, for the
+Digital machines the model provides improved precision to one
+microsecond (us). Since these specific implementations involve
+modifications to licensed code, they cannot be provided directly.
+Inquiries should be directed to the manufacturer's representatives.
+However, the engineering model for these implementations, including a
+simulator with code segments almost identical to the implementations,
+but not involving licensed code, is available via anonymous FTP from
+host louie.udel.edu in the directory pub/ntp and compressed tar archive
+kernel.tar.Z. The NTP Version 3 distribution can be obtained via
+anonymous ftp from the same host and directory in the compressed tar
+archive xntp3.3q.tar.Z, where the version number shown as 3.3q may be
+adjusted for new versions as they occur.
+
+1. Introduction
+
+This memorandum describes a model and programming interface for generic
+operating system software that manages the system clock and timer
+functions. The model provides improved accuracy and stability for most
+computers using the Network Time Protocol (NTP) or similar time
+synchronization protocol. This memorandum describes the design
+principles and implementations of the model, while related technical
+reports discuss the design approach, engineering analysis and
+performance evaluation of the model as implemented in Unix kernels for
+modern workstations. The NTP Version 3 daemon xntpd operates with these
+implementations to provide improved accuracy and stability, together
+with diminished overhead in the operating system and network. In
+addition, the model supports the use of external timing sources, such as
+precision pulse-per-second (PPS) signals and the industry standard IRIG
+timing signals. The NTP daemon automatically detects the presence of the
+new features and utilizes them when available.
+
+There are three prototype implementations of the model presented in this
+memorandum, one each for the Sun Microsystems SPARCstation with the
+SunOS 4.1.x kernel, Digital Equipment DECstation 5000 with the Ultrix
+4.x kernel and Digital Equipment 3000 AXP Alpha with the OSF/1 V1.x
+kernel. In addition, for the DECstation 5000/240 and 3000 AXP Alpha
+machines, a special feature provides improved precision to 1 us (stock
+Sun kernels already do provide this precision). Other than improving the
+system clock accuracy, stability and precision, these implementations do
+not change the operation of existing Unix system calls which manage the
+system clock, such as gettimeofday(), settimeofday() and adjtime();
+however, if the new features are in use, the operations of
+gettimeofday() and adjtime() can be controlled instead by new system
+calls ntp_gettime() and ntp_adjtime() as described below.
+
+A detailed description of the variables and algorithms that operate upon
+them is given in the hope that similar functionality can be incorporated
+in Unix kernels for other machines. The algorithms involve only minor
+changes to the system clock and interval timer routines and include
+interfaces for application programs to learn the system clock status and
+certain statistics of the time synchronization process. Detailed
+installation instructions are given in a specific README files included
+in the kernel distributions.
+
+In this memorandum, NTP Version 3 and the Unix implementation xntp3 are
+used as an example application of the new system calls for use by a
+synchronization daemon. In principle, these system calls can be used by
+other protocols and implementations as well. Even in cases where the
+local time is maintained by periodic exchanges of messages at relatively
+long intervals, such as using the NIST Automated Computer Time Service
+[LEV89], the ability to precisely adjust the system clock frequency
+simplifies the synchronization procedures and allows the telephone call
+frequency to be considerably reduced.
+
+2. Design Approach
+
+While not strictly necessary for an understanding or implementation of
+the model, it may be helpful to briefly describe how NTP operates to
+control the system clock in a client computer. As described in [MIL91],
+the NTP protocol exchanges timestamps with one or more peers sharing a
+synchronization subnet to calculate the time offsets between peer clocks
+and the local clock. These offsets are processed by several algorithms
+which refine and combine the offsets to produce an ensemble average,
+which is then used to adjust the local clock time and frequency. The
+manner in which the local clock is adjusted represents the main topic of
+this memorandum. The goal in the enterprise is the most accurate and
+stable system clock possible with the available computer hardware and
+kernel software.
+
+In order to understand how the new model works, it is useful to review
+how most Unix kernels maintain the system clock. In the Unix design a
+hardware counter interrupts the kernel at a fixed rate: 100 Hz in the
+SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in the OSF/1
+kernel. Since the Ultrix timer interval (reciprocal of the rate) does
+not evenly divide one second in microseconds, the kernel adds 64 us once
+each second, so the timescale consists of 255 advances of 3906 us plus
+one of 3970 us. Similarly, the OSF/1 kernel adds 576 us once each
+second, so its timescale consists of 1023 advances of 976 us plus one of
+1552 us.
+
+2.1. Mechanisms to Adjust Time and Frequency
+
+In most Unix kernels it is possible to slew the system clock to a new
+offset relative to the current time by using the adjtime() system call.
+To do this the clock frequency is changed by adding or subtracting a
+fixed amount (tickadj) at each timer interrupt (tick) for a calculated
+number of timer interrupts. Since this calculation involves dividing the
+requested offset by tickadj, it is possible to slew to a new offset with
+a precision only of tickadj, which is usually in the neighborhood of 5
+us, but sometimes much larger. This results in a roundoff error which
+can accumulate to an unacceptable degree, so that special provisions
+must be made in the clock adjustment procedures of the synchronization
+daemon.
+
+In order to implement a frequency discipline function, it is necessary
+to provide time offset adjustments to the kernel at regular adjustment
+intervals using the adjtime() system call. In order to reduce the system
+clock jitter to the regime consistent with the model, it is necessary
+that the adjustment interval be relatively small, in the neighborhood of
+1 s. However, the Unix adjtime() implementation requires each offset
+adjustment to complete before another one can be begun, which means that
+large adjustments must be amortized over possibly many adjustment
+intervals. The requirement to implement the adjustment interval and
+compensate for roundoff error considerably complicates the synchronizing
+daemon implementation.
+
+In the new model this scheme is replaced by another that represents the
+system clock as a multiple-word, precision-time variable in order to
+provide very precise clock adjustments. At each timer interrupt a
+precisely calibrated quantity is added to the kernel time variable and
+overflows propagated as required. The quantity is computed as in the NTP
+local clock model described in [MIL92b], which operates as an adaptive-
+parameter, first-order, type-II phase-lock loop (PLL). In principle,
+this PLL design can provide precision control of the system clock
+oscillator within 1 us and frequency to within parts in 10^11. While
+precisions of this order are surely well beyond the capabilities of the
+CPU clock oscillator used in typical workstations, they are appropriate
+using precision external oscillators, as described below.
+
+The PLL design is identical to the one originally implemented in NTP and
+described in [MIL92b]. In the original design the software daemon
+simulates the PLL using the adjtime() system call; however, the daemon
+implementation is considerably complicated by the considerations
+described above. The modified kernel routines implement the PLL in the
+kernel using precision time and frequency representations, so that these
+complications are avoided. A new system call ntp_adjtime() is called
+only as each new time update is determined, which in NTP occurs at
+intervals of from 16 s to 1024 s. In addition, doing frequency
+compensation in the kernel means that the system clock runs true even if
+the daemon were to cease operation or the network paths to the primary
+synchronization source fail.
+
+In the new model the new ntp_adjtime() operates in a way similar to the
+original adjtime() system call, but does so independently of adjtime(),
+which continues to operate in its traditional fashion. When used with
+NTP, it is the design intent that settimeofday() or adjtime() be used
+only for system clock adjustments greater than +-128 ms, although the
+dynamic range of the new model is much larger at +-512 ms. It has been
+the Internet experience that the need to change the system clock in
+increments greater than +-128 ms is extremely rare and is usually
+associated with a hardware or software malfunction or system reboot.
+
+The easiest way to set the time is with the settimeofday() system call;
+however, this can under some conditions cause the clock to jump
+backwards. If this cannot be tolerated, adjtime() can be used to slew
+the clock to the new value without running backward or affecting the
+frequency discipline process. Once the system clock has been set within
++-128 ms, the ntp_adjtime() system call is used to provide periodic
+updates including the time offset, maximum error, estimated error and
+PLL time constant. With NTP the update interval and time constant depend
+on the measured delay and dispersion; however, the scheme is quite
+forgiving and neither moderate loss of updates nor variations in the
+update interval are serious.
+
+2.2 Daemon and Application Interface
+
+Unix application programs can read the system clock using the
+gettimeofday() system call, which returns only the system time and
+timezone data. For some applications it is useful to know the maximum
+error of the reported time due to all causes, including clock reading
+errors, oscillator frequency errors and accumulated latencies on the
+path to the primary synchronization source. However, in the new model
+the PLL adjusts the system clock to compensate for its intrinsic
+frequency error, so that the time error expected in normal operation
+will usually be much less than the maximum error. The programming
+interface includes a new system call ntp_gettime(), which returns the
+system time, as well as the maximum error and estimated error. This
+interface is intended to support applications that need such things,
+including distributed file systems, multimedia teleconferencing and
+other real-time applications. The programming interface also includes a
+new system call ntp_adjtime(), which can be used to read and write
+kernel variables for time and frequency adjustment, PLL time constant,
+leap-second warning and related data.
+
+In addition, the kernel adjusts the indicated maximum error to grow by
+an amount equal to the maximum oscillator frequency tolerance times the
+elapsed time since the last update. The default engineering parameters
+have been optimized for update intervals in the order of 64 s. As shown
+in [MIL93], this is near the optimum interval for NTP used with ordinary
+room-temperature quartz oscillators. For other intervals the PLL time
+constant can be adjusted to optimize the dynamic response over intervals
+of 16-1024 s. Normally, this is automatically done by NTP. In any case,
+if updates are suspended, the PLL coasts at the frequency last
+determined, which usually results in errors increasing only to a few
+tens of milliseconds over a day using typical modern workstations.
+
+While any synchronization daemon can in principle be modified to use the
+new system calls, the most likely will be users of the NTP Version 3
+daemon xntpd. The xntpd code determines whether the new system calls are
+implemented and automatically reconfigures as required. When
+implemented, the daemon reads the frequency offset from a system file
+and provides it and the initial time constant via ntp_adjtime(). In
+subsequent calls to ntp_adjtime(), only the time offset and time
+constant are affected. The daemon reads the frequency from the kernel
+using ntp_adjtime() at intervals of about one hour and writes it to a
+system file. This information is recovered when the daemon is restarted
+after reboot, for example, so the sometimes extensive training period to
+learn the frequency separately for each oscillator can be avoided.
+
+2.3. Precision Clocks for DECstation 5000/240 and 3000 AXP Alpha
+
+The stock microtime() routine in the Ultrix kernel for Digital Equipment
+MIPS-based workstations returns system time to the precision of the
+timer interrupt interval, which is in the 1-4 ms range. However, in the
+DECstation 5000/240 and possibly other machines of that family, there is
+an undocumented IOASIC hardware register that counts system bus cycles
+at a rate of 25 MHz. The new microtime() routine for the Ultrix kernel
+uses this register to interpolate system time between timer interrupts.
+This results in a precision of 1 us for all time values obtained via the
+gettimeofday() and ntp_gettime() system calls. For the Digital Equipment
+3000 AXP Alpha, the architecture provides a hardware Process Cycle
+Counter and a machine instruction (rpcc) to read it. This counter
+operates at the fundamental frequency of the CPU clock or some
+submultiple of it, 133.333 MHz for the 3000/400 for example. The new
+microtime() routine for the OSF/1 kernel uses this counter in the same
+fashion as the Ultrix routine. Support for this feature is conditionally
+compiled in the kernel only if the MICRO option is used in the kernel
+configuration file.
+
+In both the Ultrix and OSF/1 kernels the gettimeofday() and
+ntp_gettime() system call use the new microtime() routine, which returns
+the interpolated value to 1-us resolution, but does not change the
+kernel time variable. Therefore, other routines that access the kernel
+time variable directly and do not call either gettimeofday(),
+ntp_gettime() or microtime() will continue their present behavior. The
+microtime() feature is independent of other features described here and
+is operative even if the kernel PLL or new system calls have not been
+implemented.
+
+The SunOS kernel already includes a system clock with 1-us resolution;
+so, in principle, no microtime() routine is necessary. An existing
+kernel routine uniqtime() implements this function, but it is coded in
+the C language and is rather slow at 42-85 us per call on a SPARCstation
+IPC. A replacement microtime() routine coded in assembler language is
+available in the NTP Version 3 distribution and is much faster at about
+3 us per call. Note that, as explained later, this routine should be
+called at an interrupt priority level not greater than that of the timer
+interrupt routine. Otherwise, it is possible to miss a tick increment,
+with result the time returned can be late by one tick. This is always
+true in the case of gettimeofday() and ntp_gettime(), but might not be
+true in other cases, such as when using the PPS signal described later
+in this memorandum.
+
+2.4. External Time and Frequency Discipline
+
+The overall accuracy of a time synchronization subnet with respect to
+Coordinated Universal Time (UTC) depends on the accuracy and stability
+of the primary synchronization source, usually a radio or satellite
+receiver, and the CPU clock oscillator of the primary server. As
+discussed in [MIL93], the traditional interface using a ASCII serial
+timecode and RS232 port precludes the full accuracy of most radio
+clocks. In addition, the poor frequency stability of typical CPU clock
+oscillators limits the accuracy, whether or not precision time sources
+are available. There are, however, several ways in which the system
+clock accuracy and stability can be improved to the degree limited only
+by the accuracy and stability of the synchronization source and the
+jitter of the interface and operating system.
+
+Many radio clocks produce special signals that can be used by external
+equipment to precisely synchronize time and frequency. Most produce a
+pulse-per-second (PPS) signal that can be read via a modem-control lead
+of a serial port and some produce a special IRIG signal that can be read
+directly by a bus peripheral, such as the KSI/Odetics TPRO IRIG SBus
+interface, or indirectly via the audio codec of some workstations, as
+described in [MIL93]. In the NTP Version 3 daemon xntpd, the PPS signal
+can be used to augment the less precise ASCII serial timecode to improve
+accuracy to the order of a few tens of microseconds. Support is also
+included in the NTP distribution for the TPRO interface, as well as the
+audio codec; however, the latter requires a modified kernel audio driver
+contained in the compressed tar archive bsd_audio.tar.Z in the same host
+and directory as the NTP Version 3 distribution mentioned previously.
+2.4.1. PPS Signal
+
+The most convenient way to interface a PPS signal to a computer is
+usually with a serial port and RS232-compatible signal; however, the PPS
+signal produced by most radio clocks and laboratory instruments is
+usually a TTL pulse signal. Therefore, some kind of level
+converter/pulse generator is necessary to adapt the PPS signal to a
+serial port. An example design, including schematic and printed-circuit
+board artwork, is in the compressed tar archive gadget.tar.Z in the same
+host and directory as the NTP Version 3 distribution mentioned
+previously. There are several ways the PPS signal can be used in
+conjunction with the NTP Version 3 daemon xntpd, as described in [MIL93]
+and in the documentation included in the distribution.
+
+The NTP Version 3 distribution includes a special ppsclock module for
+the SunOS 4.1.x kernel that captures the PPS signal presented via a
+modem-control lead of a serial port. Normally, the ppsclock module
+produces a timestamp at each transition of the PPS signal and provides
+it to the synchronization daemon for integration with the serial ASCII
+timecode, also produced by the radio clock. With the conventional PLL
+implementation in either the daemon or the kernel as described in
+[MIL93], the accuracy of this scheme is limited by the intrinsic
+stability of the CPU clock oscillator to a millisecond or two, depending
+on environmental temperature variations.
+
+The ppsclock module has been modified to in addition call a new kernel
+routine hardpps() once each second. In addition, the Ultrix 4.3 kernel
+has been modified to provide a similar functionality. The hardpps()
+routine compares the timestamp with a sample of the CPU clock oscillator
+in order to discipline the oscillator to the time and frequency of the
+PPS signal. Using this method, the time accuracy is improved to
+typically 20 us or less and frequency stability a few parts in 10^8,
+which is about two orders of magnitude better than the undisciplined
+oscillator. The new feature is conditionally compiled in the code
+described below only if the PPS_SYNC option is used in the kernel
+configuration file.
+
+When using the PPS signal to adjust the time, there is a problem with
+some kernels which is very difficult to fix. The serial port interrupt
+routine often operates at an interrupt priority level above the timer
+interrupt routine. Thus, as explained below, it is possible that a tick
+increment can be missed and the time returned late by one tick. It may
+happen that, if the CPU clock oscillator frequency is close to the PPS
+oscillator frequency (less than a few ppm), this condition can persist
+for two or more successive PPS interrupts. A useful workaround in the
+code is to use a glitch detector and median filter to process the PPS
+sample offsets. The glitch detector suppresses offset bursts greater
+than half the tick interval and which last less than 30 successive PPS
+interrupts. The median filter ranks the offsets in a moving window of
+three samples and uses the median as the output and the difference
+between the other two as a dispersion measure.
+
+2.4.2. External Clocks
+
+It is possible to replace the system clock function with an external bus
+peripheral. The TPRO device mentioned previously can be used to provide
+IRIG-synchronized time with a precision of 1 us. A driver for this
+device tprotime.c and header file tpro.h are included in the
+kernel.tar.Z distribution mentioned previously. Using this device, the
+system clock is read directly from the interface; however, the device
+does not record the year, so special provisions have been made to obtain
+the year from the kernel time variable and initialize the driver
+accordingly. Support for this feature is conditionally compiled in the
+kernel only if the EXT_CLOCK and TPRO options are used in the kernel
+configuration file.
+
+While the system clock function is provided directly by the microtime()
+routine in the driver, the kernel time variable must be disciplined as
+well, since not all system timing functions use the microtime() routine.
+This is done by measuring the time difference between the microtime()
+clock and kernel time variable and using it to adjust the kernel PLL as
+if the adjustment were provided by an external peer and NTP.
+
+A good deal of error checking is done in the TPRO driver, since the
+system clock is vulnerable to a misbehaving radio clock, IRIG signal
+source, interface cables and TPRO device itself. Unfortunately, there is
+no practical way to utilize the extensive diversity and redundancy
+capabilities available in the NTP synchronization daemon. In order to
+avoid disruptions that might occur if the TPRO time is far different
+from the kernel time variable, the latter is used instead of the former
+if the difference between the two exceeds 1000 s; presumably in that
+case operator intervention is required.
+
+2.4.2. External Oscillators
+
+Even if a source of PPS or IRIG signals is not available, it is still
+possible to improve the stability of the system clock through the use of
+a specialized bus peripheral. In order to explore the benefits of such
+an approach, a special SBus peripheral called HIGHBALL has been
+constructed. The device includes a pair of 32-bit hardware counters in
+Unix timeval format, together with a precision, oven-controlled quartz
+oscillator with a stability of a few parts in 10^9. A driver for this
+device hightime.c and header file high.h are included in the
+kernel.tar.Z distribution mentioned previously. Support for this feature
+is conditionally compiled in the kernel only if the EXT_CLOCK and
+HIGHBALL options are used in the kernel configuration file.
+
+Unlike the external clock case, where the system clock function is
+provided directly by the microtime() routine in the driver, the HIGHBALL
+counter offsets with respect to UTC must be provided first. This is done
+using the ordinary kernel PLL, but controlling the counter offsets
+directly, rather than the kernel time variable. At first, this might
+seem to defeat the purpose of the design, since the jitter and wander of
+the synchronization source will affect the counter offsets and thus the
+accuracy of the time. However, the jitter is much reduced by the PLL and
+the wander is small, especially if using a radio clock or another
+primary server disciplined in the same way. In practice, the scheme
+works to reduce the incidental wander to a few parts in 10^8, or about
+the same as using the PPS signal.
+
+As in the previous case, the kernel time variable must be disciplined as
+well, since not all system timing functions use the microtime() routine.
+However, the kernel PLL cannot be used for this, since it is already in
+use providing offsets for the HIGHBALL counters. Therefore, a special
+correction is calculated from the difference between the microtime()
+clock and the kernel time variable and used to adjust the kernel time
+variable at the next timer interrupt. This somewhat roundabout approach
+is necessary in order that the adjustment does not cause the kernel time
+variable to jump backwards and possibly lose or duplicate a timer event.
+
+2.5 Other Features
+
+It is a design feature of the NTP architecture that the system clocks in
+a synchronization subnet are to read the same or nearly the same values
+before during and after a leap-second event, as declared by national
+standards bodies. The new model is designed to implement the leap event
+upon command by an ntp_adjtime() argument. The intricate and sometimes
+arcane details of the model and implementation are discussed in [MIL92b]
+and [MIL93]. Further details are given in the technical summary later in
+this memorandum.
+3. Technical Summary
+
+In order to more fully understand the workings of the model, a stand-
+alone simulator kern.c and header file timex.h are included in the
+kernel.tar.Z distribution mentioned previously. In addition, an example
+kernel module kern_ntptime.c which implements the ntp_gettime() and
+ntp_adjtime() system calls is included. Neither of these programs
+incorporate licensed code. Since the distribution is somewhat large, due
+to copious comments and ornamentation, it is impractical to include a
+listing of these programs in this memorandum. In any case, implementors
+may choose to snip portions of the simulator for use in new kernel
+designs; but, due to formatting conventions, this would be difficult if
+included in this memorandum.
+
+The kern.c program is an implementation of an adaptive-parameter, first-
+order, type-II phase-lock loop. The system clock is implemented using a
+set of variables and algorithms defined in the simulator and driven by
+explicit offsets generated by the main() routine in the program. The
+algorithms include code fragments almost identical to those in the
+machine-specific kernel implementations and operate in the same way, but
+the operations can be understood separately from any licensed source
+code into which these fragments may be integrated. The code fragments
+themselves are not derived from any licensed code. The following
+discussion assumes that the simulator code is available for inspection.
+
+3.1. PLL Simulation
+
+The simulator operates in conformance with the analytical model
+described in [MIL92b]. The main() program operates as a driver for the
+fragments hardupdate(), hardclock(), second_overflow(), hardpps() and
+microtime(), although not all functions implemented in these fragments
+are simulated. The program simulates the PLL at each timer interrupt and
+prints a summary of critical program variables at each time update.
+
+There are three defined options in the kernel configuration file
+specific to each implementation. The PPS_SYNC option provides support
+for a pulse-per-second (PPS) signal, which is used to discipline the
+frequency of the CPU clock oscillator. The EXT_CLOCK option provides
+support for an external kernel-readable clock, such as the KSI/Odetics
+TPRO IRIG interface or HIGHBALL precision oscillator, both for the SBus.
+The TPRO option provides support for the former, while the HIGHBALL
+option provides support for the latter. External clocks are implemented
+as the microtime() clock driver, with the specific source code selected
+by the kernel configuration file.
+
+The PPS signal is carefully monitored for error conditions which can
+affect accuracy, stability and reliability. The time_status kernel
+variable contains bits that both control the use of the PPS signal and
+reveal its operational status. The function of each bit is described in
+a later section of this memo.
+
+3.1.1. The hardupdate() Fragment
+
+The hardupdate() fragment is called by ntp_adjtime() as each update is
+computed to adjust the system clock phase and frequency. Note that the
+time constant is in units of powers of two, so that multiplies can be
+done by simple shifts. The phase variable is computed as the offset
+divided by the time constant, but clamped to a maximum (for robustness).
+Then, the time since the last update is computed and clamped to a
+maximum and to zero if initializing. The offset is multiplied (sorry
+about the ugly multiply) by the result and divided by the square of the
+time constant and then added to the frequency variable. Note that all
+shifts are assumed to be positive and that a shift of a signed quantity
+to the right requires a little dance.
+
+The STA_PLL and STA_PPSTIME status bits, which are set by the
+ntp_adjtime() system call, serve to enable or inhibit the kernel PLL and
+PPS time-discipline functions. The STA_PPSSIGNAL status bit is set by
+the hardpps() code fragment when the PPS signal is present and operating
+within nominal bounds. Time discipline from the PPS signal operates only
+if both the STA_PPSTIME and STA_PPSSIGNAL bits are set; otherwise, the
+discipline operates from the offset given in the ntp_adjtime() system
+call. In the intended mode of operation, the synchronization daemon sets
+STA_PLL to enable the PLL when first initialized, then sets STA_PPSTIME
+when reliable synchronization to within +-128 ms has been achieved with
+either a radio clock or external peer. The daemon can detect and
+indicate this condition for monitoring purposes by noting that both
+STA_PPSTIME and STA_PPSSIGNAL are set.
+
+With the defines given in the program and header files, the maximum time
+offset is determined by the size in bits of the long type (32 or 64)
+less the SHIFT_UPDATE scale factor (12) or at least 20 bits (signed).
+The scale factor is chosen so that there is no loss of significance in
+later steps, which may involve a right shift up to SHIFT_UPDATE bits.
+This results in a time adjustment range over +-512 ms. Since
+time_constant must be greater than or equal to zero, the maximum
+frequency offset is determined by the SHIFT_USEC scale factor (16) or at
+least 16 bits (signed). This results in a frequency adjustment range
+over +-31,500 ppm.
+
+In the addition step, the value of offset * mtemp is not greater than
+MAXPHASE * MAXSEC = 31 bits (signed), which will not overflow a long add
+on a 32-bit machine. There could be a loss of precision due to the right
+shift of up to 12 bits, since time_constant is bounded at 6. This
+results in a net worst-case frequency resolution of about .063 ppm,
+which is not significant for most quartz oscillators. The worst case
+could be realized only if the NTP peer misbehaves according to the
+protocol specification.
+
+The time_offset value is clamped upon entry. The time_phase variable is
+an accumulator, so is clamped to the tolerance on every call. This helps
+to damp transients before the oscillator frequency has been stabilized,
+as well as to satisfy the correctness assertions if the time
+synchronization protocol or implementation misbehaves.
+
+3.1.2. The hardclock() Fragment
+
+The hardclock() fragment is inserted in the hardware timer interrupt
+routine at the point the system clock is to be incremented by the value
+of tick. Previous to this fragment the time_update variable has been
+initialized to the tick increment plus the value computed by the
+adjtime() system call in the stock Unix kernel, normally plus/minus the
+tickadj value, which is usually in the order of 5 us. The time_phase
+variable, which represents the instantaneous phase of the system clock,
+is advanced by time_adj, which is calculated in the second_overflow()
+fragment described below. If the value of time_phase exceeds 1 us in
+scaled units, time_update is increased by the (signed) excess and
+time_phase retains the residue.
+
+In those cases where a PPS signal is connected by a serial port
+operating at an interrupt priority level greater than the timer
+interrupt, special consideration should be given the location of the
+hardclock() fragment in the timer interrupt routine. The system clock
+should be advanced as early in the routine as possible, preferably
+before the hardware timer interrupt flag is cleared. This reduces or
+eliminates the possibility that the microtime() routine may latch the
+time after the flag is cleared, but before the system clock is advanced,
+which results in a returned time late by one tick.
+
+Except in the case of an external oscillator such as the HIGHBALL
+interface, the hardclock() fragment advances the system clock by the
+value of tick plus time_update. However, in the case of an external
+oscillator, the system clock is obtained directly from the interface and
+time_update used to discipline that interface instead. However, the
+system clock must still be disciplined as explained previously, so the
+value of clock_cpu computed by the second_overflow() fragment is used
+instead.
+
+3.1.3. The second_overflow() Fragment
+
+The second_overflow() fragment is inserted at the point where the
+microseconds field of the system time variable is being checked for
+overflow. Upon overflow the maximum error time_maxerror is increased by
+time_tolerance to reflect the maximum time offset due to oscillator
+frequency error. Then, the increment time_adj to advance the kernel time
+variable is calculated from the (scaled) time_offset and time_freq
+variables updated at the last call to the hardclock() fragment.
+
+The phase adjustment is calculated as a (signed) fraction of the
+time_offset remaining, where the fraction is added to time_adj, then
+subtracted from time_offset. This technique provides a rapid convergence
+when offsets are high, together with good resolution when offsets are
+low. The frequency adjustment is the sum of the (scaled) time_freq
+variable, an adjustment necessary when the tick interval does not evenly
+divide one second fixtick and PPS frequency adjustment pps_freq (if
+configured).
+
+The scheme of approximating exact multiply/divide operations with shifts
+produces good results, except when an exact calculation is required,
+such as when the PPS signal is being used to discipline the CPU clock
+oscillator frequency as described below. As long as the actual
+oscillator frequency is a power of two in Hz, no correction is required.
+However, in the SunOS kernel the clock frequency is 100 Hz, which
+results in an error factor of 0.78. In this case the code increases
+time_adj by a factor of 1.25, which results in an overall error less
+than three percent.
+
+On rollover of the day, the leap-second state machine described below
+determines whether a second is to be inserted or deleted in the
+timescale. The microtime() routine insures that the reported time is
+always monotonically increasing.
+
+3.1.4. The hardpps() Fragment
+
+The hardpps() fragment is operative only if the PPS_SYNC option is
+specified in the kernel configuration file. It is called from the serial
+port driver or equivalent interface at the on-time transition of the PPS
+signal. The code operates as a first-order, type-I, frequency-lock loop
+(FLL) controlled by the difference between the frequency represented by
+the pps_freq variable and the frequency of the hardware clock
+oscillator. It also provides offsets to the hardupdate() fragment in
+order to discipline the system clock time.
+
+In order to avoid calling the microtime() routine more than once for
+each PPS transition, the interface requires the calling program to
+capture the system time and hardware counter contents at the on-time
+transition of the PPS signal and provide a pointer to the timestamp
+(Unix timeval) and counter contents as arguments to the hardpps() call.
+The hardware counter contents are determined by saving the microseconds
+field of the system time, calling the microtime() routine, and
+subtracting the saved value. If a microseconds overflow has occurred
+during the process, the resulting microseconds value will be negative,
+in which case the caller adds 1000000 to normalize the microseconds
+field.
+
+In order to avoid large jitter when the PPS interrupt occurs during the
+timer interrupt routine before the system clock is advanced, a glitch
+detector is used. The detector latches when an offset exceeds a
+threshold tick/2 and stays latched until either a subsequent offset is
+less than the threshold or a specified interval MAXGLITCH (30 s) has
+elapsed. As long as the detector remains latched, it outputs the offset
+immediately preceding the latch, rather than the one received.
+
+A three-stage median filter is used to suppress jitter less than the
+glitch threshold. The median sample drives the PLL, while the difference
+between the other two samples represents the time dispersion. Time
+dispersion samples are averaged and used as a jitter estimate. If this
+estimate exceeds a threshold MAXTIME/2 (100 us), an error bit
+STA_PPSJITTER is raised in the status word.
+
+The frequency of the hardware oscillator is determined from the
+difference in hardware counter readings at the beginning and end of the
+calibration interval divided by the duration of the interval. However,
+the oscillator frequency tolerance, as much as 100 ppm, may cause the
+difference to exceed the tick value, creating an ambiguity. In order to
+avoid this ambiguity, the hardware counter value at the beginning of the
+interval is increased by the current pps_freq value once each second,
+but computed modulo the tick value. At the end of the interval, the
+difference between this value and the value computed from the hardware
+counter is the control signal for the FLL.
+
+Control signal samples which exceed the frequency tolerance MAXFREQ (100
+ppm) are discarded, as well as samples resulting from excessive interval
+duration jitter. In these cases an error bit STA_PPSERROR is raised in
+the status word. Surviving samples are then processed by a three-stage
+median filter. The median sample drives the FLL, while the difference
+between the other two samples represents the frequency dispersion.
+Frequency dispersion samples are averaged and used as a stabiity
+estimate. If this estimate is below a threshold MAXFREQ/4 (25 ppm), the
+median sample is used to correct the oscillator frequency pps_freq with
+a weight expressed as a shift PPS_AVG (2).
+
+Initially, an approximate value for the oscillator frequency is not
+known, so the duration of the calibration interval must be kept small to
+avoid overflowing the tick. The time difference at the end of the
+calibration interval is measured. If greater than tick/4, the interval
+is reduced by half. If less than this fraction for four successive
+calibration intervals, the interval is doubled. This design
+automatically adapts to nominal jitter in the PPS signal, as well as the
+value of tick. The duration of the calibration interval is set by the
+pps_shift variable as a shift in powers of two. The minimum value
+PPS_SHIFT (2) is chosen so that with the highest CPU oscillator
+frequency 1024 Hz and frequency tolerance 100 ppm the tick will not
+overflow. The maximum value PPS_SHIFTMAX (8) is chosen such that the
+maximum averaging time is about 1000 s as determined by measurements of
+Allan variance [MIL93].
+
+Should the PPS signal fail, the current frequency estimate pps_freq
+continues to be used, so the nominal frequency remains correct subject
+only to the instability of the undisciplined oscillator. The procedure
+to save and restore the frequency estimate works as follows. When
+setting the frequency from a file, the time_freq value is set as the
+file value minus the pps_freq value; when retrieving the frequency, the
+two values are added before saving in the file. This scheme provides a
+seamless interface should the PPS signal fail or the kernel
+configuration change. Note that the frequency discipline is active
+whether or not the synchronization daemon is active. Since all Unix
+systems take some time after reboot to build a running system, usually
+by that time the discipline process has already settled down and the
+initial transients due to frequency discipline have damped out.
+3.1.4. External Clock Interface
+
+The external clock driver interface is implemented with two routines,
+microtime(), which returns the current clock time, and clock_set(),
+which furnishes the apparent system time derived from the kernel time
+variable. The latter routine is called only when the clock is set using
+the settimeofday() system call, but can be called from within the
+driver, such as when the year rolls over, for example.
+
+In the stock SunOS kernel and modified Ultrix and OSF/1 kernels, the
+microtime() routine returns the kernel time variable plus an
+interpolation between timer interrupts based on the contents of a
+hardware counter. In the case of an external clock, such as described
+above, the system clock is read directly from the hardware clock
+registers. Examples of external clock drivers are in the tprotime.c and
+hightime.c routines included in the kernel.tar.Z distribution.
+
+The external clock routines return a status code which indicates whether
+the clock is operating correctly and the nature of the problem, if not.
+The return code is interpreted by the ntp_gettime() system call, which
+transitions the status state machine to the TIME_ERR state if an error
+code is returned. This is the only error checking implemented for the
+external clock in the present version of the code.
+
+The simulator has been used to check the PLL operation over the design
+envelope of +-512 ms in time error and +-100 ppm in frequency error.
+This confirms that no overflows occur and that the loop initially
+converges in about 15 minutes for timer interrupt rates from 50 Hz to
+1024 Hz. The loop has a normal overshoot of a few percent and a final
+convergence time of several hours, depending on the initial time and
+frequency error.
+
+3.2. Leap Seconds
+
+It does not seem generally useful in the user application interface to
+provide additional details private to the kernel and synchronization
+protocol, such as stratum, reference identifier, reference timestamp and
+so forth. It would in principle be possible for the application to
+independently evaluate the quality of time and project into the future
+how long this time might be "valid." However, to do that properly would
+duplicate the functionality of the synchronization protocol and require
+knowledge of many mundane details of the platform architecture, such as
+the subnet configuration, reachability status and related variables. For
+the curious, the ntp_adjtime() system call can be used to reveal some of
+these mysteries.
+
+However, the user application may need to know whether a leap second is
+scheduled, since this might affect interval calculations spanning the
+event. A leap-warning condition is determined by the synchronization
+protocol (if remotely synchronized), by the timecode receiver (if
+available), or by the operator (if awake). This condition is set by the
+synchronization daemon on the day the leap second is to occur (30 June
+or 31 December, as announced) by specifying in a ntp_adjtime() system
+call a status bit of either STA_DEL, if a second is to be deleted, or
+STA_INS, if a second is to be inserted. Note that, on all occasions
+since the inception of the leap-second scheme, there has never been a
+deletion, nor is there likely to be one in future. If the bit is
+STA_DEL, the kernel adds one second to the system time immediately
+following second 23:59:58 and resets the clock state to TIME_WAIT. If
+the bit is STA_INS, the kernel subtracts one second from the system time
+immediately following second 23:59:59 and resets the clock stateto
+TIME_OOP, in effect causing system time to repeat second 59. Immediately
+following the repeated second, the kernel resets the clock status to
+TIME_WAIT.
+
+Following the leap operations, the clock remains in the TIME_WAIT state
+until both the STA_DEL and STA_INS status bits are reset. This provides
+both an unambiguous indication that a leap recently occured, as well as
+time for the daemon or operator to clear the warning condition.
+
+Depending upon the system call implementation, the reported time during
+a leap second may repeat (with the TIME_OOP return code set to advertise
+that fact) or be monotonically adjusted until system time "catches up"
+to reported time. With the latter scheme the reported time will be
+correct before and shortly after the leap second (depending on the
+number of microtime() calls during the leap second), but freeze or
+slowly advance during the leap second itself. However, Most programs
+will probably use the ctime() library routine to convert from timeval
+(seconds, microseconds) format to tm format (seconds, minutes,...). If
+this routine is modified to use the ntp_gettime() system call and
+inspect the return code, it could simply report the leap second as
+second 60.
+
+3.3. Clock Status State Machine
+
+The various options possible with the system clock model described in
+this memorandum require a careful examination of the state transitions,
+status indications and recovery procedures should a crucial signal or
+interface fail. In this section is presented a prototype state machine
+designed to support leap second insertion and deletion, as well as
+reveal various kinds of errors in the synchronization process. The
+states of this machine are decoded as follows:
+
+ TIME_OK If a PPS signal or external clock is present, it is
+ working properly and the system clock is derived
+ from it. If not, the synchronization daemon is
+ working properly and the system clock is
+ synchronized to a radio clock or one or more peers.
+
+ TIME_INS An insertion of one second in the system clock has
+ been declared following the last second of the
+ current day, but has not yet been executed.
+
+ TIME_DEL A deletion of the last second of the current day has
+ been declared, but not yet executed.
+
+ TIME_OOP An insertion of one second in the system clock has
+ been declared following the last second of the
+ current day. The second is in progress, but not yet
+ completed. Library conversion routines should
+ interpret this second as 23:59:60.
+
+ TIME_WAIT The scheduled leap event has occurred, but the
+ STA_DEL and STA_INS status bits have not yet been
+ cleared.
+
+ TIME_ERROR Either (a) the synchronization daemon has declared
+ the protocol is not working properly, (b) all
+ sources of outside synchronization have been lost or
+ (c) a PPS signal or external clock is present, but
+ not working properly.
+
+In all states the system clock is derived from either a PPS signal or
+external clock, if present, or the kernel time variable, if not. If a
+PPS error condition is recognized, the PPS signal is disabled and
+ntp_adjtime() updates are used instead. If an external clock error
+condition is recognized, the external clock is disabled and the kernel
+time variable is used instead.
+
+The state machine makes a transition once each second at an instant
+where the microseconds field of the kernel time variable overflows and
+one second is added to the seconds field. However, this condition is
+checked when the timer overflows, which may not coincide with the actual
+seconds increment. This may lead to some interesting anomalies, such as
+a status indication of a leap second in progress (TIME_OOP) when the
+leap second has already expired. This ambiguity is unavoidable, unless
+the timer interrupt is made synchronous with the system clock.
+
+The following state transitions are executed automatically by the kernel
+at rollover of the microseconds field:
+
+ any state -> TIME_ERROR This transition occurs when an error
+ condition is recognized and continues as long
+ as the condition persists. The error indication
+ overrides the normal state indication, but does
+ not affect the actual clock state. Therefore,
+ when the condition is cleared, the normal state
+ indication resumes.
+
+ TIME_OK->TIME_DEL This transition occurs if the STA_DEL bit is
+ set in the status word.
+
+ TIME_OK->TIME_INS This transition occurs if the STA_INS bit is
+ set in the status word.
+
+ TIME_INS->TIME_OOP This transition occurs immediately following
+ second 86,400 of the current day when an
+ insert-second event has been declared.
+
+ TIME_OOP->TIME_WAIT This transition occurs immediately following
+ second 86,401 of the current day; that is, one
+ second after entry to the TIME_OOP state.
+
+ TIME_DEL->TIME_WAIT This transition occurs immediately following
+ second 86,399 of the current day when a delete-
+ second event has been declared.
+
+ TIME_WAIT->TIME_OK This transition occurs when the STA_DEL and
+ STA_INS bits are cleared by an ntp_adjtime()
+ call.
+
+The following table summarizes the actions just before, during and just
+after a leap-second event. Each line in the table shows the UTC and NTP
+times at the beginning of the second. The left column shows the behavior
+when no leap event is to occur. In the middle column the state machine
+is in TIME_INS at the end of UTC second 23:59:59 and the NTP time has
+just reached 400. The NTP time is set back one second to 399 and the
+machine enters TIME_OOP. At the end of the repeated second the machine
+enters TIME_OK and the UTC and NTP times are again in correspondence. In
+the right column the state machine is in TIME_DEL at the end of UTC
+second 23:59:58 and the NTP time has just reached 399. The NTP time is
+incremented, the machine enters TIME_OK and both UTC and NTP times are
+again in correspondence.
+
+ No Leap Leap Insert Leap Delete
+ UTC NTP UTC NTP UTC NTP
+ ---------------------------------------------
+ 23:59:58|398 23:59:58|398 23:59:58|398
+ | | |
+ 23:59:59|399 23:59:59|399 00:00:00|400
+ | | |
+ 00:00:00|400 23:59:60|399 00:00:01|401
+ | | |
+ 00:00:01|401 00:00:00|400 00:00:02|402
+ | | |
+ 00:00:02|402 00:00:01|401 00:00:03|403
+ | | |
+To determine local midnight without fuss, the kernel code simply finds
+the residue of the time.tv_sec (or time.tv_sec + 1) value mod 86,400,
+but this requires a messy divide. Probably a better way to do this is to
+initialize an auxiliary counter in the settimeofday() routine using an
+ugly divide and increment the counter at the same time the time.tv_sec
+is incremented in the timer interrupt routine. For future embellishment.
+
+4. Programming Model and Interfaces
+
+This section describes the programming model for the synchronization
+daemon and user application programs. The ideas are based on suggestions
+from Jeff Mogul and Philip Gladstone and a similar interface designed by
+the latter. It is important to point out that the functionality of the
+original Unix adjtime() system call is preserved, so that the modified
+kernel will work as the unmodified one, should the new features not be
+in use. In this case the ntp_adjtime() system call can still be used to
+read and write kernel variables that might be used by a synchronization
+daemon other than NTP, for example.
+
+The kernel routines use the clock state variable time_state, which
+records whether the clock is synchronized, waiting for a leap second,
+etc. The value of this variable is returned as the result code by both
+the ntp_gettime() and ntp_adjtime() system calls. It is set implicitly
+by the STA_DEL and STA_INS status bits, as described previously. Values
+presently defined in the timex.h header file are as follows:
+
+ TIME_OK 0 no leap second warning
+ TIME_INS 1 insert leap second warning
+ TIME_DEL 2 delete leap second warning
+ TIME_OOP 3 leap second in progress
+ TIME_WAIT 4 leap second has occured
+ TIME_ERROR 5 clock not synchronized
+
+In case of a negative result code, the kernel has intercepted an invalid
+address or (in case of the ntp_adjtime() system call), a superuser
+violation.
+
+4.1. The ntp_gettime() System Call
+
+The syntax and semantics of the ntp_gettime() call are given in the
+following fragment of the timex.h header file. This file is identical,
+except for the SHIFT_HZ define, in the SunOS, Ultrix and OSF/1 kernel
+distributions. (The SHIFT_HZ define represents the logarithm to the base
+2 of the clock oscillator frequency specific to each system type.) Note
+that the timex.h file calls the syscall.h system header file, which must
+be modified to define the SYS_ntp_gettime system call specific to each
+system type. The kernel distributions include directions on how to do
+this.
+
+ /*
+ * This header file defines the Network Time Protocol (NTP)
+ * interfaces for user and daemon application programs. These are
+ * implemented using private system calls and data structures and
+ * require specific kernel support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int system call(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NTP user interface - used to read kernel clock values
+ * Note: maximum error = NTP synch distance = dispersion + delay /
+ * 2
+ * estimated error = NTP dispersion.
+ */
+ struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ long maxerror; /* maximum error (us) (ro) */
+ long esterror; /* estimated error (us) (ro) */
+ };
+
+The ntp_gettime() system call returns three read-only (ro) values in the
+ntptimeval structure: the current time in unix timeval format plus the
+maximum and estimated errors in microseconds. While the 32-bit long data
+type limits the error quantities to something more than an hour, in
+practice this is not significant, since the protocol itself will declare
+an unsynchronized condition well below that limit. In the NTP Version 3
+specification, if the protocol computes either of these values in excess
+of 16 seconds, they are clamped to that value and the system clock
+declared unsynchronized.
+
+Following is a detailed description of the ntptimeval structure members.
+
+struct timeval time (ro)
+
+ This member is the current system time expressed as a Unix timeval
+ structure. The timeval structure consists of two 32-bit words; the
+ first is the number of seconds past 1 January 1970 assuming no
+ intervening leap-second insertions or deletions, while the second
+ is the number of microseconds within the second.
+
+long maxerror (ro)
+
+ This member is the value of the time_maxerror kernel variable,
+ which represents the maximum error of the indicated time relative
+ to the primary synchronization source, in microseconds. For NTP,
+ the value is initialized by a ntp_adjtime() call to the
+ synchronization distance, which is equal to the root dispersion
+ plus one-half the root delay. It is increased by a small amount
+ (time_tolerance) each second to reflect the maximum clock frequency
+ error. This variable is provided bu a ntp-adjtime() system call and
+ modified by the kernel, but is otherwise not used by the kernel.
+
+long esterror (ro)
+
+ This member is the value of the time_esterror kernel variable,
+ which represents the expected error of the indicated time relative
+ to the primary synchronization source, in microseconds. For NTP,
+ the value is determined as the root dispersion, which represents
+ the best estimate of the actual error of the system clock based on
+ its past behavior, together with observations of multiple clocks
+ within the peer group. This variable is provided bu a ntp-adjtime()
+ system call, but is otherwise not used by the kernel.
+
+4.2. The ntp_adjtime() System Call
+
+The syntax and semantics of the ntp_adjtime() call are given in the
+following fragment of the timex.h header file. Note that, as in the
+ntp_gettime() system call, the syscall.h system header file must be
+modified to define the SYS_ntp_adjtime system call specific to each
+system type. In the fragment, rw = read/write, ro = read-only, wo =
+write-only.
+
+ /*
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int system call(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ * NTP daemon interface - used to discipline kernel clock
+ * oscillator
+ */
+ struct timex {
+ unsigned int mode; /* mode selector (wo) */
+ long offset; /* time offset (us) (rw) */
+ long frequency; /* frequency offset (scaled ppm) (rw)
+ */
+ long maxerror; /* maximum error (us) (rw) */
+ long esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ long constant; /* pll time constant (rw) */
+ long precision; /* clock precision (us) (ro) */
+ long tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro)
+ */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+ };
+
+The ntp_adjtime() system call is used to read and write certain time-
+related kernel variables summarized below. Writing these variables can
+only be done in superuser mode. To write a variable, the mode structure
+member is set with one or more bits, one of which is assigned each of
+the following variables in turn. The current values for all variables
+are returned in any case; therefore, a mode argument of zero means to
+return these values without changing anything.
+
+Following is a description of the timex structure members.
+
+mode (wo)
+
+ This is a bit-coded variable selecting one or more structure
+ members, with one bit assigned each member. If a bit is set, the
+ value of the associated member variable is copied to the
+ corresponding kernel variable; if not, the member is ignored. The
+ bits are assigned as given in the following, with the variable name
+ indicated in parens. Note that the precision, tolerance and PPS
+ variables are determined by the kernel and cannot be changed by
+ ntp_adjtime().
+
+ MOD_OFFSET 0x0001 time offset (offset)
+ MOD_FREQUENCY 0x0002 frequency offset (frequency)
+ MOD_MAXERROR 0x0004 maximum time error (maxerror)
+ MOD_ESTERROR 0x0008 estimated time error (esterror)
+ MOD_STATUS 0x0010 clock status (status)
+ MOD_TIMECONST 0x0020 pll time constant (constant)
+ MOD_CLKB 0x4000 set clock B
+ MOD_CLKA 0x8000 set clock A
+
+ Note that the MOD_CLK0 and MOD_CLK1 bits are intended for those
+ systems where more than one hardware clock is available for backup,
+ such as in Tandem Non-Stop computers. Presumably, in such cases
+ each clock would have its own oscillator and require a separate PLL
+ for each. Refinements to this model are for further study. The
+ interpretation of these bits is as follows:
+
+offset (rw)
+
+ If selected, this member specifies the time adjustment, in
+ microseconds. The absolute value must be less than MAXPHASE
+ (128000) microseconds defined in the timex.h header file. On
+ return, this member contains the residual offset remaining between
+ a previously specified offset and the current system time, in
+ microseconds.
+
+frequency (rw)
+
+ If selected, this member replaces the value of the time_frequency
+ kernel variable. The value is in ppm, with the integer part in the
+ high order 16 bits and fraction in the low order 16 bits. The
+ absolute value must be in the range less than MAXFREQ (100) ppm
+ defined in the timex.h header file.
+
+ The time_freq variable represents the frequency offset of the CPU
+ clock oscillator. It is recalculated as each update to the system
+ clock is determined by the offset member of the timex structure. It
+ is usually set from a value stored in a file when the
+ synchronization daemon is first started. The current value is
+ usually retrieved via this member and written to the file about
+ once per hour.
+
+maxerror (rw)
+
+ If selected, this member replaces the value of the time_maxerror
+ kernel variable, in microseconds. This is the same variable as in
+ the ntp_getime() system call.
+
+esterror (rw)
+
+ If selected, this member replaces the value of the time_esterror
+ kernel variable, in microseconds. This is the same variable as in
+ the ntp_getime() system call.
+
+int status (rw)
+
+ If selected, this member replaces the value of the time_status
+ kernel variable. This variable controls the state machine used to
+ insert or delete leap seconds and shows the status of the
+ timekeeping system, PPS signal and external oscillator, if
+ configured.
+
+ STA_PLL 0x0001 enable PLL updates (r/w)
+ STA_PPSFREQ 0x0002 enable PPS freq discipline (r/w)
+ STA_PPSTIME 0x0004 enable PPS time discipline (r/w)
+ STA_INS 0x0010 insert leap (r/w)
+ STA_DEL 0x0020 delete leap (r/w)
+ STA_UNSYNC 0x0040 clock unsynchronized (r/w)
+ STA_PPSSIGNAL 0x0100 PPS signal present (r)
+ STA_PPSJITTER 0x0200 PPS signal jitter exceeded (r)
+ STA_PPSWANDER 0x0400 PPS signal wander exceeded (r)
+ STA_PPSERROR 0x0800 PPS signal calibration error (r)
+ STA_CLOCKERR 0x1000 clock hardware fault (r)
+
+ The interpretation of these bits is as follows:
+
+ STA_PLL set/cleared by the caller to enable PLL updates
+
+ STA_PPSFREQ set/cleared by the caller to enable PPS frequency
+ discipline
+
+ STA_PPSTIME set/cleared by the caller to enable PPS time
+ discipline
+
+ STA_INS set by the caller to insert a leap second at the end
+ of the current day; cleared by the caller after the
+ event
+
+ STA_DEL set by the caller to delete a leap second at the end
+ of the current day; cleared by the caller after the
+ event
+
+ STA_UNSYNC set/cleared by the caller to indicate clock
+ unsynchronized (e.g., when no peers are reachable)
+
+ STA_PPSSIGNAL set/cleared by the hardpps() fragment to indicate
+ PPS signal present
+
+ STA_PPSJITTER set/cleared by the hardpps() fragment to indicates
+ PPS signal jitter exceeded
+
+ STA_PPSWANDER set/cleared by the hardpps() fragment to indicates
+ PPS signal wander exceeded
+
+ STA_PPSERROR set/cleared by the hardpps() fragment to indicates
+ PPS signal calibration error
+
+ STA_CLOCKERR set/cleared by the external hardware clock driver to
+ indicate hardware fault
+
+ An error condition is raised when (a) either STA_UNSYNC or
+ STA_CLOCKERR is set (loss of synchronization), (b) STA_PPSFREQ or
+ STA_PPSTIME is set and STA_PPSSIGNAL is clear (loss of PPS signal),
+ (c) STA_PPSTIME and STA_PPSJITTER are both set (jitter exceeded),
+ (d) STA_PPSFREQ is set and either STA_PPSWANDER or STA_PPSERROR is
+ set (wander exceeded). An error condition results in a system call
+ return code of TIME_ERROR.
+
+constant (rw)
+
+ If selected, this member replaces the value of the time_constant
+ kernel variable. The value must be between zero and MAXTC (6)
+ defined in the timex.h header file.
+
+ The time_constant variable determines the bandwidth or "stiffness"
+ of the PLL. The value is used as a shift between zero and MAXTC
+ (6), with the effective PLL time constant equal to a multiple of (1
+ << time_constant), in seconds. For room-temperature quartz
+ oscillators, the recommended default value is 2, which corresponds
+ to a PLL time constant of about 900 s and a maximum update interval
+ of about 64 s. The maximum update interval scales directly with the
+ time constant, so that at the maximum time constant of 6, the
+ update interval can be as large as 1024 s.
+
+ Values of time_constant between zero and 2 can be used if quick
+ convergence is necessary; values between 2 and 6 can be used to
+ reduce network load, but at a modest cost in accuracy. Values above
+ 6 are appropriate only if an precision external oscillator is
+ present.
+
+precision (ro)
+
+ This is the current value of the time_precision kernel variable in
+ microseconds.
+
+ The time_precision variable represents the maximum error in reading
+ the system clock, in microseconds. It is usually based on the
+ number of microseconds between timer interrupts (tick), 10000 us
+ for the SunOS kernel, 3906 us for the Ultrix kernel, 976 us for the
+ OSF/1 kernel. However, in cases where the time can be interpolated
+ between timer interrupts with microsecond resolution, such as in
+ the stock SunOS kernel and modified Ultrix and OSF/1 kernels, the
+ precision is specified as 1 us. In cases where a PPS signal or
+ external oscillator is available, the precision can depend on the
+ operating condition of the signal or oscillator. This variable is
+ determined by the kernel for use by the synchronization daemon, but
+ is otherwise not used by the kernel.
+
+tolerance (ro)
+
+ This is the current value of the time_tolerance kernel variable.
+ The value is in ppm, with the integer part in the high order 16
+ bits and fraction in the low order 16 bits.
+
+ The time_tolerance variable represents the maximum frequency error
+ in ppm of the particular CPU clock oscillator and is a property of
+ the hardware; however, in principle it could change as result of
+ the presence of external discipline signals, for instance.
+
+ The recommended value for time_tolerance MAXFREQ (200) ppm is
+ appropriate for room-temperature quartz oscillators used in typical
+ workstations. However, it can change due to the operating condition
+ of the PPS signal and/or external oscillator. With either the PPS
+ signal or external oscillator, the recommended value for MAXFREQ is
+ 100 ppm.
+
+The following members are defined only if the PPS_SYNC option is
+specified in the kernel configuration file. These members are useful
+primarily as a monitoring and evalutation tool. These variables can be
+written only by the kernel.
+
+ppsfreq (ro)
+
+ This is the current value of the pps_freq kernel variable, which is
+ the CPU clock oscillator frequency offset relative to the PPS
+ discipline signal. The value is in ppm, with the integer part in
+ the high order 16 bits and fraction in the low order 16 bits.
+
+jitter (ro)
+
+ This is the current value of the pps_jitter kernel variable, which
+ is the average PPS time dispersion measured by the time-offset
+ median filter, in microseconds.
+
+shift (ro)
+
+ This is the current value of the pps_shift kernel variable, which
+ determines the duration of the calibration interval as the value of
+ 1 << pps_shift, in seconds.
+stabil (ro)
+
+ This is the current value of the pps_stabil kernel variable, which
+ is the average PPS frequency dispersion measured by the frequency-
+ offset median filter. The value is in ppm, with the integer part in
+ the high order 16 bits and fraction in the low order 16 bits.
+
+jitcnt (ro)
+
+ This is the current value of the pps_jitcnt kernel variable, counts
+ the number of PPS signals where the average jitter exceeds the
+ threshold MAXTIME (200 us).
+
+calcnt (ro)
+
+ This is the current value of the pps_calcnt kernel variable, which
+ counts the number of frequency calibration intervals. The duration
+ of these intervals can range from 4 to 256 seconds, as determined
+ by the pps_shift kernel variable.
+
+errcnt (ro)
+
+ This is the current value of the pps_errcnt kernel variable, which
+ counts the number of frequency calibration cycles where (a) the
+ apparent frequency offset is greater than MAXFREQ (100 ppm) or (b)
+ the interval jitter exceeds tick * 2.
+
+stbcnt (ro)
+
+ This is the current value of the pps_discnt kernel variable, which
+ counts the number of calibration intervals where the average
+ stability exceeds the threshold MAXFREQ / 4 (25 ppm).
+
+7. References
+
+[MIL91] Mills, D.L. Internet time synchronization: the Network Time
+Protocol, IEEE Trans. Communications COM-39, 10 (October 1991),
+1482-1493. Also in: Yang, Z., and T.A. Marsland (Eds.). Global
+States and Time in Distributed Systems, IEEE Press, Los Alamitos,
+CA, 91-102.
+
+[MIL92a] Mills, D.L. Network Time Protocol (Version 3) specification,
+implementation and analysis, RFC 1305, University of Delaware, March
+1992, 113 pp.
+
+[MIL92b] Mills, D.L. Modelling and analysis of computer network clocks,
+Electrical Engineering Department Report 92-5-2, University of Delaware,
+May 1992, 29 pp.
+
+[MIL92c] Mills, D.L. Simple Network Time Protocol (SNTP), RFC 1361,
+University of Delaware, August 1992, 10 pp.
+
+[MIL93] Mills, D.L. Precision synchronizatin of computer network clocks,
+Electrical Engineering Department Report 93-11-1, University of
+Delaware, November 1993, 66 pp.
+
+[LEV89] Levine, J., M. Weiss, D. Davis, D. Allan, and D. Sullivan. The
+NIST automated computer time service. J. Research National Institute of
+Standards and Technology 94, 5 (September-October 1989), 311-321.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+3 April 1994
diff --git a/usr.sbin/xntpd/doc/README.magic b/usr.sbin/xntpd/doc/README.magic
new file mode 100644
index 0000000..f473a92
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.magic
@@ -0,0 +1,346 @@
+ Magic Tricks for Precision Timekeeping
+
+ Revised 19 September 1993
+
+Note: This information file is included in the NTP Version 3
+distribution (xntp3.tar.Z) as the file README.magic. This distribution
+can be obtained via anonymous ftp from louie.udel.edu in the directory
+pub/ntp.
+
+1. Introduction
+
+It most cases it is possible using NTP to synchronize a number of hosts
+on an Ethernet or moderately loaded T1 network to a radio clock within a
+few tens of milliseconds with no particular care in selecting the radio
+clock or configuring the servers on the network. This may be adequate
+for the majority of applications; however, modern workstations and high
+speed networks can do much better than that, generally to within some
+fraction of a millisecond, by using special care in the design of the
+hardware and software interfaces.
+
+The timekeeping accuracy of a NTP-synchronized host depends on two
+quantities: the delay due to hardware and software processing and the
+accumulated jitter due to such things as clock reading precision and
+varying latencies in hardware and software queuing. Processing delays
+directly affect the timekeeping accuracy, unless minimized by systematic
+analysis and adjustment. Jitter, on the other hand, can be essentially
+removed, as long as the statistical properties are unbiased, by the low-
+pass filtering of the phase-lock loop incorporated in the NTP local
+clock model.
+
+This note discusses issues in the connection of external time sources
+such as radio clocks and related timing signals to a primary (stratum-1)
+NTP time server. Of principal concern are various techniques that can be
+utilized to improve the accuracy and precision of the time accuracy and
+frequency stability. Radio clocks are most often connected to a time
+server using a serial asynchronous port. Much of the discussion in this
+memorandum has to do with ways in which the delay incurred in this type
+of connection can be controlled and ways in which the jitter due to
+various causes can be minimized.
+
+However, there are ways other than serial ports to connect a radio
+clock, including special purpose hardware devices for some
+architectures, and even unusual applications of existing interface
+devices, such as the audio codec provided in some systems. Many of these
+methods can yield accuracies as good as any attainable with a serial
+port. For those radio clocks equipped with an IRIG-B signal output, for
+example, a hardware device is available for the Sun SPARCstation; see
+the xntpd.8 manual page in the doc directory of the NTP Version 3
+distribution for further information. In addition, it is possible to
+decode the IRIG-B signal using the audio codec included in the Sun
+SPARCstation and a special kernel driver described in the irig.txt file
+in the doc directory of the NTP Version 3 distribution. These devices
+will not be discussed further in this memorandum.
+
+2. Connection via Serial Port
+
+Most radio clocks produce an ASCII timecode with a precision only to the
+millisecond. This results in a maximum peak-to-peak (p-p) jitter in the
+clock readings of one millisecond. However, assuming the read requests
+are statistically independent of the clock update times, the reading
+error is uniformly distributed over the millisecond, so that the average
+over a large number of readings will make the clock appear 0.5 ms late.
+To compensate for this, it is only necessary to add 0.5 ms to its
+reading before further processing by the NTP algorithms.
+
+Radio clocks are usually connected to the host computer using a serial
+port operating at a typical speed of 9600 baud. The on-time reference
+epoch for the timecode is usually the start bit of a designated
+character, usually <CR>, which is part of the timecode. The UART chip
+implementing the serial port most often has a sample clock of eight to
+16 times the basic baud rate. Assuming the sample clock starts midway in
+the start bit and continues to midway in the first stop bit, this
+creates a processing delay of 10.5 baud times, or about 1.1 ms, relative
+to the start bit of the character. The jitter contribution is usually no
+more than a couple of sample-clock periods, or about 26 usec p-p. This
+is small compared to the clock reading jitter and can be ignored. Thus,
+the UART delay can be considered constant, so the hardware contribution
+to the total mean delay budget is 0.5 + 1.1 = 1.6 ms.
+
+In some kernel serial port drivers, in particular, the Sun zs driver,
+an intentional delay is introduce in input character processing when the
+first character is received after an idle period. A batch of characters
+is passed to the calling program when either (a) a timeout in the
+neighborhood of 10 ms expires or (b) an input buffer fills up. The
+intent in this design is to reduce the interrupt load on the processor
+by batching the characters where possible. Obviously, this can cause
+severe problems for precision timekeeping. It is possible to patch the
+zs driver to eliminate the jitter due to this cause; contact the author
+for further details. However, there is a better solution which will be
+described later in this note. The problem does not appear to be present
+in the Serial/Parallel Controller (SPC) for the SBus, which contains
+eight serial asynchronous ports along with a parallel port. The
+measurements referred to below were made using this controller.
+
+Good timekeeping depends strongly on the means available to capture an
+accurate sample of the local clock or timestamp at the instant the stop
+bit of the on-time character is found; therefore, the code path delay
+between the character interrupt routine and the first place a timestamp
+can be captured is very important, since on some systems such as Sun
+SPARCstations, this path can be astonishingly long. The Sun scheduling
+mechanisms involve both a hardware interrupt queue and a software
+interrupt queue. Entries are made on the hardware queue as the interrupt
+is signalled and generally with the lowest latency, estimated at 20-30
+microseconds (usec) for a SPARC 4/65 IPC. Then, after minimal
+processing, an entry is made on the software queue for later processing
+in order of software interrupt priority. Finally, the software interrupt
+unblocks the NTP daemon which calculates the current local clock offset
+and introduces corrections as required.
+
+Opportunities exist to capture timestamps at the hardware interrupt
+time, software interrupt time and at the time the NTP daemon is
+activated, but these involve various degrees of kernel trespass and
+hardware gimmicks. To gain some idea of the severity of the errors
+introduced at each of these stages, measurements were made using a Sun
+4/65 IPC and a test setup that results in an error between the host
+clock and a precision time source (calibrated cesium clock) no greater
+than 0.1 ms. The total delay from the on-time epoch to when the NTP
+daemon is activated was measured at 8.3 ms in an otherwise idle system,
+but increased on rare occasion to over 25 ms under load, even when the
+NTP daemon was operated at the highest available software priority
+level. Since 1.6 ms of the total delay is due to the hardware, the
+remaining 6.7 ms represents the total code path delay accounting for all
+software processing from the hardware interrupt to the NTP daemon.
+
+It is commonly observed that the latency variations (jitter) in typical
+real-time applications scale as the processing delay. In the case above,
+the ratio of the maximum observed delay (25 ms) to the baseline code
+path delay (8.3 ms) is about three. It is natural to expect that this
+ratio remain the same or less as the code path between the hardware
+interrupt and where the timestamp is captured is reduced. However, in
+general this requires trespass on kernel facilities and/or making use of
+features not common to all or even most Unix implementations. In order
+to assess the cost and benefits of increasingly more aggressive insult
+to the hardware and software of the system, it is useful to construct a
+budget of the code path delay at each of the timestamp opportunity
+times. For instance, on Unix systems which include support for the SIGIO
+facility, it is possible to intervene at the time the software interrupt
+is serviced. The NTP daemon code uses this facility, when available, to
+capture a timestamp and save it along with the data in a buffer for
+later processing. This reduces the total code path delay from 6.7 ms to
+3.5 ms on an otherwise idle system. This reduction applies to all input
+processing, including network interfaces and serial ports.
+
+3. The CLK Mode
+
+By far the best place to capture the timestamp is right in the kernel
+interrupt routine, but this gerally requires intruding in the code
+itself, which can be intricate and architecture dependent. The next best
+place is in some routine close to the interrupt routine on the code
+path. There are two ways to do this, depending on the ancestry of the
+Unix operating system variant. Older systems based primarily on the
+original Unix 4.3bsd support what is called a line discipline module,
+which is a hunk of code with more-or-less well defined interface
+specifications that can get in the way, so to speak, of the code path
+between the interrupt routine and the remainder of the serial port
+processing. Newer systems based on System V STREAMS can do the same
+thing using what is called a streams module. Both approaches are
+supported in the NTP Version 3 distribution, as described in the README
+files in the kernel directory of the distribution. In either case,
+header and source files have to be copied to the kernel build tree and
+certain tables in the kernel have to be modified. In neither case,
+however, are kernel sources required. In order to take advantage of
+this, the clock driver must include code to activate the feature and
+extract the timestamp. At present, this support is included in the clock
+drivers for the Spectracom WWVB clock (WWVB define), the PSTI/Traconex
+WWV/WWVH clock (PST define) and a special one-pulse-per-second (pps)
+signal (PPSCLK define) described later. If justified, support can be
+easily added to most other clock drivers as well. For future reference,
+these modules operating with supported drivers will be called the CLK
+support.
+
+The CLK line discipline and STREAMS modules operate in the same way.
+They look for a designated character, usually <CR>, and stuff a Unix
+timestamp in the data stream following that character whenever it is
+found. Eventually, the data arrive at the particular clock driver
+configured in the NTP Version 3 distribution. The driver then uses the
+timestamp as a precise reference epoch, subject to the earlier
+processing delays and jitter budget, for future reference. In order to
+gain some insight as to the effectiveness of this approach, measurements
+were made using the same test setup described above. The total delay
+from the on-time epoch to the instant when the timestamp is captured was
+measured at 3.5 ms. Thus, the code path delay is this value less the
+hardware delay 3.5 - 1.6 = 1.9 ms.
+
+While the improvement in accuracy in the baseline case is significant,
+there is another factor, at least in Sun systems, that makes it even
+more worthwhile. When processing the code path up to the CLK module, the
+priority is apparently higher than for processing beyond it. In case of
+heavy CPU activity, this can lead to relatively long tails in the
+processing delays for the driver, which of course are avoided by
+capturing the timestamp early in the code path.
+
+4. The PPSCLK Mode
+
+Many timing receivers can produce a 1-pps signal of considerably better
+precision than the ASCII timecode. Using this signal, it is possible to
+avoid the 1-ms p-p jitter and 1.6 ms hardware timecode adjustment
+entirely. However, a device is required to interface this signal to the
+hardware and operating system. In general, this requires some sort of
+level converter and pulse generator that can turn the 1-pps signal on-
+time transition into a valid character. An example of such a device is
+described in the gadget directory of the NTP Version 3 distribution.
+Although many different circuit designs could be used as well, this
+particular device generates a single 26-usec start bit for each 1-pps
+signal on-time transition. This appears to the UART operating at 38.4K
+baud as an ASCII DEL (hex FF).
+
+Now, assuming a serial port can be dedicated to this purpose, a source
+of 1-pps character interrupts is available and can be used to provide a
+precision reference. The NTP Version 3 daemon can be configured to
+utilize this feature by specifying the PPSCLK define, which requires the
+CLK module and gadget box described above. The character resulting from
+each 1-pps signal on-time transition is intercepted by the CLK module
+and a timestamp is inserted in the data stream. An interrupt is created
+for the device driver, which reads the timestamp and discards the DEL
+character. Since the timestamp is captured at the on-time transition,
+the seconds-fraction portion is the offset between the local clock and
+the on-time epoch less the UART delay of 273 usec at 38.4K baud. If the
+local clock is within +-0.5 second of this epoch, as determined by other
+means, the local clock correction is taken as the offset itself, if
+between zero and 0.5 s, and the offset minus one second, if between 0.5
+and 1.0 s. In the NTP daemon the resulting correction is first processed
+by a multi-stage median/trimmed mean filter to remove residual jitter
+and then processed by the usual NTP algorithms.
+
+The baseline delay between the on-time transition and the timestamp
+capture was measured at 400+-10 usec on an otherwise idle test system.
+As the UART delay at 38.4K baud is about 270 usec, the difference, 130
+usec, must be due to the hardware interrupt latency plus the time to
+call the microtime() routine which actually reads the system clock and
+microsecond counter. For these measurements the assembly-coded version
+of this routine described in the ppsclock directory of the NTP Version 3
+distribution was used. This routine reduces the time to read the system
+clock from 42-85 usec with the native Sun C-coded routine to about 3
+usec using the microtime() assembly-coded routine and can be ignored.
+Thus, the 130 usec must be accounted for in interrupt service, register
+window, context switching, streams operations and measurement
+uncertainty, which is probably not unreasonable. The reason for the
+difference between the this figure and the previously calculated value
+of 1.9 ms for the CLK module and serial ASCII timecode is probably due
+to the fact that all STREAMS modules other than the CLK module were
+removed, since the serial port is not used for ordinary ASCII data.
+
+An interesting feature of this approach is that the 1-pps signal is not
+necessarily associated with any particular radio clock and, indeed,
+there may be no such clock at all. Some precision timekeeping equipment,
+such as cesium clocks, VLF receivers and LORAN-C timing receivers
+produce only a precision 1-pps signal and rely on other mechanisms to
+resolve the second of the day and day of the year. It is possible for an
+NTP-synchronized host to derive the latter information using other NTP
+peers, presumably properly synchronized within +-0.5 second, and to
+remove residual jitter using the 1-pps signal. This makes it quite
+practical to deliver precision time to local clients when the subnet
+paths to remote primary servers are heavily congested. In extreme cases
+like this, it has been found useful to increase the tracking aperture
+from +-128 ms to as high as +-512 ms.
+
+In the current implementation the radio timecode and 1-pps signal are
+separately processed. The timecode capture and CLK support, if provided
+by the radio driver, operate the same way whether or not the PPSCLK
+support is enabled. If the local clock is reliably synchronized within
++-0.5 s and the 1-pps signal has been valid for some number of seconds,
+its offset rather than whatever synchronization source has been selected
+is used instead. However, while a this procedure delivers a new offset
+estimate every second, the local clock is updated only as each valid
+update is computed for the peer selected as the source of
+synchronization.
+
+However, there is a hazard to the use of the 1-pps signal in this way if
+the radio generating the 1-pps signal misbehaves or loses
+synchronization with its transmitter. In such a case the radio might
+indicate the error, but the system has no way to associate the error
+with the 1-pps signal. To deal with this problem the prefer parameter
+described in the xntpd.8 man page in the doc directory of the NTP
+Version 3 distribution can be used both to cause the clock selection
+algorithm to choose a preferred peer, all other things being equal, as
+well as associate the error indications in such a way that the 1-pps
+signal will be disregarded if the peer stops providing valid updates,
+such as would occur in an error condition. The prefer parameter can be
+used in other situations as well when preference is to be given a
+particular source of synchronization.
+
+5. The PPS Mode
+
+For the ultimate accuracy and lowest jitter, it would be best to
+eliminate the UART and capture the 1-pps on-time transition directly
+using an appropriate interface. This is in fact possible using a
+modified serial port driver and data lead in the serial port interface
+cable. In this scheme, described in detail in the ppsclock directory of
+the NTP Version 3 distribution, the 1-pps source is connected via the
+previously described gadget box to the carrier-detect lead of a serial
+port. Happily, this can be the same port used for a radio clock, for
+example, or another unrelated serial device. The scheme, referred to
+subsequently as the PPS mode, is specific to the SunOS 4.1.x kernel and
+requires a special STREAMS module. Instructions on how to build the
+kernel are also included in that directory.
+
+Except for special-purpose interface modules, such as the KSI/Odetics
+TPRO IRIG-B decoder and the modified audio driver for the IRIG-B signal
+mentioned previously, the PPS mode provides the most accurate and
+precise timestamp available. There is essentially no latency and the
+timestamp is captured within 20-30 usec of the on-time epoch.
+
+The PPS mode requires the PPSPPS define and one of the radio clock
+serial ports to be selected as the PPS interface. This is the port which
+handles the 1-pps signal; however, the signal path has nothing to do
+with the ordinary serial data path; the two signals are not related,
+other than by the need to activate the PPS mode and pass the file
+descriptor to a common processing routine. Thus, for the port to be
+selected for the PPS function, the define for the associated radio clock
+needs to have a PPS suffix. In case of multiple radio clocks on a single
+time server, the PPS suffix is necessary on only one of them; more than
+one PPS suffix would be an error.
+
+The PPS mode works just like the CLK mode in the treatment of the prefer
+parameter and indicated peer errors. As in the CLK mode, only the offset
+within the second is used and only when the offset is less than +-0.5 s.
+However, the precision of the clock adjustments is usually so fine that
+the error budget is dominated by the inherent short-term stability of
+typical computer local clock oscillators. Therefore, it is advisable to
+reduce the poll interval for the preferred peer from the default 64 s to
+something less, like 16 s. This is done using the minpoll and maxpoll
+parameters of the peer or server command associated with the clock.
+These parameters take as arguments a power of 2, in seconds, which
+becomes the poll interval and, indirectly, affects the bandwidth of the
+tracking loop.
+
+6. Results and Conclusions
+
+It is clear from the above that substantial improvements in timekeeping
+accuracy are possible with varying degrees of hardware and software
+intrusion. While the ultimate accuracy depends on the jitter and wander
+characteristics of the computer local oscillator, it is possible to
+reduce jitter to a negligible degree simply by processing with the NTP
+phase-lock loop and local clock algorithms. The residual jitter using
+the PPS mode on a Sun4 IPC is typically in the 40-100 usec range, while
+the wander is rarely more than twice that under typical environmental
+room conditions.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+25 August 1993
diff --git a/usr.sbin/xntpd/doc/README.refclock b/usr.sbin/xntpd/doc/README.refclock
new file mode 100644
index 0000000..8c6c228
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.refclock
@@ -0,0 +1,1421 @@
+ Information on Reference Clock Drivers
+
+ Revised 5 August 1994
+
+Support for most of the commonly available radio clocks is included in
+the default configuration of xntpd. Individual clocks can be activated
+by configuration file commands, specifically the server and fudge
+commands described in the xntpd.8 man page. This file contains
+information useful in configuring and operating these clocks. Note that
+the man pages and documentation files mentioned in this note can be
+found in the ./doc directory of the xntp3 distribution.
+
+Radio clocks by convention have addresses in the form 127.127.t.u, where
+"t" is the clock type and "u" is a unit number in the range 0-3 used to
+distinguish multiple instances of clocks of the same type. Most of these
+clocks require support in the form of a serial port or special bus
+peripheral. The particular device is normally specified by adding a soft
+link /dev/device%d to the particular hardware device involved. The name
+"device" is compiled in the driver according to the table below. The
+table shows the type number, device name, short description used in some
+displays, and long description used in other displays.
+
+Type Device Name Description
+-------------------------------------------------------
+1 (none) LOCAL Undisciplined Local Clock
+2 trak GPS_TRAK TRAK 8820 GPS Receiver
+3 pst WWV_PST PSTI/Traconex WWV/WWVH Receiver
+4 wwvb WWVB_SPEC Spectracom WWVB Receiver
+5 goes GPS_GOES_TRUE TrueTime GPS/GOES Receivers
+6 irig IRIG_AUDIO IRIG Audio Decoder
+7 chu CHU Scratchbuilt CHU Receiver
+8 refclock- GENERIC Generic Reference Clock Driver
+9 gps GPS_MX4200 Magnavox MX4200 GPS Receiver
+10 gps GPS_AS2201 Austron 2201A GPS Receiver
+11 omega OMEGA_TRUE TrueTime OM-DC OMEGA Receiver
+12 tpro IRIG_TPRO KSI/Odetics TPRO/S IRIG Interface
+13 leitch ATOM_LEITCH Leitch CSD 5300 Master Clock Controller
+14 ees MSF_EES EES M201 MSF Receiver
+15 gpstm GPS_TRUE TrueTime GPS/TM-TMD Receiver
+16 * GPS_BANC Bancomm GPS/IRIG Receiver
+17 datum GPS_DATUM Datum Precision Time System
+18 acts NIST_ACTS NIST Automated Computer Time Service
+19 heath WWV_HEATH Heath WWV/WWVH Receiver
+20 nmea GPS_NMEA Generic NMEA GPS Receiver
+21 * GPS_MOTO Motorola Six Gun GPS Receiver
+22 pps ATOM_PPS PPS Clock Discipline
+
+* Not yet available.
+
+A radio clock is specified in the configuration using the server command
+
+ server 127.127.t.u [ prefer ] [ mode m ]
+
+where t is the type number above and u can be in the range 0-3,
+conventionally 1. Ordinarily, this is the only command necessary to
+configure a radio clock. The optional prefer keyword can be used to
+modify the clock selection algorithm as described in Appendix B. For
+those clock drivers that support multiple modes of operation, the
+optional mode parameter selects which one. This parameter affects the
+operation of each driver as described in Appendix A.
+
+In rare cases a fudge command is necessary to specify additional
+details. This command has the following syntax
+
+fudge 127.127.t.u [stratum s] [refid r] [time1 t1] [time2 t2] [flags]
+and must follow the corresponding server command in the configuration
+file. The optional fields following the clock address are interpreted as
+follows:
+
+stratum s The s, a decimal number in the range 0-15, overrides the
+ default stratum assigned by the driver.
+
+refid r The r, a 4-character, null-terminated ASCII string, overrides
+ the reference identifier assigned by the driver.
+
+time1 t1 The t1, a fixed-point decimal number in seconds, specifies a
+ constant to be added to the time offset produced by the
+ driver. This provides a way to correct a systematic error or
+ bias on the part of the particular clock.
+
+time2 t2 The t2, a fixed-point decimal number, is interpreted in a
+ driver-dependent way. See the descriptions of specific clock
+ drivers in Appendix A.
+
+There are four optional flags named flag1, flag2, flag3 and flag4. A
+flag is specified in the form <keyword> <value>, where <keyword> is one
+of the flag names and <value> is either 0 or 1, as appropriate. Two of
+the flags are generic and are interpreted by all applicable drivers, and
+two are driver dependent. The generic ones are as follows:
+
+flag4 This flag is used to enable detailed status monitoring and
+ event recording. The data collected are written to the
+ clockstats file maintained by the filegen utility (See the
+ xntpd.8 man page). This file is normally processed by a cron
+ job run once per day to produce summary statistics and
+ performance data. The ./scripts/stats directory contains a
+ number of shell and awk scripts for this, as well as S-
+ language programs that produce PostScript plots of performance
+ data.
+
+flag3 This flag is used with Sun SPARCstation baseboard serial ports
+ to assign the ppsclock streams driver for a 1-pps signal
+ produced by some radio clocks and timekeeping devices. See the
+ dscription of the PPS Clock Discipline Driver (type 22) in
+ Appendix A for further information.
+
+Note that the fudge factors above can be changed at run time using the
+xntpdc program (see the xntpdc.8 man page). This program does not have
+to be run on the server machine itself, since it communicates with the
+xntpd daemon using cryptographically authenticated messages.
+
+The PPS Signal
+
+Some radio clocks and related timekeeping gear have a pulse-per-second
+(PPS) signal that can be used to discipline the local clock oscillator
+to a high degree of precision, typically to the order less than 50 us in
+time and 0.1 ppm in frequency. The PPS signal can be connected in either
+of two ways, either via the data leads of a serial port or via the modem
+control leads. Either way requires conversion of the PPS signal, usually
+at TTL levels, to RS232 levels, which can be done using a circuit such
+as described in the ./gadget directory of the xntp3 distribution.
+
+The data leads interface requires regenerating the PPS pulse and
+converting to RS232 signal levels, so that the pulse looks like a
+legitimate ASCII character. The tty_clk module in the ./kernel directory
+inserts a timestamp following this character in the input data stream.
+The driver uses this timestamp to determine the time of arrival of the
+PPS pulse to within 26 us at 38.4 kbps while eliminating error due to
+operating system queues and service times. In order to use the tty_clk
+module, the xntp3 distribution must be compiled with CLK defined.
+The modem control leads interface requires converting to RS232 levels
+and connecting to the carrier detect (CD) lead of a serial port. The
+ppsclock streams module in the ./ppsclock directory is used to capture a
+timestamp upon transition of the PPS signal. The driver reads the latest
+timestamp with a designated ioctl() system call to determine the time of
+arrival of the PPS pulse to within a few tens of microseconds. In order
+to use the ppsclock module, the xntp3 distribution must be compiled with
+PPS defined.
+
+Both of these mechanisms are supported by the ATOM_PPS reference clock
+driver described in Appendix A. This driver is ordinarily used in
+conjunction with another clock driver that supports the radio clock that
+produces the PPS pulse. This driver furnishes the coarse timecode used
+to disambiguate the seconds numbering of the PPS pulse itself. The NTP
+daemon mitigates between the radio clock driver and ATOM_PPS driver as
+described in Appendix B in order to provide the most accurate time,
+while respecting the various types of equipment failures that could
+happen.
+
+For the utmost time quality, a number of Unix system kernel
+modifications can be made as described in the README.magic and
+README.kernel files. Specifically, the ppsclock module can be used to
+interface the PPS signal directly to the kernel for use as discipline
+sources for both time and frequency. These sources can be separately
+enabled and monitored using the ntp_adjtime() system call described in
+README.kernel and the ./include/sys/timex.h header file in the xntp3
+distribution. In order to use the kernel PPS signal, the xntp3
+distribution must be compiled with KERNEL_PLL defined.
+
+In some configurations may have multiple radio clocks, each with PPS
+outputs, as well as a kernel modified to use the PPS signal. In order to
+provide the highest degree of redundancy and survivability, the kernel
+PPS discipline, tty_clk module and ppsclock module may all be in use at
+the same time, each backing up the other. The sometimes complicated
+mitigation rules are described in Appendix B.
+
+Debugging Hints
+
+The ntpq and xntpdc utility programs can be used to debug reference
+clocks, either on the server itself or from another machine elsewhere in
+the network. The server is compiled, installed and started using the
+command-line switches described in the xntpd.8 man page. The first thing
+to look for are error messages on the system log. If none occur, the
+daemon has started, opened the devices specified and waiting for peers
+and radios to come up.
+
+The next step is to be sure the RS232 messages, if used, are getting to
+and from the clock. The most reliable way to do this is with an RS232
+tester and to look for data flashes as the driver polls the clock and/or
+as data arrive from the clock. Our experience is that the overwhelming
+fraction of problems occurring during installation are due to problems
+such as miswired connectors or improperly configured radio clocks at
+this stage.
+
+If RS232 messages are getting to and from the clock, the variables of
+interest can be inspected using the ntpq program and various commands
+described in the ntpq.8 man page. First, use the pe and as commands to
+display a pair of billboards showing the peer configuration and
+association IDs for all peers, including the radio clock peers. The
+assigned clock address should appear in the pe billboard and the
+association ID for it at the same relative line position in the as
+billboard. If things are operating correctly, after a minute or two
+samples should show up in the pe display line for the clock.
+
+Additional information is available with the rv and clockvar commands,
+which take as argument the association ID shown in the as billboard. The
+rv command with no argument shows the system variables, while the rv
+command with argument shows the peer variables for the clock, as well as
+any other peers of interest. The clockvar command with argument shows
+the peer variables specific to reference clock peers, including the
+clock status, device name, last received timecode (if relevant), and
+various event counters. In addition, a subset of the fudge parameters is
+included.
+
+The xntpdc utility program can be used for detailed inspection of the
+clock driver status. The most useful are the clockstat and clkbug
+commands described in the xntpdc.8 man page. While these commands permit
+getting quite personal with the particular driver involved, their use is
+seldom necessary, unless an implementation bug shows up.
+
+Most drivers write a message to the clockstats file as each timecode or
+surrogate is received from the radio clock. By convention, this is the
+last ASCII timecode (or ASCII gloss of a binary-coded one) received from
+the radio. This file is managed by the filegen facility described in the
+xntpd.8 man page and requires specific commands in the configuration
+file. This forms a highly useful record to discover anomalies during
+regular operation of the clock. The scripts included in the
+./scripts/stats directory can be run from a cron job to collect and
+summarize these data on a daily or weekly basis. The summary files have
+proven invaluable to detect infrequent misbehavior due to clock
+implementation bugs in some radios.
+
+Appendix A. Individual Driver Descriptions
+
+Following are detailed descriptions of the clock drivers, together with
+configuration data useful for special circumstances.
+
+Type 1: Undisciplined Local Clock
+
+ This is a hack to allow a machine to use its own system clock as a
+ reference clock, i.e., to free-run using no outside clock
+ discipline source. This is useful if you want to use NTP in an
+ isolated environment with no radio clock or NIST modem available.
+ Pick a machine that you figure has a good clock oscillator and
+ configure it with this driver. Set the clock using the best means
+ available, like eyeball-and-wristwatch. Then, point all the other
+ machines at this one or use broadcast (not multicast) mode to
+ distribute time.
+
+ Another application for this driver is if you want to use a
+ particular server's clock as the clock of last resort when all
+ other normal synchronization sources have gone away. This is
+ especially useful if that server has an ovenized oscillator. For
+ this you would configure this driver at a higher stratum (say 3 or
+ 4) to prevent the server's stratum from falling below that.
+
+ A third application for this driver is when an external discipline
+ source is available, such as the NIST "lockclock" program, which
+ synchronizes the local clock via a telephone modem and the NIST
+ Automated Computer Time Service (ACTS), or the Digital Time
+ Synchronization Service (DTSS), which runs on DCE machines. In this
+ case the stratum should be set at zero, indicating a bona fide
+ stratum-1 source. Exercise some caution with this, since there is
+ no easy way to telegraph via NTP that something might be wrong in
+ the discipline source itself. In the case of DTSS, the local clock
+ can have a rather large jitter, depending on the interval between
+ corrections and the intrinsic frequency error of the clock
+ oscillator. In extreme cases, this can cause clients to exceed the
+ 128-ms slew window and drop off the NTP subnet.
+
+ In the default mode the behavior of the clock selection algorithm
+ is modified when this driver is in use. The algorithm is designed
+ so that this driver will never be selected unless no other
+ discipline source is available. This can be overridden with the
+ prefer keyword of the server configuration command, in which case
+ only this driver will be selected for synchronization and all other
+ discipline sources will be ignored. This behavior is intended for
+ use when an external discipline source controls the system clock.
+
+ Fudge Factors
+
+ The stratum for this driver LCLSTRATUM is set at 3 by default, but
+ can be changed by the fudge command and/or the xntpdc utility. The
+ reference ID is "LCL" by default, but can be changed using the same
+ mechanisms. *NEVER* configure this driver to operate at a stratum
+ which might possibly disrupt a client with access to a bona fide
+ primary server, unless the local clock oscillator is reliably
+ disciplined by another source. *NEVER NEVER* configure a server
+ which might devolve to an undisciplined local clock to use
+ multicast mode.
+
+ This driver provides a mechanism to trim the local clock in both
+ time and frequency, as well as a way to manipulate the leap bits.
+ The fudge time1 parameter adjusts the time, in seconds, and the
+ fudge time2 parameter adjusts the frequency, in ppm. Both
+ parameters are additive; that is, they add increments in time or
+ frequency to the present values. (Note: The frequency cannot be
+ changed when the kernel modifications are in use - see the
+ README.kern file). The fudge flag1 and fudge flag2 bits set the
+ corresponding leap bits; for example, setting flag1 causes a leap
+ second to be added at the end of the UTC day. These bits are not
+ reset automatically when the leap takes place; they must be turned
+ off manually after the leap event.
+
+Type 2: TRAK 8820 GPS Receiver
+
+ This driver supports the TRAK 8820 GPS Station Clock. The claimed
+ accuracy at the 1-pps output is 200-300 ns relative to the
+ broadcast signal; however, in most cases the actual accuracy is
+ limited by the precision of the timecode and the latencies of the
+ serial interface and operating system.
+
+ For best accuracy, this radio requires the LDISC_ACTS line
+ discipline, which captures a timestamp at the '*' on-time character
+ of the timecode. Using this discipline the jitter is in the order
+ of 1 ms and systematic error about 0.5 ms. If unavailable, the
+ buffer timestamp is used, which is captured at the \r ending the
+ timecode message. This introduces a systematic error of 23
+ character times, or about 24 ms at 9600 bps, together with a jitter
+ well over 8 ms on Sun IPC-class machines.
+
+ Using the menus, the radio should be set for 9600 bps, one stop bit
+ and no parity. It should be set to operate in computer (no echo)
+ mode. The timecode format includes neither the year nor leap-second
+ warning. No provisions are included in this preliminary version of
+ the driver to read and record detailed internal radio status.
+
+ In operation, this driver sends a RQTS\r request to the radio at
+ initialization in order to put it in continuous time output mode.
+ The radio then sends the following message once each second:
+
+ *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+
+ on-time = '*'
+ ddd = day of year
+ hh:mm:ss = hours, minutes, seconds
+ q = quality indicator (phase error), 0-6:
+ 0 > 20 us
+ 6 > 10 us
+ 5 > 1 us
+ 4 > 100 ns
+ 3 > 10 ns
+ 2 < 10 ns
+
+ The alarm condition is indicated by '0' at Q, which means the radio
+ has a phase error than 20 usec relative to the broadcast time. The
+ absence of year, DST and leap-second warning in this format is also
+ alarming.
+
+ The continuous time mode is disabled using the RQTX<cr> request,
+ following which the radio sends a RQTX DONE<cr><lf> response. In
+ the normal mode, other control and status requests are effective,
+ including the leap-second status request RQLS<cr>. The radio
+ responds with RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year,
+ month and day. Presumably, this gives the epoch of the next leap
+ second, RQLS 00,00,00 if none is specified in the GPS message.
+ Specified in this form, the information is generally useless and is
+ ignored by the driver.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 3: PSTI/Traconex WWV/WWVH Receiver
+
+ This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ Receivers. No specific claim of accuracy is made for these
+ receiver, but actual experience suggests that 10 ms would be a
+ conservative assumption.
+
+ The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ of-year format and UTC time zone. Automatic correction for DST
+ should be disabled. It is very important that the year be set
+ correctly in the DIPswitches; otherwise, the day of year will be
+ incorrect after 28 April of a normal or leap year. The propagation
+ delay DIPswitches should be set according to the distance from the
+ transmitter for both WWV and WWVH, as described in the
+ instructions. While the delay can be set only to within 11 ms, the
+ fudge time1 parameter can be used for vernier corrections.
+
+ Using the poll sequence QTQDQM, the response timecode is in three
+ sections totalling 50 ASCII printing characters, as concatenated by
+ the driver, in the following format:
+
+ ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+
+ on-time = first <cr>
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ a = AM/PM indicator (' ' for 24-hour mode)
+ yy = year (from DIPswitches)
+ dd/mm/ddd = day of month, month, day of year
+ s = daylight-saving indicator (' ' for 24-hour mode)
+ f = frequency enable (O = all frequencies enabled)
+ r = baud rate (3 = 1200, 6 = 9600)
+ d = features indicator (@ = month/day display enabled)
+ z = time zone (0 = UTC)
+ y = year (5 = 91)
+ cc = WWV propagation delay (52 = 22 ms)
+ hh = WWVH propagation delay (81 = 33 ms)
+ SS = status (80 or 82 = operating correctly)
+ F = current receive frequency (4 = 15 MHz)
+ T = transmitter (C = WWV, H = WWVH)
+ tttt = time since last update (0000 = minutes)
+ uu = flush character (03 = ^c)
+ xx = 94 (unknown)
+
+ The alarm condition is indicated by other than '8' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for an extended period; unlock condition is indicated by other
+ than "0000" in the tttt subfield at Q.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 4: Spectracom WWVB Receiver
+
+ This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ Synchronized Clock. This clock has proven a reliable source of
+ time, except in some cases of high ambient conductive RF
+ interference. The claimed accuracy of the clock is 100 usec
+ relative to the broadcast signal; however, in most cases the actual
+ accuracy is limited by the precision of the timecode and the
+ latencies of the serial interface and operating system.
+
+ The DIPswitches on this clock should be set to 24-hour display,
+ AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
+ baud rate 9600. If this clock is to used as the source for the IRIG
+ Audio Decoder (refclock_irig.c in this distribution), set the
+ DIPswitches for AM IRIG output and IRIG format 1 (IRIG B with
+ signature control).
+
+ There are two timecode formats used by these clocks. Format 0,
+ which is available with both the Netclock/2 and 8170, and format 2,
+ which is available only with the Netclock/2 and specially modified
+ 8170.
+
+ Format 0 (22 ASCII printing characters):
+
+ <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+
+ on-time = first <cr>
+ hh:mm:ss = hours, minutes, seconds
+ i = synchronization flag (' ' = in synch, '?' = out synch)
+
+ The alarm condition is indicated by other than ' ' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for about ten hours.
+
+ Format 2 (24 ASCII printing characters):
+
+ <cr><lf>iqyy ddd hh:mm:ss.fff ld
+
+ on-time = <cr>
+ i = synchronization flag (' ' = in synch, '?' = out synch)
+ q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ yy = year (as broadcast)
+ ddd = day of year
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+
+ The alarm condition is indicated by other than ' ' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for about ten hours. The unlock condition is indicated by
+ other than ' ' at Q.
+
+ The Q is normally ' ' when the time error is less than 1 ms and a
+ character in the set 'A'...'D' when the time error is less than 10,
+ 100, 500 and greater than 500 ms respectively. The L is normally '
+ ', but is set to 'L' early in the month of an upcoming UTC leap
+ second and reset to ' ' on the first day of the following month.
+ The D is set to 'S' for standard time 'I' on the day preceding a
+ switch to daylight time, 'D' for daylight time and 'O' on the day
+ preceding a switch to standard time. The start bit of the first
+ <cr> is synchronized to the indicated time as returned.
+
+ This driver does not need to be told which format is in use - it
+ figures out which one from the length of the message. A three-stage
+ median filter is used to reduce jitter and provide a dispersion
+ measure. The driver makes no attempt to correct for the intrinsic
+ jitter of the radio itself, which is a known problem with the older
+ radios.
+
+ Fudge Factors
+
+ This driver can retrieve a table of quality data maintained
+ internally by the Netclock/2 receiver. If flag4 of the fudge
+ configuration command is set to 1, the driver will retrieve this
+ table and write it to the clockstats file on when the first
+ timecode message of a new day is received.
+
+Type 5: TrueTime GPS/GOES Receivers
+
+ This driver supports at least two models of Kinemetrics/TrueTime
+ Timing Receivers, the 468-DC MK III GOES Synchronized Clock and
+ GPS-DC MK III GPS Synchronized Clock and very likely others in the
+ same model family that use the same timecode formats. The clocks
+ are connected via a serial port. Up to four units, with unit
+ numbers in the range 0 through 3, can be configured. The driver
+ assumes the serial port device name is /dev/goes%d (i.e., unit 1 at
+ 127.127.5.1 opens the clock at /dev/goes1) and that the clock is
+ configured for 9600-baud operation.
+
+Type 6: IRIG Audio Decoder
+
+ This driver supports the Inter-Range Instrumentation Group standard
+ time-distribution signal IRIG-B using the audio codec native to the
+ Sun SPARCstation. This signal is generated by several radio clocks,
+ including those made by Austron, TrueTime, Odetics and Spectracom,
+ among others, although it is generally an add-on option. The signal
+ is connected via an attenuator box and cable to the audio codec
+ input on a Sun SPARCstation and requires a specially modified
+ kernel audio driver. Details are in the README.irig file.
+
+ Timing jitter using the decoder and a Sun IPC is in the order of a
+ few microseconds, although the overall timing accuracy is limited
+ by the wander of the CPU oscillator used for timing purposes to a
+ few hundred microseconds. These figures are comparable with what
+ can be achieved using the 1-pps discipline as describe elsewhere in
+ this note.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic. The
+ flag3 and flag4 flags are not applicable to this driver.
+
+Type 7: Scratchbuilt CHU Receiver
+
+ This driver supports a shortwave receiver and special modem
+ circuitry described in the ./gadget directory of the xntp3
+ distribution. It requires the chu-clk line discipline or chu_clk
+ STREAMS module described in the ./kernel directory of that
+ distribution. It is connected via a serial port operating at 300
+ baud.
+ Unlike the NIST time services, whose timecode requires quite
+ specialized hardware to interpret, the CHU timecode can be received
+ directly via a serial port after demodulation. While there are
+ currently no known commercial CHU receivers, the hardware required
+ to receive the CHU timecode is fairly simple to build. While it is
+ possible to configure several CHU units simultaneously, this is in
+ general not useful.
+
+ Fudge Factors
+
+ The time1 option can be used to set the CHU propagation delay,
+ compensate for inherent latencies in the serial port hardware and
+ operating system. The default value is 0.0025 seconds, which is
+ about right for Toronto. Values for other locations can be
+ calculated using the propdelay program in the util directory of the
+ xntp3 distribution or equivalent means.
+
+ The time2 option can be used to compensate for inherent latencies
+ in the serial port hardware and operating system. The value, which
+ defaults to zero, is in addition to the propagation delay provided
+ by the time1 option. The default value is 0.0002 seconds, which is
+ about right for typical telephone modem chips.
+
+ The flag1 option can be used to modify the averaging algorithm used
+ to smooth the clock indications. Ordinarily, the algorithm picks
+ the median of a set of samples, which is appropriate under
+ conditions of poor to fair radio propagation conditions. If the
+ clock is located relatively close to the WWV or WWVH transmitters,
+ setting this flag will cause the algorithm to average the set of
+ samples, which can reduce the residual jitter and improve accuracy.
+
+Type 8: Generic Reference Clock Driver
+
+ The timecode of these receivers is sampled via a STREAMS module in
+ the kernel (The STREAMS module has been designed for use with SUN
+ Systems under SunOS 4.1.x. It can be linked directly into the
+ kernel or loaded via the loadable driver mechanism). This STREAMS
+ module can be adapted to be able to convert different time code
+ formats. If the daemon is
+
+ compiled without the STREAM definition synchronization will work
+ without the Sun streams module, though accuracy is significantly
+ degraded.
+
+ The actual receiver status is mapped into various synchronization
+ states generally used by receivers. The STREAMS module is
+ configured to interpret the time codes of DCF U/A 31, PZF535,
+ GPS166, Trimble SV6 GPS, ELV DCF7000, Schmid and low cost receivers
+ (see list below).
+
+ The reference clock support in xntp contains the necessary
+ configuration tables for those receivers. In addition to supporting
+ up to 32 different clock types and 4 devices, the generation a a
+ PPS signal is also provided as an configuration option. The PPS
+ configuration option uses the receiver generated time stamps for
+ feeding the PPS loopfilter control for much finer clock
+ synchronization.
+
+ CAUTION: The PPS configuration option is different from the
+ hardware PPS signal, which is also supported (see below), as it
+ controls the way xntpd is synchronized to the reference clock,
+ while the hardware PPS signal controls the way time offsets are
+ determined.
+
+ The use of the PPS option requires receivers with an accuracy of
+ better than 1ms.
+ Fudge factors
+
+ Only two fudge factors are utilized. The time1 fudge factor defines
+ the phase offset of the synchronization character to the actual
+ time. On the availability of PPS information the time2 fudge factor
+ defines the skew between the PPS time stamp and the receiver
+ timestamp of the PPS signal. This parameter is usually zero, as
+ usually the PPS signal is believed in time and OS delays should be
+ corrected in the machine specific section of the kernel driver.
+ time2 needs only be set when the actual PPS signal is delayed for
+ some reason. The flag0 enables input filtering. This a median
+ filter with continuous sampling. The flag1 selects averaging of the
+ samples remaining after the filtering. Leap secondhandling is
+ controlled with the flag2. When set a leap second will be deleted
+ on receipt of a leap second indication from the receiver. Otherwise
+ the leap second will be added, (which is the default).
+
+ ntpq (8)
+
+ timecode variable
+
+ The ntpq program can read clock variables command list several
+ variables. These hold the following information: refclock_time is
+ the local time with the offset to UTC (format HHMM). The currently
+ active receiver flags are listed in refclock_status. Additional
+ feature flags of the receiver are optionally listed in parentheses.
+ The actual time code is listed in timecode. A qualification of the
+ decoded time code format is following in refclock_format. The last
+ piece of information is the overall running time and the
+ accumulated times for the clock event states in refclock_states.
+ When PPS information is present additional variable are available.
+ refclock_ppstime lists then the PPS timestamp and refclock_ppsskew
+ lists the difference between RS232 derived timestamp and the PPS
+ timestamp.
+
+ Unit encoding
+
+ The unit field <u> encodes the device, clock type and the PPS
+ generation option. There are 4 possible units, which are encoded in
+ the lower two bits of the <u> field. The devices are named
+ /dev/refclock-0 through /dev/refclock-3. Bits 2 thru 6 encode the
+ clock type. The fudge factors of the clock type are taken from a
+ table clockinfo in refclock_parse.c. The generation of PPS
+ information for disciplining the local NTP clock is encoded in bit
+ 7 of <u>.
+
+ Currently, nine clock types (devices /dev/refclock-0 -
+ /dev/refclock-3) are supported.
+
+ 127.127.8.0-3 16
+
+ Meinberg PZF535 receiver (FM demodulation/TCXO / 50us)
+
+ 127.127.8.4-7 16
+
+ Meinberg PZF535 receiver (FM demodulation/OCXO / 50us)
+
+ 127.127.8.8-11 16
+
+ Meinberg DCF U/A 31 receiver (AM demodulation / 4ms)
+
+ 127.127.8.12-15 16
+
+ ELV DCF7000 (sloppy AM demodulation / 50ms)
+
+ 127.127.8.16-19 16
+
+ Walter Schmid DCF receiver Kit (AM demodulation / 1ms)
+
+ 127.127.8.20-23 16
+
+ RAW DCF77 100/200ms pulses (Conrad DCF77 receiver module / 5ms)
+
+ 127.127.8.24-27 16
+
+ RAW DCF77 100/200ms pulses (TimeBrick DCF77 receiver module / 5ms)
+
+ 127.127.8.28-31 16
+
+ Meinberg GPS166 receiver (GPS / <<1us)
+
+ 127.127.8.32-35 16
+
+ Trimble SV6 GPS receiver (GPS / <<1us)
+
+ 127.127.8.40-43 16
+
+ RAW DCF77 100/200ms pulses (Boeder DCF77 receiver / 5ms)
+
+ The reference clock support carefully monitors the state
+ transitions of the receiver. All state changes and exceptional
+ events such as loss of time code transmission are logged via the
+ syslog facility. Every hour a summary of the accumulated times for
+ the clock states is listed via syslog.
+
+ PPS support is only available when the receiver is completely
+ synchronized. The receiver is believed to deliver correct time for
+ an additional period of time after losing synchronizations, unless
+ a disruption in time code transmission is detected (possible power
+ loss). The trust period is dependent on the receiver oscillator and
+ thus a function of clock type. This is one of the parameters in the
+ clockinfo field of the reference clock implementation. This
+ parameter cannot be configured by xntpdc.
+
+ In addition to the PPS loopfilter control a true PPS hardware
+ signal can be applied on Sun Sparc stations via the CPU serial
+ ports on the CD pin. This signal is automatically detected and will
+ be used for offset calculation. The input signal must be the time
+ mark for the following time code. (The edge sensitivity can be
+ selected - look into the appropriate kernel/parsestreams.c for
+ details). Meinberg receivers can be connected by feeding the PPS
+ pulse of the receiver via a 1488 level converter to Pin 8 (CD) of a
+ Sun serial zs\-port.
+
+ There exists a special firmware release for the PZF535 Meinberg
+ receivers. This release (PZFUERL 4.6 (or higher - The UERL is
+ important)) is absolutely recommended for XNTP use, as it provides
+ LEAP warning, time code time zone information and alternate antenna
+ indication. Please check with Meinberg for this firmware release.
+ For the Meinberg GPS166 receiver is also a special firmware release
+ available (Uni-Erlangen). This release must be used for proper
+ operation.
+
+ The raw DCF77 pulses can be fed via a level converter directly into
+ Pin 3 (Rx) of the Sun. The telegrams will be decoded an used for
+ synchronization. AM DCF77 receivers are running as low as $25. The
+ accuracy is dependent on the receiver and is somewhere between 2ms
+ (expensive) to 10ms (cheap). Upon bad signal reception of DCF77
+ synchronizations will cease as no backup oscillator is available as
+ usually found in other reference clock receivers. So it is
+ important to have a good place for the DCF77 antenna. For
+ transmitter shutdowns you are out of luck unless you have other NTP
+ servers with alternate time sources available.
+
+Type 9: Magnavox MX4200 GPS Receiver
+
+ This driver supports the Magnavox MX4200 Navigation Receiver
+ adapted to precision timing applications. This requires an
+ interface box described in the ./ppsclock directory of the xntp3
+ distribution. It is connected via a serial port and requires the
+ ppsclock STREAMS module described in the same directory.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 10: Austron 2201A GPS Receiver
+
+ This driver supports the Austron 2200A/2201A GPS/LORAN Synchronized
+ Clock and Timing Receiver connected via a serial port. It supports
+ several special features of the clock, including the Input Buffer
+ Module, Output Buffer Module, IRIG-B Interface Module and LORAN
+ Assist Module. It requires the RS232 Serial Interface module for
+ communication with the driver.
+
+ This receiver is capable of a comprehensive and large volume of
+ statistics and operational data. The specific data collection
+ commands and attributes are embedded in the driver source code;
+ however, the collection process can be enabled or disabled using
+ the flag4 flag. If set, collection is enabled; if not, which is the
+ default, it is disabled. A comprehensive suite of data reduction
+ and summary scripts is in the ./scripts/stats directory of the
+ xntp3 distribution.
+
+ To achieve the high accuracy this device provides, it is necessary
+ to use the ppsclock feature of the xntp3 program distribution or,
+ alternatively, to install the kernel modifications described in the
+ README.kern. The clock can be wired to provide time to a single CPU
+ or bussed in parallel to several CPUs, with one CPU controlling the
+ receiver and the others just listening. Fair accuracy can be
+ achieved in the single-CPU configuration without use of the 1-pps
+ signal, but in multiple CPU configurations accuracy is severely
+ degraded without it.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 11: TrueTime OM-DC OMEGA Receiver
+
+ This driver supports the Kinemetrics/TrueTime OMEGA-DC OMEGA
+ Synchronized Clock connected via a serial port. This clock is
+ sufficiently different from other Kinemetrics/TrueTime models that
+ a separate driver is required. Up to four units, with unit numbers
+ in the range 0 through 3, can be configured. The driver assumes the
+ serial port device name is /dev/omega%d (i.e., unit 1 at
+ 127.127.11.1 opens the clock at /dev/omega1) and that the clock is
+ configured for 9600-baud operation.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 12: KSI/Odetics TPRO/S IRIG Interface
+
+ This driver supports the KSI/Odetics TPRO and TPRO-SAT IRIG-B
+ Decoder, which is a module connected directly to the SBus of a Sun
+ workstation. The module works with the IRIG-B signal generated by
+ several radio clocks, including those made by Austron, TrueTime,
+ Odetics and Spectracom, among others, although it is generally an
+ add-on option. In the case of the TPRO-SAT, the module is an
+ integral part of a GPS receiver, which serves as the primary timing
+ source.
+
+ Using the TPRO interface as a NTP reference clock provides
+ precision time only to xntpd and its clients. With suitable kernel
+ modifications, it is possible to use the TPRO as the CPU system
+ clock, avoiding errors introduced by the CPU clock oscillator
+ wander. See the README.kernel and kern.c files for further details.
+
+Type 13: Leitch CSD 5300 Master Clock Controller
+
+ Information not available.
+
+Type 14: EES M201 MSF Receiver
+
+ This driver supports the EES M201 MSF receiver connected to a Sun
+ SPARCstation running SunOS 4.x with the "ppsclock" STREAMS module.
+
+ Fudge Factors
+
+ If flag1 is set, then the system clock is assumed to be sloppy
+ (e.g. Sun4 with 20ms clock), so samples are averaged. If flag2 is
+ set, then leaphold is set. If flag3 is set, then the sample
+ information is dumped. If flag4 is set, then the input data is
+ smoothed, and all data points are used.
+
+Type 15: TrueTime GPS/TM-TMD Receiver
+
+ Information not available.
+
+Type 16: Bancomm GPS/IRIG Receiver
+
+ Information not available.
+
+Type 17: Datum Precision Time System
+
+ Information not available.
+
+Type 18: NIST Automated Computer Time Service
+
+ This driver supports the NIST Automated Computer Time Service
+ (ACTS). It periodically dials a prespecified telephone number,
+ receives the NIST timecode data and calculates the local clock
+ correction. It designed primarily for use when neither a radio
+ clock nor connectivity to Internet time servers is available. For
+ the best accuracy, the individual telephone line/modem delay needs
+ to be calibrated using outside sources.
+
+ The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ toll call from Newark, DE, costs between three and four cents,
+ although it is not clear what carrier and time of day discounts
+ apply. The modem dial string will differ depending on local
+ telephone configuration, etc., and is specified by the phone
+ command in the configuration file. The argument to this command is
+ an AT command for a Hayes compatible modem.
+
+ The accuracy produced by this driver should be in the range of a
+ millisecond or two, but may need correction due to the delay
+ characteristics of the individual modem involved. For undetermined
+ reasons, some modems work with the ACTS echo-delay measurement
+ scheme and some don't. This driver tries to do the best it can with
+ what it gets. Initial experiments with a Practical Peripherals
+ 9600SA modem here in Delaware suggest an accuracy of a millisecond
+ or two can be achieved without the scheme by using a fudge time1
+ value of 65.0 ms. In either case, the dispersion for a single call
+ involving ten samples is about 1.3 ms.
+
+ The driver can operate in either of three modes, as determined by
+ the mode parameter in the server configuration command. In mode 0
+ (automatic) the driver operates continuously at intervals depending
+ on the prediction error, as measured by the driver, usually in the
+ order of several hours. In mode 1 (backup) the driver is enabled in
+ automatic mode only when no other source of synchronization is
+ available and when more than MAXOUTAGE (3600 s) have elapsed since
+ last synchronized by other sources. In mode 2 (manual) the driver
+ operates only when enabled using a fudge flags switch, as described
+ below.
+
+ For reliable call management, this driver requires a 1200-bps modem
+ with a Hayes-compatible command set and control over the modem data
+ terminal ready (DTR) control line. Present restrictions require the
+ use of a POSIX-compatible programming interface, although other
+ interfaces may work as well. The ACTS telephone number and modem
+ setup string are hard-coded in the driver and may require changes
+ for nonstandard modems or special circumstances.
+
+ Fudge Factors
+
+ Ordinarily, the propagation time correction is computed
+ automatically by ACTS and the driver. When this is not possible or
+ erratic due to individual modem characteristics, the fudge flag2
+ switch should be set to disable the ACTS echo-delay scheme. In any
+ case, the fudge time1 parameter can be used to adjust the
+ propagation delay as required.
+
+ The ACTS call interval is determined in one of three ways. In
+ manual mode a call is initiated by setting fudge flag1 using
+ xntpdc, either manually or via a cron job. In automatic mode this
+ flag is set by the peer timer, which is controlled by the sys_poll
+ variable in response to measured errors. In backup mode the driver
+ is ordinarily asleep, but awakes (in automatic mode) if all other
+ synchronization sources are lost. In either automatic or backup
+ modes, the call interval increases as long as the measured errors
+ do not exceed the value of the fudge time2 parameter.
+
+ When the fudge flag1 is set, the ACTS calling program is activated.
+ This program dials each number listed in the phones command of the
+ configuration file in turn. If a call attempt fails, the next
+ number in the list is dialed. The fudge flag1 and counter are reset
+ and the calling program terminated if (a) a valid clock update has
+ been determined, (b) no more numbers remain in the list, (c) a
+ device fault or timeout occurs, or (d) fudge flag1 is reset
+ manually using xntpdc.
+
+ The NIST timecode message is transmitted at 1200 bps in the
+ following format (see the driver source for more information):
+
+ jjjjj yy-mm-dd hh:mm:ss tt l uuu mmmmm UTC(NIST) *
+
+ jjjjj = modified Julian day
+ yy-mm-dd = year, month, day
+ hh:mm:ss = hours, minutes, seconds
+ tt = DST indicator (see driver listing)
+ l = leap-second warning (see driver listing)
+ uuu = DUT1 correction (see driver listing)
+ mmmmm = modem calibration (see driver listing)
+ on-time = '*'
+
+ The timecode message is transmitted continuously after a signon
+ banner, which this driver ignores. The driver also ignores all but
+ the yy-mm-dd, hh:mm:ss and on-time character '*' fields, although
+ it checks the format of all fields of the message. A timestamp is
+ captured at the '*' character, as required by the ACTS
+ specification, and used as the reference time of the timecode. If a
+ message with an on-time character of '#' is received, the driver
+ updates the propagation delay. The driver disconnects when (a) ten
+ valid messages have been received, (b) no message has been received
+ for 15 s, (c) an on-time character of '#' is received. These
+ messages are processed by a trimmed-mean filter to reduce timing
+ noise and then by the usual NTP algorithms to develop the clock
+ correction.
+
+ The behavior of the clock selection algorithm is modified when this
+ driver is in use. The algorithm is designed so that this driver
+ will never be selected unless no other discipline source is
+ available. This can be overridden with the prefer keyword of the
+ server configuration command, in which case only this driver will
+ be selected for synchronization and all other discipline sources
+ will be ignored. Ordinarily, the prefer keyword would be used only
+ in automatic mode ehen primary time is to be obtained via ACTS and
+ backup NTP peers used only when ACTS fails.
+
+ Call Management
+
+ Since ACTS will be a toll call in most areas of the country, it is
+ necessary to carefully manage the calling interval. The ACTS call
+ program is initiated by setting fudge flag1. This flag can be set
+ manually using xntpdc, by a cron job that calls xntpdc, or
+ automatically by the driver itself. The fudge flag1 is reset when
+ the program terminates after a time determination is comlete or
+ when no more numbers remain in the alternate path list, a device
+ fault or timeout has occured, or the fudge flag1 has been reset
+ using xntpdc.
+
+ In automatic and backup modes, the driver determines the call
+ interval using a procedure depending on the measured prediction
+ error and the fudge time2 parameter. If the error exceeds time2 for
+ a number of times depending on the current interval, the interval
+ is decreased, but not less than about 1000 s. If the error is less
+ than time2 for some number of times, the interval is increased, but
+ not more than about 18 h. With the default value of zero for fudge
+ time2, the interval will increase from 1000 s to the 4000-8000-s
+ range, in which the expected accuracy should be in the 1-2-ms
+ range. Setting fudge time2 to a large value, like 0.1 s, may result
+ in errors of that order, but increase the call interval to the
+ maximum. The exact value for each configuration will depend on the
+ modem and operating system involved, so some experimentation may be
+ necessary.
+
+ Manual call attempts can be made at any time by setting fudge flag1
+ using xntpdc. For example, the xntpdc command
+
+ fudge 127.127.18.1 flags 1
+
+ will ask for a key identifier and password and, if authenticated by
+ the server, will set flag1. There may be a short delay until the
+ expiration of the current poll timeout.
+
+ The flag1 can be set from a cron job in the following way.
+ Construct a file with contents
+
+ keyid 11
+ passwd dialup
+ fudge 127.127.18.1 flags 1
+ quit
+
+ Then, run the following program at specified times as required.
+
+ /usr/local/bin/xntpdc <file
+
+Type 19: Heath WWV/WWVH Receiver
+
+ This driver supports the Heath GC-1000 Most Accurate Clock, with
+ RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ robust than other supported receivers. Its claimed accuracy is 100
+ ms when actually synchronized to the broadcast signal, but this
+ doesn't happen even most of the time, due to propagation
+ conditions, ambient noise sources, etc. When not synchronized, the
+ accuracy is at the whim of the internal clock oscillator, which can
+ wander into the sunset without warning. Since the indicated
+ precision is 100 ms, expect a host synchronized only to this thing
+ to wander to and fro, occasionally being rudely stepped when the
+ offset exceeds the default CLOCK_MAX of 128 ms.
+
+ The internal DIPswitches should be set to operate at 1200 baud in
+ MANUAL mode and the current year. The external DIPswitches should
+ be set to GMT and 24-hour format. It is very important that the
+ year be set correctly in the DIPswitches. Otherwise, the day of
+ year will be incorrect after 28 April of a normal or leap year.
+
+ In MANUAL mode the clock responds to a rising edge of the request
+ to send (RTS) modem control line by sending the timecode.
+ Therefore, it is necessary that the operating system implement the
+ TIOCMBIC and TIOCMBIS ioctl system calls and TIOCM_RTS control bit.
+ Present restrictions require the use of a POSIX-compatible
+ programming interface, although other interfaces may work as well.
+
+ The clock message consists of 23 ASCII printing characters in the
+ following format:
+
+ hh:mm:ss.f dd/mm/yr<cr>
+
+ hh:mm:ss.f = hours, minutes, seconds
+ f = deciseconds ('?' when out of spec)
+ dd/mm/yr = day, month, year
+
+ The alarm condition is indicated by '?', rather than a digit, at A.
+ Note that 0?:??:??.? is displayed before synchronization is first
+ established and hh:mm:ss.? once synchronization is established and
+ then lost again for about a day.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic. A fudge
+ time1 value of .07 s appears to center the clock offset residuals.
+
+Type 20: Generic NMEA GPS Receiver
+
+ Information not available.
+
+Type 21: Motorola Six Gun GPS Receiver
+
+ Information not available.
+
+Type 22: PPS Clock Discipline
+
+ This driver furnishes an interface for pulse-per-second (PPS)
+ signals produced by a cesium clock, timing receiver or related
+ equipment. It can be used to remove accumulated jitter and retime a
+ secondary server when synchronized to a primary server over a
+ congested, wide-area network and before redistributing the time to
+ local clients. Note that this driver does not control the system
+ clock if the kernel modifications described in the README.kernel
+ file have been installed, but it can be useful as a monitoring
+ tool.
+
+ In order for this driver to work, the local clock must be set to
+ within +-500 ms by another means, such as a radio clock or NTP
+ itself. The 1-pps signal is connected via a serial port and gadget
+ box consisting of a one-shot and RS232 level converter. When
+ operated at 38.4 kbps with a SPARCstation IPC, this arrangement has
+ a worst-case jitter less than 26 us.
+
+ There are three ways in which this driver can be used. The first
+ way uses the LDISC_PPS line discipline and works only for the
+ baseboard serial ports of the Sun SPARCstation. The PPS signal is
+ connected via a gadget box to the carrier detect (CD) line of a
+ serial port and flag3 of the driver configured for that port is
+ set. This causes the ppsclock streams module to be configured for
+ that port and capture a timestamp at the on-time transition of the
+ PPS signal. This driver then reads the timestamp directly by a
+ designated ioctl() system call. This provides the most accurate
+ time and least jitter of any other scheme. There is no need to
+ configure a dedicated device for this purpose, which ordinarily is
+ the device used for the associated radio clock.
+
+ The second way uses the LDISC_CLKPPS line discipline and works for
+ any architecture supporting a serial port. If after a few seconds
+ this driver finds no ppsclock module configured, it attempts to
+ open a serial port device /dev/pps%d, where %d is the unit number,
+ and assign the LDISC_CLKPPS line discipline to it. If the line
+ discipline fails, no harm is done except the accuracy is reduced
+ somewhat. The pulse generator in the gadget box is adjusted to
+ produce a start bit of length 26 us at 38400 bps (or 104 us at 9600
+ bps). Used with the LDISC_CLKPPS line discipline, this produces an
+ ASCII DEL character ('\377') followed by a timestamp at each
+ seconds epoch.
+
+ The third way involves an auxiliary radio clock driver which calls
+ the PPS driver with a timestamp captured by that driver. This use
+ is documented in the source code for the driver(s) involved.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic and those
+ explicitly defined above. The fudge time1 parameter can be used to
+ compensate for miscellaneous UART and OS delays. Allow about 247 us
+ for uart delays at 38400 bps and about 1 ms for SunOS streams
+ nonsense.
+
+Appendix B. Mitigation Rules
+
+In order to provide robust backup sources, stratum-1 peers are usually
+operated in a diversity configuration, in which the local server
+operates with a number of remote peers in addition with one or more
+radio clocks operating also as local peers. In these configurations the
+suite of algorithms used in NTP to refine the data from each peer
+separately and to select and weight the data from a number of peers can
+be used with the entire ensemble of remote peers and local radios.
+However, Because of small but significant systematic time offsets
+between the peers, it is in general not possible to achieve the lowest
+jitter and highest stability in these configurations. In addition, there
+are a number of special configurations involving auxiliary radio clock
+outputs, telephone backup services and other special cases, so that a
+set of mitigation rules becomes necessary.
+
+The mitigation rules are based on a set of special characteristics of
+the various reference clock drivers configured on the server. For
+instance, it is possible to designate a peer as "preferred," in which
+case, all other things being equal, this peer will be selected for
+synchronization over all other eligible candidates in the clock
+selection procedures. The precise characterization of the prefer peer is
+described below. In addition, when a pulse-per-second (PPS) signal is
+connected via the PPS Clock Discipline Driver (type 22), the
+corresponding peer is called the PPS peer. The manner in which this peer
+operates is described below. When the Undisciplined Local Clock Driver
+(type 1) is configured in the server, this becomes the local-clock peer.
+When the Automated Computer Time Service Driver (type 18) is configured
+in the server, this becomes the ACTS peer. Both the local-clock and ACTS
+peers operates in the manner described in Appendix A. Finally, where
+support is available, the PPS signal may be processed directly by the
+kernel. In the following this will be called the kernel discipline.
+
+The mitigation rules apply in the clock selection procedures following
+the sanity checks, intersection algorithm and clustering algorithm. The
+survivors at this point represent the subset of all peers which can
+provide the most accurate, stable time. In the general case, with no
+designated prefer peer, PPS peer or local-clock peer, the mitigation
+rules require all survivors be averaged according to a weight depending
+on the reciprocal of the dispersion, as provided in the NTP
+specification.
+
+The mitigation rules establish the choice of system peer, which
+determine the stratum, reference identifier and several other system
+variables which are visible to clients of the local server. In addition,
+they establish which source or combination of sources control the local
+clock. In detail, these rules operate as follows:
+
+1. If there is a prefer peer and it is the local-clock peer or the
+ ACTS peer; or, if there is a prefer peer and the kernel discipline
+ is active, choose the prefer peer as the system peer.
+
+2. If the above is not the case and there is a PPS peer, then choose
+ it as the system peer and its offset as the system clock offset.
+
+3. If the above is not the case and there is a prefer peer (not the
+ local-clock or ACTS peer in this case), then choose it as the
+ system peer and its offset as the system clock offset.
+
+4. If the above is not the case and the peer previously chosen as the
+ system peer is in the surviving population, then choose it as the
+ system peer and average its offset along with the other survivors
+ to determine the system clock offset. This behavior is designed to
+ avoid excess jitter due to "clockhopping," when switching the
+ system peer would not materially improve the time accuracy.
+
+5. If the above is not the case, then choose the first candidate in
+ the list of survivors ranked in order of synchronization distance
+ and average its offset along with the other survivors to determine
+ the system clock offset. This is the default case and the only case
+ considered in the current NTP specification.
+
+The specific interpretation of the prefer peer and PPS peer require some
+explanation, which is given in following sections.
+
+B.1. Using the prefer Keyword
+
+For the reasons stated previously, a scheme has been implemented in NTP
+to provide an intelligent mitigation between various classes of peers,
+one designed to provide the best quality time without compromising the
+normal operation of the NTP algorithms. This scheme in its present form
+is not an integral component of the NTP specification. but is likely to
+be included in future versions of the specification. The scheme is based
+on the "preferred peer," which is specified by including the prefer
+keyword with the associated server or peer command in the configuration
+file. This keyword can be used with any peer or server, but is most
+commonly used with a radio clock server.
+
+The prefer scheme works on the set of peers that have survived the
+sanity and intersection algorithms of the clock select procedures.
+Ordinarily, the members of this set can be considered truechimers and
+any one of them could in principle provide correct time; however, due to
+various error contributions, not all can provide the most stable time.
+The job of the clustering algorithm, which is invoked at this point, is
+to select the best subset of the survivors providing the least variance
+in the combined ensemble compared to the variance in each member of the
+subset. The detailed operation of the clustering algorithm, which are
+given in the specification, are not important here, other than to point
+out it operates in rounds, where a survivor, presumably the worst of the
+lot, is discarded in each round until one of several termination
+conditions is met.
+
+In the prefer scheme the clustering algorithm is modified so that the
+prefer peer is never discarded; on the contrary, its potential removal
+becomes a termination condition. If the original algorithm were about to
+toss out the prefer peer, the algorithm terminates right there. The
+prefer peer can still be discarded by the sanity and intersection
+algorithms, of course, but it will always survive the clustering
+algorithm. A preferred peer retains that designation as long as it
+survives the intersection algorithm. If for some reason the prefer peer
+fails to survive the intersection algorithm, either because it was
+declared a falseticker or became unreachable, it loses that designation
+and the clock selection remitigates as described above.
+
+Along with this behavior, the clock select procedures are modified so
+that the combining algorithm is not used when a prefer (or PPS) peer is
+present. Instead, the offset of the prefer (or PPS) peer is used
+exclusively as the synchronization source. In the usual case involving a
+radio clock and a flock of remote stratum-1 peers, and with the radio
+clock designated a prefer peer, the result is that the high quality
+radio time disciplines the server clock as long as the radio itself
+remains operational and with valid time, as determined from the remote
+peers, sanity algorithm and intersection algorithm.
+
+While the model does not forbid it, it does not seem useful to designate
+more than one peer as preferred, since the additional complexities to
+mitigate among them do not seem justified from on the air experience.
+Note that the prefer peer interacts with the PPS peer discussed in
+Appendix C. It also interacts with the Undisciplined Local Clock Driver
+(type 1), as described in Appendix A. See the main text for the
+mitigation rules applying to the general case.
+
+B.2. Using the Pulse-per-Second (PPS) Signal
+
+Most radio clocks are connected using a serial port operating at speeds
+of 9600 bps or lower. The accuracy using typical timecode formats, where
+the on-time epoch is indicated by a designated ASCII character, like
+carriage-return <cr>, is limited to a millisecond at best and a few
+milliseconds in typical cases. However, some radios produce a precision
+pulse-per-second (PPS) signal which can be used to improve the accuracy
+in typical workstation servers to the order of a few tens of
+microseconds. The details of how this can be accomplished are discussed
+in the README.magic file; the following discusses how this signal is
+implemented and configured in a typical working server.
+
+First, it should be pointed out that the PPS signal is inherently
+ambiguous, in that it provides a precise seconds epoch, but does not
+provide a way to number the seconds. In principle and most commonly,
+another source of synchronization, either the timecode from an
+associated radio clock, or even a set of remote peers, is available to
+perform that function. In all cases a specific, configured peer or
+server must be designated as associated with the PPS signal. This is
+done by including the prefer keyword with the associated server or peer
+command in the configuration file. This PPS signal can be associated in
+this way any peer or server, but is most commonly used with the radio
+clock generating the PPS signal.
+
+The PPS signal is processed by a special PPS Clock Discipline Driver
+(type 22) described in Appendix A. That description specifies the
+hardware configurations in which this signal can be connected to the
+server. This driver replaces the former scheme based on conditional
+compilation and the PPS, CLK and PPSCLK compile-time switches.
+Regardless of method, the driver, like all other drivers, is mitigated
+in the manner described for the prefer peer in Appendix B. However, in
+the case of the PPS peer, the behavior is slightly more complex.
+
+First, in order for the PPS peer to be considered at all, its associated
+prefer peer must have survived the sanity and intersection algorithms
+and have been designated the prefer peer. This insures that the radio
+clock hardware is operating correctly and that, presumably, the PPS
+signal is operating correctly as well. Second, the absolute time offset
+from that peer must be less than CLOCK_MAX, the gradual-adjustment
+range, which is ordinarily set at 128 ms, or well within the +-0.5-s
+unambiguous range of the PPS signal itself. Finally, the time offsets
+generated by the PPS peer are propagated via the clock filter to the
+clock selection procedures just like any other peer. Should these pass
+the sanity and intersection algorithms, they will show up along with the
+offsets of the prefer peer itself. Note that, unlike the prefer peer,
+the PPS peer samples are not protected from discard by the clustering
+algorithm. These complicated procedures insure that the PPS offsets
+developed in this way are the most accurate, reliable available for
+synchronization.
+
+A PPS peer retains that designation as long as it survives the
+intersection algorithm; however, like any other clock driver, it runs a
+reachability algortihm on the PPS signal itself. If for some reason the
+signal fails or displays gross errors, the PPS peer will either become
+unreachable or stray out of the survivor population. In this case the
+clock selection remitigates as described above.
+
+Finally, the mitigation procedures described above for the prefer peer
+are modified so that, if the PPS peer survives the clustering algorithm,
+its offset is mitigated over the prefer peer offset; in other words in
+case of ties, the PPS offset wins. See the main text for the mitigation
+rules applying to the general case.
+
+B.3. Using the Kernel Discipline
+
+Code to implement the kernel discipline is a special feature that can be
+incorporated in the kernel of some workstations as described in the
+README.kernel file. The discipline provides for the control of the local
+clock oscillator time and/or frequency by means of an external PPS
+signal interfaced via a modem control lead. As the PPS signal is derived
+from external equipment, cables, etc., which sometimes fail, a good deal
+of error checking is done in the kernel to detect signal failure and
+excessive noise.
+
+In order to operate, the kernel discipline must be enabled and the
+signal must be present and within nominal jitter and wander error
+tolerances. In the NTP daemon the kernel is enabled only when the prefer
+peer is among the survivors of the clustering algorithm, as described
+above. Then, the PPS peer is designated the prefer peer as long as the
+PPS signal is present and operating within tolerances. Under these
+conditions the kernel disregards updates produced by the NTP daemon and
+uses its internal PPS source instead. The kernel maintains a watchdog
+timer for the PPS signal; if the signal has not been heard or is out of
+tolerance for more than some interval, currently two minutes, the kernel
+discipline is declared inoperable and operation continues as if it were
+not present.
+Appendix C. NTP Local Clock Discipline
+
+Implementation of the ACTS driver caused somewhat of a shakeup in the
+NTP local clock model and implementation. The model described in the
+specification RFC-1305 is based on a phase-lock loop (PLL) design, which
+is optimum or near optimum for the update intervals used for NTP peers
+and radio clocks, ordinarily in the range 64-1024 s. However, the ACTS
+driver must operate with update intervals in the range well above 1024
+s, where the performance of the PLL model deteriorates. As suggested by
+Judah Levine of NIST and used in his "lockclock" algorithm, a hybrid
+frequency-lock loop (FLL) gives better performance at the longer update
+intervals up to a maximum depending on the acceptable error bound.
+
+In a series of experiments and simulations, it was verified that the PLL
+model provides better performance in the regime less than about 1000 s,
+while the FLL model provides better performance above that. The
+parameters of each model were optimized by simulation for the lowest
+time and frequency error using data collected on an undisciplined
+computer clock oscillator over a period of about two weeks. The PLL/FLL
+hybrid loop has been implemented in NTP, along with certain other
+refinements described below. While it was designed primarily with ACTS
+in mind, it can be used with any NTP peer or radio clock, should that
+prove useful.
+
+To take advantage of this feature for other than the ACTS driver, where
+it is automatic, note that the default minimum poll interval is 64 s and
+default maximum poll interval 1024 s (for the ACTS driver the default
+minimum is 1024 s and default maximum 16384 s). However, using the
+minpoll and/or maxpoll parameters of the server or peer commands in the
+configuration file, it is possible to set the minimum poll interval as
+low as 16 s and the maximum poll interval as high as 16384 s. Poll
+intervals less than 64 s are useful if an exceptionally quick lock is
+required, like in real-time or portable systems. Poll intervals above
+1024 s, other than ACTS, may be useful to reduce traffic in some
+situations, such as when charges are made on a per-packet basis.
+
+Another modification to the stock NTP local clock discipline is to avoid
+errors due to old data. From a study of the stability characteristics of
+typical computer clock oscillators using both experiment and simulation,
+it was determined that data used to discipline the PLL are not generally
+useful if older than about 1000 s. This corresponds roughly to the knee
+in the Allan variance characteristic measured for a typical workstation
+oscillator. The NTP clock filter algorithm was modified to adjust the
+effective length of the shift register so that samples older than about
+1000 s are not used to determine the filtered offset, delay and
+dispersion values. This design has the useful byproduct that the time to
+acquire lock when first coming up and to declare unreachability is
+independent of the poll interval.
+
+A problem which has recurred on every occasion a leap second has been
+inserted in the UTC timescale is that not all radio clocks detect and
+implement the leap event. As a result, some radios sail right through
+the leap, become confused for periods up to 15 minutes, then reacquire
+lock. In order to cope with this, as well as other occasions where
+atypically large offsets occur, the NTP clock discipline has been
+modified to disregard offsets over 128 ms, unless (a) first coming up,
+(b) first returning to service after a period when unsynchronized, or
+(c) an interval of about 15 minutes has elapsed since the last update
+less than 128 ms was received. In addition, the discipline has been
+modified so that, if the first offset received after coming up is less
+than 128 ms, the local clock is immediately reset to that offset without
+affecting the PLL variables.
+
+It has been the experience of some users that, when first installed in a
+system, the NTP clock discipline fails to reliably lock to other peers
+and servers as configured. The indications are that the daemon locks for
+some period of time, but is unable to stabilize the frequency estimate.
+As the result, the time offsets eventually climb above 128 ms and the
+discipline unlocks again. After the 15-minute timeout, the daemon locks
+again and the cycle repeats. The problem here is that the intrinsic
+frequency error of the local clock exceeds the design capture range of
+the PLL, 100 ppm. This particular limit was selected as a compromise
+between useful maximum error indications and the tolerances found in
+typical computer clock oscillators.
+
+In spite of the tolerance assumed in the NTP specification of 100 ppm,
+the NTP daemon for Unix can operate with an intrinsic frequency error of
+over 380 ppm, depending on the values of tick and tickadj selected by
+the tickadj program. However, with errors that large, the PLL will not
+reliably lock, and the behavior noted above can occur. Formerly, the
+only remedial in cases where this happens waas a somewhat painful manual
+process where the nominal oscillator frequency is measured by some other
+means, such as eyeball-and-wristwatch, and a specific drift file
+(ntp.drift) crafted.
+
+In order to avoid the above problem, the NTP clock discipline has been
+modified to measure the frequency during periods when not locked to
+another server or radio clock. Such periods occur when the time offset
+wanders through and beyond the 128-ms window as described above. When
+synchronization is reestablished, the working frequency used by NTP is
+initialized with the measured value. Since a precise frequency
+determination is not always possible under these chaotic conditions, it
+may take more than one cycle of this type to get the residual error
+below 100 ppm and reliable lock established.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+3 July 1994
diff --git a/usr.sbin/xntpd/doc/UofT b/usr.sbin/xntpd/doc/UofT
new file mode 100644
index 0000000..54420d5
--- /dev/null
+++ b/usr.sbin/xntpd/doc/UofT
@@ -0,0 +1,146 @@
+This file is the original README, and is a little out of date. It
+is also very specific to UofT, since there was a time when the daemon
+was only run here.
+
+To run this:
+
+(1) Fix your kernel's value of tickadj. Tickadj sets both the
+ precision with which time slews can be performed and the amount
+ of slew you can do in a given interval. Xntpd operates by making
+ a bunch of little adjustments. Make tickadj too large (the default
+ value almost always is) and xntpd will perform poorly since the
+ slews will disappear in the roundoff. Make tickadj too small
+ and large slews won't complete before the next adjustment is
+ ready.
+
+ To determine a good value of tickadj to use, first determine your
+ kernel's value of hz (50 on a Sun 3, 100 on Sun 4's and vaxes).
+ Divide that number into 500 (i.e. compute 500/hz) and use an
+ integer near there as tickadj (say, 10 on Sun 3's, 5 on Sun 4's
+ and vaxes). Then adb your kernel and write the new value. You
+ should probably do both the running kernel and the disk image.
+
+ If your machine doesn't come with adb, or if the kernel is of a
+ non-Berkeley flavour, take a look at the util directory, particularly
+ util/tickadj.
+
+(2) Edit the Config file in this directory. You *must* tell it whether
+ your machine uses big endian or little endian byte order. Also,
+ Suns running SunOS 3.x require special consideration, as well as Vaxes
+ running Ultrix 2.0 and compilers which don't understand `signed char'
+ declarations. When you've got all this worked out, type `make makefiles'
+ to distribute configuration information to Makefiles for individual
+ programs, followed by `make' to compile everything.
+
+(2a) Note that, among other things, two programs were made in the authstuff
+ directory, authcert and authspeed. The last two are utilities for
+ checking the authentication code. Type `authcert < certdata'. If
+ this provokes a massive failure you probably got the byte order wrong
+ in the Config file. Type `authspeed -n 10000 auth.samplekeys', or
+ something, a couple of times to get a value of authdelay to stick in
+ the configuration file. The numbers for machines I've tried look like:
+
+ uVax II 0.001450
+ Sun 3/180 0.000620
+ uVax III 0.000515
+ Sun 3/60 0.000455
+ IBM RT Mdl 125 0.000323
+ Sun 3/280 0.000302
+ Sun 4/280 0.000110
+ MIPS M/1000 0.000100
+
+(3) Typing `make install' will nstall xntpd, xntpdc, ntpdate and ntpq. Watch
+ the install location in the Config file.
+
+(4) If you will be running xntpd (see 4a below for the alternative),
+ configure it (configuration is necessary for all machines now, though
+ this restriction will go away when I get broadcast time fully tested).
+ xntpd reads its configuration from /etc/ntp.conf (by default) and
+ you must tell it which machines it is to get its time from in
+ here.
+
+ Note that NTP operates in a hierarchy. Machines with radio clocks
+ (which are stratum 1 servers) are at the top of the heap, in that
+ all time originates with them. The situation with servers locally
+ is in a state of flux. We currently have one semi-reliable stratum 1
+ server on campus (suzuki.ccie), and maintain three other stratum 2
+ servers which (gently) access other people's off-campus stratum 1
+ servers. All of these machines are lightly loaded and have good
+ quality clocks, and so will probably do until we get some more stratum 1
+ weight.
+
+ Thus you are probably faced with choosing whether your hosts should
+ be stratum 2 or stratum 3 (or stratum 3 or 4 when suzuki's clock is down).
+ The rule of thumb is to make your best clocks and/or your file servers
+ stratum 2 (or 3) by peering them with the four campus servers, and make
+ lesser clocks and clients stratum 3 (or 4) by peering them with near
+ by servers which are synchonized to the campus servers. The second rule
+ of thumb is that more servers are better. It is quite possible to
+ synchronize with just a single server, but if you do your xtnpd daemon
+ won't have any cross checks to tell it when the server has gone
+ wonky. 3 or 4 lower stratum peers is about right. Note that while
+ you can also peer with same-stratum peers, you shouldn't do this
+ unless the same-stratum peer is exchanging time with a lower stratum
+ peer you don't talk to directly.
+
+ Anyway, for your stratum 2 servers you can probably use ntp.conf
+ from the conf directory directly. You will have to handcraft the
+ peer assocations for your stratum 3 servers.
+
+ Oh, and a note about the drift file (see ntp.conf). One of the
+ things xntpd does is accumulate a correction for the frequency of
+ the crystal in your computer. It usually takes a day or so of
+ running to figure this out, after which the value will usually remain
+ pretty stable, especially if the computer is in a machine room. The
+ value is printed in your syslog file (once a minute, currently, though
+ this will change), and can be obtained from the daemon using xntpdc.
+
+ To avoid having to wait a day after restarts before the computer
+ synchronizes really well, xntpd will optionally write its current
+ value of the frequency correction into a file, once an hour. When
+ it is killed and restarted, xntpd reinitializes itself to this
+ value on start up. This is an advantageous feature, so a driftfile
+ line should always be included in the configuration file.
+
+(4a) Xntpd is a daemon. It will keep your time exquisitely precise under
+ normal conditions (it is quite capable of keeping a good clock within
+ a millisecond of a good server. Our servers aren't normally this
+ good, yet, but may become so when we get a few more stable local
+ stratum 1 peers). Even when cut off entirely from its servers xntpd
+ will prevent your clock from drifting seriously by continuing to apply
+ its accumulated frequency correction. The cost of this is that xntpd
+ will permanently consume memory while it is running, and real memory
+ at that since xntpd is unlikely to ever swap out. This cost is
+ currently over 100 kb.
+
+ If you aren't too worried about millisecond timing and feel religious
+ about keeping memory consumption at a minimum (perhaps on memory-poor
+ workstations), a passable alternative might be to run ntpdate instead.
+ Ntpdate is the NTP equivalent of rdate, a one shot date setting
+ program, and implements the same multiple sample/multiple server
+ filter algorithms as xntpd. Ntpdate was explicitly designed to be
+ run repeatly from cron, though it also makes a good boot time date
+ setter. Running ntpdate from cron on an hourly basis will keep all
+ but seriously broken clocks within 100 ms of on-time, and for most
+ clocks will probably do better than 50 ms. If this is an attractive
+ alternative see the manual page. You should choose ntpdate's servers
+ as you would the peer associations for a stratum 3 xntpd server.
+
+(5) Once everything is configured, start the daemon(s). ntpq can be
+ used to see what xntpd is doing. It runs both interactive and from
+ the command line, type ? to see the interactive commands and ? command
+ to see what a command does. The `peers' command is a good one. ntpq
+ can also be used to see what other peoples' servers are doing, in
+ particular the fuzzball primary servers.
+
+(6) If you want to use the authentication facility (this might be useful
+ if, for example, you were running Kerberos since this prevents people
+ from setting your time back and doing replay attacks on the server),
+ you might find a couple of useful programs in the auth_stuff directory.
+ mkrandkeys will generate some very random keys to use. keyparity
+ generates odd parity bits for keys (needed for the key file) and will
+ convert between key formats.
+
+All bug reports gratefully received.
+
+Dennis
diff --git a/usr.sbin/xntpd/doc/acts.c b/usr.sbin/xntpd/doc/acts.c
new file mode 100644
index 0000000..7d3733b
--- /dev/null
+++ b/usr.sbin/xntpd/doc/acts.c
@@ -0,0 +1,878 @@
+/*
+ * refclock_acts - clock driver for the NIST Automated Computer Time
+ * Service aka Amalgamated Containerized Trash Service (ACTS)
+ */
+#if defined(REFCLOCK) && defined(ACTS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NIST Automated Computer Time Service (ACTS).
+ * It periodically dials a prespecified telephone number, receives the
+ * NIST timecode data and calculates the local clock correction. It is
+ * designed primarily for use as a backup when neither a radio clock nor
+ * connectivity to Internet time servers is available. For the best
+ * accuracy, the individual telephone line/modem delay needs to be
+ * calibrated using outside sources.
+ *
+ * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ * toll call from a residence telephone in Newark, DE, costs between 14
+ * and 27 cents, depending on time of day, and from a campus telephone
+ * between 3 and 4 cents, although it is not clear what carrier and time
+ * of day discounts apply in this case. The modem dial string will
+ * differ depending on local telephone configuration, etc., and is
+ * specified by the phone command in the configuration file. The
+ * argument to this command is an AT command for a Hayes compatible
+ * modem.
+ *
+ * The accuracy produced by this driver should be in the range of a
+ * millisecond or two, but may need correction due to the delay
+ * characteristics of the individual modem involved. For undetermined
+ * reasons, some modems work with the ACTS echo-delay measurement scheme
+ * and some don't. This driver tries to do the best it can with what it
+ * gets. Initial experiments with a Practical Peripherals 9600SA modem
+ * here in Delaware suggest an accuracy of a millisecond or two can be
+ * achieved without the scheme by using a fudge time1 value of 65.0 ms.
+ * In either case, the dispersion for a single call involving ten
+ * samples is about 1.3 ms.
+ *
+ * The driver can operate in either of two modes, as determined by the
+ * mode parameter in the server configuration command. In mode 0 the
+ * driver operates continuously at intervals determined by the fudge
+ * time1 parameter, as described above. In mode 1 the driver is enabled
+ * only when no other sources of synchronization are available and when
+ * we have gone more than MAXOUTAGE (3600 s) since last synchronized by
+ * other sources of synchronization.
+ *
+ * For reliable call management, this driver requires a 1200-bps modem
+ * with a Hayes-compatible command set and control over the modem data
+ * terminal ready (DTR) control line. Present restrictions require the
+ * use of a POSIX-compatible programming interface, although other
+ * interfaces may work as well. The modem setup string is hard-coded in
+ * the driver and may require changes for nonstandard modems or special
+ * circumstances.
+ *
+ * Further information can be found in the README.refclock file in the
+ * xntp3 distribution.
+ *
+ * Fudge Factors
+ *
+ * Ordinarily, the propagation time correction is computed automatically
+ * by ACTS and the driver. When this is not possible or erratic due to
+ * individual modem characteristics, the fudge flag2 switch should be
+ * set to disable the ACTS echo-delay scheme. In any case, the fudge
+ * time1 parameter can be used to adjust the propagation delay as
+ * required.
+ *
+ * The ACTS call interval is determined in one of three ways. In MANUAL
+ * mode a call is initiated by setting fudge flag1 using xntpdc, either
+ * manually or via a cron job. In AUTO mode this flag is set by the peer
+ * timer, which is controlled by the sys_poll variable in response to
+ * measured errors. In BACKUP mode the driver is ordinarily asleep, but
+ * awakes (in AUTO mode) if all other synchronization sources are lost.
+ * In either AUTO or BACKUP modes, the call interval increases as long
+ * as the measured errors do not exceed the value of the fudge time2
+ * parameter.
+ *
+ * When the fudge flag1 is set, the ACTS calling program is activated.
+ * This program dials each number listed in the phones command of the
+ * configuration file in turn. If a call attempt fails, the next number
+ * in the list is dialed. The fudge flag1 and counter are reset and the
+ * calling program terminated if (a) a valid clock update has been
+ * determined, (b) no more numbers remain in the list, (c) a device
+ * fault or timeout occurs or (d) fudge flag1 is reset manually using
+ * xntpdc.
+ */
+
+/*
+ * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
+ * (reformatted from ACTS on-line computer help information)
+ *
+ * The following is transmitted (at 1200 baud) following completion of
+ * the telephone connection.
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
+ * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
+ * etc..etc...etc.......
+ *
+ * UTC = Universal Time Coordinated, the official world time referred to
+ * the zero meridian.
+ *
+ * DST Daylight savings time characters, valid for the continental
+ * U.S., are set as follows:
+ *
+ * 00 We are on standard time (ST).
+ * 01-49 Now on DST, go to ST when your local time is 2:00 am and
+ * the count is 01. The count is decremented daily at 00
+ * (UTC).
+ * 50 We are on DST.
+ * 51-99 Now on ST, go to DST when your local time is 2:00 am and
+ * the count is 51. The count is decremented daily at 00
+ * (UTC).
+ *
+ * The two DST characters provide up to 48 days advance notice of a
+ * change in time. The count remains at 00 or 50 at other times.
+ *
+ * LS Leap second flag is set to "1" to indicate that a leap second is
+ * to be added as 23:59:60 (UTC) on the last day of the current UTC
+ * month. The LS flag will be reset to "0" starting with 23:59:60
+ * (UTC). The flag will remain on for the entire month before the
+ * second is added. Leap seconds are added as needed at the end of
+ * any month. Usually June and/or December are chosen.
+ *
+ * The leap second flag will be set to a "2" to indicate that a
+ * leap second is to be deleted at 23:59:58--00:00:00 on the last
+ * day of the current month. (This latter provision is included per
+ * international recommendation, however it is not likely to be
+ * required in the near future.)
+ *
+ * DUT1 Approximate difference between earth rotation time (UT1) and
+ * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC.
+ *
+ * MJD Modified Julian Date, often used to tag certain scientific data.
+ *
+ * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity.
+ * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud
+ * the MJD and DUT1 values are deleted and the time is transmitted only
+ * on even seconds.
+ *
+ * Maximum on line time will be 56 seconds. If all lines are busy at any
+ * time, the oldest call will be terminated if it has been on line more
+ * than 28 seconds, otherwise, the call that first reaches 28 seconds
+ * will be terminated.
+ *
+ * Current time is valid at the "on-time" marker (OTM), either "*" or
+ * "#". The nominal on-time marker (*) will be transmitted 45 ms early
+ * to account for the 8 ms required to send 1 character at 1200 Baud,
+ * plus an additional 7 ms for delay from NIST to the user, and
+ * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems.
+ * If the caller echoes all characters, NIST will measure the round trip
+ * delay and advance the on-time marker so that the midpoint of the stop
+ * bit arrives at the user on time. The amount of msADV will reflect the
+ * actual required advance in milliseconds and the OTM will be a "#".
+ *
+ * (The NIST system requires 4 or 5 consecutive delay measurements which
+ * are consistent before switching from "*" to "#". If the user has a
+ * 1200 Baud modem with the same internal delay as that used by NIST,
+ * then the "#" OTM should arrive at the user within +-2 ms of the
+ * correct time.
+ *
+ * However, NIST has studied different brands of 1200 Baud modems and
+ * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM
+ * of +-10 ms. For many computer users, +-10 ms accuracy should be more
+ * than adequate since many computer internal clocks can only be set
+ * with granularity of 20 to 50 ms. In any case, the repeatability of
+ * the offset for the "#" OTM should be within +-2 ms, if the dial-up
+ * path is reciprocal and the user doesn't change the brand or model of
+ * modem used.
+ *
+ * This should be true even if the dial-up path on one day is a land-
+ * line of less than 40 ms (one way) and on the next day is a satellite
+ * link of 260 to 300 ms. In the rare event that the path is one way by
+ * satellite and the other way by land line with a round trip
+ * measurement in the range of 90 to 260 ms, the OTM will remain a "*"
+ * indicating 45 ms advance.
+ *
+ * For user comments write:
+ * NIST-ACTS
+ * Time and Frequency Division
+ * Mail Stop 847
+ * 325 Broadway
+ * Boulder, CO 80303
+ *
+ * Software for setting (PC)DOS compatable machines is available on a
+ * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference
+ * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301)
+ * 975-6776
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "ACTS" /* reference ID */
+#define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define MSGCNT 10 /* we need this many ACTS messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 50 /* length of valid timecode string */
+#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define ACTS_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max outage before ACTS kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE 15 /* timecode timeout (s) */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern u_long last_time; /* last clock update time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_config module
+ */
+extern char sys_phone[][MAXDIAL]; /* modem dial strings */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* who is running the show */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern struct peer *sys_peer; /* system peer structure pointer */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ struct event timer; /* timeout timer */
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of ACTS messages received */
+ long redial; /* interval to next automatic call */
+ double msADV; /* millisecond advance of last message */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static int acts_write P((struct peer *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used (old acts_control) */
+ noentry, /* not used (old acts_init) */
+ noentry, /* not used (old acts_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * acts_start - open the devices and initialize data for processing
+ */
+static int
+acts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) {
+ syslog(LOG_ERR, "clock %s ACTS no modem control",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct actsunit *)
+ emalloc(sizeof(struct actsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct actsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = ACTS_MINPOLL;
+ peer->maxpoll = ACTS_MAXPOLL;
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!acts_write(peer, MODEM_SETUP)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Set up the driver timeout
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = acts_timeout;
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ TIMER_DEQUEUE(&up->timer);
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ int i;
+ l_fp tstmp;
+ u_fp disp;
+ char hangup = '%'; /* ACTS hangup */
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ u_int dst; /* daylight/standard time indicator */
+ u_int leap; /* leap-second indicator */
+ double dut1; /* DUT adjustment */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* this is NIST and you're not */
+ char flag; /* calibration flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("acts: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 2.
+ */
+ (void)strncpy(str, strtok(pp->lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS modem status %s",
+ ntoa(&peer->srcadr), pp->lastcode);
+ acts_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + CONNECT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->msgcnt = 0;
+ up->state++;
+ }
+ return;
+
+ case 2:
+
+ /*
+ * State 2. The call has been answered and we are
+ * waiting for the first ACTS message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or ACTS is
+ * down.
+ */
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + TIMECODE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->state++;
+ }
+
+ /*
+ * Real yucky things here. Ignore everything except timecode
+ * messages, as determined by the message length. We told the
+ * terminal routines to end the line with '*' and the line
+ * discipline to strike a timestamp on that character. However,
+ * when the ACTS echo-delay scheme works, the '*' eventually
+ * becomes a '#'. In this case the message is ended by the <CR>
+ * that comes about 200 ms after the '#' and the '#' cannot be
+ * echoed at the proper time. But, this may not be a lose, since
+ * we already have good data from prior messages and only need
+ * the millisecond advance calculated by ACTS. So, if the
+ * message is long enough and has an on-time character at the
+ * right place, we consider the message (but not neccesarily the
+ * timestmap) to be valid.
+ */
+ if (pp->lencode != LENCODE)
+ return;
+
+ /*
+ * We apparently have a valid timecode message, so dismember it
+ * with sscan(). This routine does a good job in spotting syntax
+ * errors without becoming overly pedantic.
+ *
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV OTM
+ * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) *
+ */
+ if (sscanf(pp->lastcode,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute,
+ &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Some modems can't be trusted (the Practical Peripherals
+ * 9600SA comes to mind) and, even if they manage to unstick
+ * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2
+ * to disable echoes, if neccessary.
+ */
+ if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag &
+ CLK_FLAG2))
+ (void)write(pp->io.fd, &flag, 1);
+
+ /*
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year. The ACTS timecode format croaks then anyway. Life is
+ * short. Would only the timecode mavens resist the urge to
+ * express months of the year and days of the month in favor of
+ * days of the year.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received. If we collect MSGCNT samples before the '#' on-time
+ * character, we use the results of the filter as is. If the '#'
+ * is found before that, the adjusted msADV is used to correct
+ * the propagation delay.
+ */
+ up->msgcnt++;
+ if (flag == '#') {
+ L_CLR(&tstmp);
+ TVUTOTSF((long)((msADV - up->msADV) * 1000.),
+ tstmp.l_uf);
+ L_ADD(&pp->offset, &tstmp);
+ } else {
+ up->msADV = msADV;
+ if (!refclock_process(pp, up->msgcnt, up->msgcnt -
+ up->msgcnt / 3)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+ }
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ * The little dance with the '%' character is an undocumented
+ * ACTS feature that hangs up the phone real quick without
+ * waiting for carrier loss or long-space disconnect, but we do
+ * these clumsy things anyway.
+ */
+ disp = LFPTOFP(&pp->fudgetime2);
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion +
+ (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ TIMER_DEQUEUE(&up->timer);
+ (void)write(pp->io.fd, &hangup, 1);
+ up->state = 0;
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call to ACTS. If not, the enable flag can be set using
+ * xntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+/*
+ * acts_timeout - called by the timer interrupt
+ */
+static void
+acts_timeout(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->state) {
+ acts_disc(peer);
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the ACTS calling program is activated
+ * by the xntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the ACTS calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the ACTS calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->refclktype !=
+ REFCLK_NIST_ACTS) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ syslog(LOG_NOTICE,
+ "clock %s ACTS invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via xntpdc, the ACTS calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Initiate a call to the ACTS service. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds. We advance to the next modem dial
+ * string. If none are left, we log a notice and clear the
+ * enable flag. For future enhancement: call the site RP and
+ * leave an obscene message in his voicemail.
+ */
+ if (sys_phone[up->pollcnt][0] == '\0') {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+#ifdef DEBUG
+ if (debug)
+ printf("acts: calling program terminated\n");
+#endif
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Raise DTR, call ACTS and start the answer timeout. We think
+ * it strange if the OK status has not been received from the
+ * modem, but plow ahead anyway.
+ */
+ if (strcmp(pp->lastcode, "OK") != 0)
+ syslog(LOG_NOTICE, "clock %s ACTS no modem status",
+ ntoa(&peer->srcadr));
+ (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ (void)acts_write(peer, sys_phone[up->pollcnt]);
+ syslog(LOG_NOTICE, "clock %s ACTS calling %s\n",
+ ntoa(&peer->srcadr), sys_phone[up->pollcnt]);
+ up->state = 1;
+ up->pollcnt++;
+ pp->polls++;
+ up->timer.event_time = current_time + ANSWER;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+acts_disc(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while ACTS is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ if (up->state > 0) {
+ up->state = 0;
+ syslog(LOG_NOTICE, "clock %s ACTS call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: call failed %d\n", up->state);
+#endif
+ }
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_write - write a message to the serial port
+ */
+int
+acts_write(peer, str)
+ struct peer *peer;
+ char *str;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/doc/notes.txt b/usr.sbin/xntpd/doc/notes.txt
new file mode 100644
index 0000000..1dd59f2
--- /dev/null
+++ b/usr.sbin/xntpd/doc/notes.txt
@@ -0,0 +1,1258 @@
+ Notes on Xntpd Configuration
+
+ David L. Mills (mills@udel.edu)
+ University of Delaware
+ 14 January 1993
+
+Introduction
+
+This document is a collection of notes concerning the use of xntpd and
+related programs, and on coping with the Network Time Protocol (NTP) in
+general. It is a major rewrite and update of an earlier document written
+by Dennis Ferguson of the University of Toronto dated 5 November 1989.
+It includes many changes and additions resulting from the NTP Version 3
+specification and new implementation features. It supersedes the earlier
+document, which should no longer be used for new configurations.
+
+Xntpd is a complete implementation of the NTP Version 3 specification as
+defined in RFC 1305. It also retains compatibility with both NTP Version
+2, as defined in RFC 1119, and NTP Version 1, as defined in RFC 1059,
+although this compatibility is sometimes strained and only
+semiautomatic. In order to support in principle the ultimate precision
+of about 232 picoseconds in the NTP specification, xntpd does no
+floating-point arithmetic and instead manipulates the 64-bit NTP
+timestamps as unsigned 64-bit integers. Xntpd fully implements NTP
+Versions 2 and 3 authentication and a mode-6 control-message facility.
+As extensions to the specification, a flexible address-and-mask
+restriction facility has been included, along with a private mode-7
+control-message facility used to remotely reconfigure the system and
+monitor a considerable amount of internal detail.
+
+The code is biased towards the needs of a busy time server with
+numerous, possibly hundreds, of clients and other servers. Tables are
+hashed to allow efficient handling of many associations, though at the
+expense of additional overhead when the number of associations is small.
+Many fancy features have been included to permit efficient management
+and monitoring of a busy primary server, features which are simply
+excess baggage for a server on a high stratum client. The code was
+written with near demonic attention to details which can affect
+precision and as a consequence should be able to make good use of high
+performance, special purpose hardware such as precision oscillators and
+radio clocks. The present code supports a number of radio clocks,
+including those for the WWV, CHU, WWVB, DCF77, GOES and GPS radio and
+satellite services. The server methodically avoids the use of Unix-
+specific library routines where possible by implementing local versions,
+in order to aid in porting the code to perverse Unix and non-Unix
+platforms.
+
+While this implementation slavishly obeys the NTP specification RFC
+1305, it has been specifically tuned to achieve the highest accuracy
+possible on whatever hardware and operating-system platform is
+available. In general, its precision is limited only by that of the
+onboard time-of-day clock maintained by the hardware and operating
+system, while its stability is limited only by that of the onboard
+frequency source, usually an uncompensated crystal oscillator. On modern
+RISC-based processors connected directly to radio clocks via serial-
+asynchronous interfaces, the accuracy is usually limited by that of the
+radio clock and interface to the order of a few milliseconds. The code
+includes special features to support a one-pulse-per-second (1-pps)
+signal generated by some radio clocks. When used in conjunction with a
+suitable hardware level converter, the accuracy can be improved to the
+order of 100 microseconds. Further improvement is possible using an
+outboard, stabilized frequency source, in which the accuracy and
+stability are limited only by the characteristics of that source.
+
+The xntp3 distribution includes, in addition to the daemon itself
+(xntpd), several utility programs, including two remote-monitoring
+programs (ntpq, xntpdc), a remote clock-setting program similar to the
+Unix rdate program (ntpdate), a traceback utility useful to discover
+suitable synchronization sources (ntptrace), and various programs used
+to configure the local platform and calibrate the intrinsic errors. NTP
+has been ported to a large number of platforms, including most RISC and
+CISC workstations and mainframes manufactured today. Example
+configuration files for many models of these machines are included in
+the xntp3 distribution. While in most cases the standard version of the
+implementation runs with no hardware or operating-system modifications,
+not all features of the distribution are available on all platforms. For
+instance, a special feature allowing Sun 4s to achieve accuracies in the
+order of 100 microseconds requires some minor changes and additions to
+the kernel and input/output support.
+
+There are, however, several drawbacks to all of this. Xntpd is very,
+very fat. This is rotten if your intended platform for the daemon is
+memory-limited. Xntpd uses SIGIO for all input, a facility which appears
+to not enjoy universal support and whose use seems to exercise the parts
+of your vendors' kernels which are most likely to have been done poorly.
+The code is unforgiving in the face of kernel problems which affect
+performance, and generally requires that you repair the problems in
+order to achieve acceptable performance. The code has a distinctly
+experimental flavour and contains features which could charitably be
+termed failed experiments, but which have not been hacked out yet. There
+is code which has not been thoroughly tested (e.g. leap-second support)
+due to the inconvenience of setting up tests. Much was learned from the
+addition of support for a variety of radio clocks, with the result that
+this support could use some rewriting.
+
+How NTP Works
+
+The approach used by NTP to achieve reliable time synchronization from a
+set of possibly unreliable remote time servers is somewhat different
+than other such protocols. In particular, NTP does not attempt to
+synchronize clocks to each other. Rather, each server attempts to
+synchronize to UTC (i.e., Universal Coordinated Time) using the best
+available source and available transmission paths to that source. This
+is a fine point which is worth understanding. A group of NTP-
+synchronized clocks may be close to each other in time, but this is not
+a consequence of the clocks in the group having synchronized to each
+other, but rather because each clock has synchronized closely to UTC via
+the best source it has access to. As such, trying to synchronize a set
+of clocks to a set of servers whose time is not in mutual agreement may
+not result in any sort of useful synchronization of the clocks, even if
+you don't care about UTC. NTP operates on the premise that there is one
+true standard time, and that if several servers which claim
+synchronization to standard time disagree about what that time is, then
+one or more of them must be broken. There is no attempt to resolve
+differences more gracefully since the premise is that substantial
+differences cannot exist. In essence, NTP expects that the time being
+distributed from the root of the synchronization subnet will be derived
+from some external source of UTC (e.g. a radio clock). This makes it
+somewhat inconvenient (though not impossible) to synchronize hosts
+together without a reliable source of UTC to synchronize them to. If
+your network is isolated and you cannot access other people's servers
+across the Internet, a radio clock may make a good investment.
+
+Time is distributed through a hierarchy of NTP servers, with each server
+adopting a "stratum" which indicates how far away from an external
+source of UTC it is operating at. Stratum-1 servers, which are at the
+top of the pile (or bottom, depending on your point of view), have
+access to some external time source, usually a radio clock synchronized
+to time signal broadcasts from radio stations which explicitly provide a
+standard time service. A stratum-2 server is one which is currently
+obtaining time from a stratum-1 server, a stratum-3 server gets its time
+from a stratum-2 server, and so on. To avoid long lived synchronization
+loops the number of strata is limited to 15.
+
+Each client in the synchronization subnet (which may also be a server
+for other, higher stratum clients) chooses exactly one of the available
+servers to synchronize to, usually from among the lowest stratum servers
+it has access to. It is thus possible to construct a synchronization
+subnet where each server has exactly one source of lower stratum time to
+synchronize to. This is, however, not an optimal configuration, for
+indeed NTP operates under another premise as well, that each server's
+time should be viewed with a certain amount of distrust. NTP really
+prefers to have access to several sources of lower stratum time (at
+least three) since it can then apply an agreement algorithm to detect
+insanity on the part of any one of these. Normally, when all servers are
+in agreement, NTP will choose the best of these, where "best" is defined
+in terms of lowest stratum, closest (in terms of network delay) and
+claimed precision, along with several other considerations. The
+implication is that, while one should aim to provide each client with
+three or more sources of lower stratum time, several of these will only
+be providing backup service and may be of lesser quality in terms of
+network delay and stratum (i.e. a same-stratum peer which receives time
+from lower stratum sources the local server doesn't access directly can
+also provide good backup service).
+
+Finally, there is the issue of association modes. There are a number of
+modes in which NTP servers can associate with each other, with the mode
+of each server in the pair indicating the behaviour the other server can
+expect from it. In particular, when configuring a server to obtain time
+from other servers, there is a choice of two modes which may be
+alternatively used. Configuring an association in symmetric-active mode
+(usually indicated by a "peer" declaration in configuration files)
+indicates to the remote server that one wishes to obtain time from the
+remote server and that one is also willing to supply time to the remote
+server if need be. This mode is appropriate in configurations involving
+a number of redundant time servers interconnected via diverse network
+paths, which is presently the case for most stratum-1 and stratum-2
+servers on the Internet today. Configuring an association in client mode
+(usually indicated by a "server" declaration in configuration files)
+indicates that one wishes to obtain time from the remote server, but that
+one is not willing to provide time to the remote server. This mode is
+appropriate for file-server and workstation clients that do not provide
+synchronization to other local clients. Client mode is also useful for
+boot-date-setting programs and the like, which really have no time to
+provide and which don't retain state about associations over the longer
+term.
+
+Configuring Your Subnet
+
+At startup time the xntpd daemon running on a host reads the initial
+configuration information from a file, usually /etc/ntp.conf, unless a
+different name has been specified at compile time. Putting something in
+this file which will enable the host to obtain time from somewhere else
+is usually the first big hurdle after installation of the software
+itself, which is described in other documents included in the xntp3
+distribution. At its simplest, what you need to do in the configuration
+file is declare the servers that the daemon should poll for time
+synchronization. In principle, no such list is needed if some other time
+server explicitly mentions the host and is willing to provide
+synchronization; however, this is considered dangerous, unless the
+access control or authentication features (described later) are in use.
+
+In the case of a workstation operating in an enterprise network for a
+public or private organization, there is often an administrative
+department that coordinates network services, including NTP. Where
+available, the addresses of appropriate servers can be provided by that
+department. However, if this infrastructure is not available, it is
+necessary to explore some portion of the existing NTP subnet now running
+in the Internet. There are at present many thousands of time servers
+running NTP in the Internet, a significant number of which are willing
+to provide a public time-synchronization service. Some of these are
+listed in a file maintained on the Internet host louie.udel.edu
+(128.175.1.3) on the path pub/ntp/doc/clock.txt. This file is updated on
+a regular basis using information provided voluntarily by various site
+administrators. There are other ways to explore the nearby subnet using
+the ntptrace and ntpq programs. See the man pages for further
+information on these programs.
+
+It is vital to carefully consider the issues of robustness and
+reliability when selecting the sources of synchronization. Normally, not
+less than three sources should be available, preferably selected to
+avoid common points of failure. It is usually better to choose sources
+which are likely to be "close" to you in terms of network topology,
+though you shouldn't worry overly about this if you are unable to
+determine who is close and who isn't. Normally, it is much more serious
+when a server becomes faulty and delivers incorrect time than when it
+simply stops operating, since an NTP-synchronized host normally can
+coast for hours or even days without its clock accumulating serious
+error over one second, for instance. Selecting at least three sources
+from different operating administrations, where possible, is the minimum
+recommended, although a lesser number could provide acceptable service
+with a degraded degree of robustness.
+
+Normally, it is not considered good practice for a single workstation to
+request synchronization from a primary (stratum-1) time server. At
+present, these servers provide synchronization for hundreds of clients
+in many cases and could, along with the network access paths, become
+seriously overloaded if large numbers of workstation clients requested
+synchronization directly. Therefore, workstations located in sparsely
+populated administrative domains with no local synchronization
+infrastructure should request synchronization from nearby stratum-2
+servers instead. In most cases the keepers of those servers listed in
+the clock.txt file provide unrestricted access without prior permission;
+however, in all cases it is considered polite to notify the
+administrator listed in the file upon commencement of regular service.
+In all cases the access mode and notification requirements listed in the
+file must be respected.
+
+In the case of a gateway or file server providing service to a
+significant number of workstations or file servers in an enterprise
+network it is even more important to provide multiple, redundant sources
+of synchronization and multiple, diversity-routed, network access paths.
+The preferred configuration is at least three administratively
+coordinated time servers providing service throughout the administrative
+domain including campus networks and subnetworks. Each of these should
+obtain service from at least two different outside sources of
+synchronization, preferably via different gateways and access paths.
+These sources should all operate at the same stratum level, which is one
+less than the stratum level to be used by the local time servers
+themselves. In addition, each of these time servers should peer with all
+of the other time servers in the local administrative domain at the
+stratum level used by the local time servers, as well as at least one
+(different) outside source at this level. This configuration results in
+the use of six outside sources at a lower stratum level (toward the
+primary source of synchronization, usually a radio clock), plus three
+outside sources at the same stratum level, for a total of nine outside
+sources of synchronization. While this may seem excessive, the actual
+load on network resources is minimal, since the interval between polling
+messages exchanged between peers usually ratchets back to no more than
+one message every 17 minutes.
+
+The stratum level to be used by the local time servers is an engineering
+choice. As a matter of policy, and in order to reduce the load on the
+primary servers, it is desirable to use the highest stratum consistent
+with reliable, accurate time synchronization throughout the
+administrative domain. In the case of enterprise networks serving
+hundreds or thousands of client file servers and workstations,
+conventional practice is to obtain service from stratum-1 primary
+servers such as listed in the clock.txt file. When choosing sources away
+from the primary sources, the particular synchronization path in use at
+any time can be verified using the ntptrace program included in the
+xntp3 distribution. It is important to avoid loops and possible common
+points of failure when selecting these sources. Note that, while NTP
+detects and rejects loops involving neighboring servers, it does not
+detect loops involving intervening servers. In the unlikely case that
+all primary sources of synchronization are lost throughout the subnet,
+the remaining servers on that subnet can form temporary loops and, if
+the loss continues for an interval of many hours, the servers will drop
+off the subnet and free-run with respect to their internal (disciplined)
+timing sources.
+
+In many cases the purchase of one or more radio clocks is justified, in
+which cases good engineering practice is to use the configurations
+described above and connect the radio clock to one of the local servers.
+This server is then encouraged to participate in a special primary-
+server subnetwork in which each radio-equipped server peers with several
+other similarly equipped servers. In this way the radio-equipped server
+may provide synchronization, as well as receive synchronization, should
+the local or remote radio clock(s) fail or become faulty. Xntpd treats
+attached radio clock(s) in the same way as other servers and applies the
+same criteria and algorithms to the time indications, so can detect when
+the radio fails or becomes faulty and switch to alternate sources of
+synchronization. It is strongly advised, and in practice for most
+primary servers today, to employ the authentication or access-control
+features of the xntp3 distribution in order to protect against hostile
+penetration and possible destabilization of the time service.
+
+Using this or similar strategies, the remaining hosts in the same
+administrative domain can be synchronized to the three (or more)
+selected time servers. Assuming these servers are synchronized directly
+to stratum-1 sources and operate normally as stratum-2, the next level
+away from the primary source of synchronization, for instance various
+campus file servers, will operate at stratum 3 and dependent
+workstations at stratum 4. Engineered correctly, such a subnet will
+survive all but the most exotic failures or even hostile penetrations of
+the various, distributed timekeeping resources.
+
+The above arrangement should provide very good, robust time service with
+a minimum of traffic to distant servers and with manageable loads on the
+local servers. While it is theoretically possible to extend the
+synchronization subnet to even higher strata, this is seldom justified
+and can make the maintenance of configuration files unmanageable.
+Serving time to a higher stratum peer is very inexpensive in terms of
+the load on the lower stratum server if the latter is located on the
+same concatenated LAN. When justified by the accuracy expectations, NTP
+can be operated in broadcast mode, so that clients need only listen for
+periodic broadcasts and do not need to send anything.
+
+When planning your network you might, beyond this, keep in mind a few
+generic don'ts, in particular:
+
+1. Don't synchronize a local time server to another peer at the same
+ stratum, unless the latter is receiving time from lower stratum
+ sources the former doesn't talk to directly. This minimizes the
+ occurance of common points of failure, but does not eliminate them
+ in cases where the usual chain of associations to the primary
+ sources of synchronization are disrupted due to failures.
+2. Don't configure peer associations with higher stratum servers. Let
+ the higher strata configure lower stratum servers, but not the
+ reverse. This greatly simplifies configuration file maintenance,
+ since there is usually much greater configuration churn in the high
+ stratum clients such as personal workstations.
+
+3. Don't synchronize more than one time server in a particular
+ administrative domain to the same time server outside that domain.
+ Such a practice invites common points of failure, as well as raises
+ the possibility of massive abuse, should the configuration file be
+ automatically distributed do a large number of clients.
+
+There are many useful exceptions to these rules. When in doubt, however,
+follow them.
+
+Dennis Ferguson writes: Note that mention was made of machines with
+"good" clocks versus machines with "bad" ones. There are two things that
+make a clock good, the precision of the clock (e.g. how many low order
+bits in a time value are actually significant) and the frequency of
+occurance (or lack thereof) of such things as lost clock interrupts.
+Among the most common computers I have observed there to be a fairly
+simple algorithm for determining the goodness of its clock. If the
+machine is a Vax, it probably has a good clock (the low order bit in the
+time is in the microseconds and most of these seem to manage to get
+along without losing clock interrupts). If the machine is a Sun 3 it
+probably doesn't (the low order clock bit is at the 10 or 20 millisecond
+mark and Sun 3s like to lose clock interrupts, particularly if they have
+a screen and particularly if they run SunOS 4.0.x). If you have IBM RTs
+running AOS 4.3, they have fair clocks (low order clock bit at about a
+millisecond and they don't lose clock interrupts, though they do have
+trouble with clock rollovers while reading the low order clock bits) but
+I recommend them as low stratum NTP servers anyway since they aren't
+much use as anything else. Sun 4s running SunOS 4.1.1 make very good
+time servers, once some native foolishness mentioned below is
+surmounted. [However, it is very important to avoid using the keyboard
+firmware, which can cause severe interrupt latencies, in favor of the
+software drivers ordinarily used in conjunction with a windowing system.
+- DLM] For other machines you are on your own since I don't have enough
+data points to venture an opinion. In any event, if at all possible you
+should try to use machines with good clocks for the lower strata.
+
+Configuring Your Server or Client
+
+As mentioned previously, the configuration file is usually called
+/etc/ntp.conf. This is an ASCII file conforming to the usual comment and
+whitespace conventions. A working configuration file might look like (In
+this and other examples, do not copy this directly.):
+
+ # peer configuration for 128.100.100.7
+ # (expected to operate at stratum 2)
+
+ server 128.4.1.1 # rackety.udel.edu
+ server 128.8.10.1 # umd1.umd.edu
+ server 192.35.82.50 # lilben.tn.cornell.edu
+ driftfile /etc/ntp.drift
+
+This particular host is expected to operate as a client at stratum 2 by
+virtue of the "server" keyward and the fact that two of the three
+servers declared (the first two, actually) have radio clocks and usually
+run at stratum 1. The third server in the list has no radio clock, but
+is known to maintain associations with a number of stratum 1 peers and
+usually operates at stratum 2. Of particular importance with the last
+host is that it maintains associations with peers besides the two
+stratum 1 peers mentioned. This can be verified using the ntpq program
+included in the xntp3 distribution. When configured using the "server"
+keyword, this host can receive synchronization from any of the listed
+servers, but can never provide synchronization to them.
+
+Unless restricted using facilities described later, this host can
+provide synchronization to dependent clients, which do not have to be
+listed in the configuration file. Associations maintained for these
+clients are transitory and result in no persistent state in the host.
+These clients are normally not visible using the ntpq program included
+in the xntp3 distribution; however, xntpd includes a monitoring feature
+(described later) which caches a minimal amount of client information
+useful for debugging administrative purposes.
+
+A time server expected to both receive synchronization from another
+server, as well as to provide synchronization to it, is delared using
+the "peer" keyword instead of the "server" keyword. In all other aspects
+the server operates the same in either mode and can provide
+synchronization to dependent clients or other peers. It is considered
+good engineering practice to declare time servers outside the
+administrative domain as "peer" and those inside as "server" in order to
+provide redundancy in the global Internet, while minimizing the
+possibility of instability within the domain itself. A time server in
+one domain can in principle heal another domain temporarily isolated
+from all other sources of synchronization. However, it is probably
+unwise for a casual workstation to bridge fragments of the local domain
+which have become temporarily isolated.
+
+Note the inclusion of a "driftfile" declaration. One of the things the
+NTP daemon does when it is first started is to compute the error in the
+intrinsic frequency of the clock on the computer it is running on. It
+usually takes about a day or so after the daemon is started to compute a
+good estimate of this (and it needs a good estimate to synchronize
+closely to its server). Once the initial value is computed, it will
+change only by relatively small amounts during the course of continued
+operation. The "driftfile" declaration indicates to the daemon the name
+of a file where it may store the current value of the frequency error so
+that, if the daemon is stopped and restarted, it can reinitialize itself
+to the previous estimate and avoid the day's worth of time it will take
+to recompute the frequency estimate. Since this is a desireable feature,
+a "driftfile" declaration should always be included in the configuration
+file.
+
+An implication in the above is that, should xntpd be stopped for some
+reason, the local platform time will diverge from UTC by an amount that
+depends on the intrinsic error of the clock oscillator and the time
+since last synchronized. In view of the length of time necessary to
+refine the frequency estimate, every effort should be made to operate
+the daemon on a continuous basis and minimize the intervals when for
+some reason it is not running.
+
+Xntpd3 Versus Previous Versions
+
+There are several items of note when dealing with a mixture of xntp3 and
+and previous distributions of xntp (NTP Version 2 xntpd) and ntp3.4 (NTP
+Version 1 ntpd). The xntp3 implementation of xntpd is an NTP Version 3
+implementation. As such, by default when no additional information is
+available concerning the preferences of the peer, xntpd claims to be
+version 3 in the packets that it sends.
+
+An NTP implementation conforming to a previous version specification
+ordinarily discards packets from a later version. However, in most
+respects documented in RFC 1305, the previous version is compatible with
+the version-3 algorithms and protocol. Ntpd, while implementing most of
+the version-2 algorithms, still believes itself to be a version-1
+implementation. The sticky part here is that, when either xntpd version
+2 or ntpd version 1 receives a packet claiming to be from a version-3
+server, it discards it without further processing. Hence there is a
+danger that in some situations synchronization with previous versions
+will fail.
+
+Xntpd is aware of this problem. In particular, when xntpd is polled
+first by a host claiming to be a previous version 1 or version 2
+implementation, xntpd claims to be a version 1 or 2 implementation,
+respectively, in packets returned to the poller. This allows xntpd to
+serve previous version clients transparently. The trouble occurs when an
+previous version is to be included in an xntpd configuration file. With
+no further indication, xntpd will send packets claiming to be version 3
+when it polls. To get around this, xntpd allows a qualifier to be added
+to configuration entries to indicate which version to use when polling.
+Hence the entry
+
+ # specify NTP version 1
+
+ peer 130.43.2.2 version 1 # apple.com (running ntpd version 1)
+ peer 130.43.2.2 version 2 # apple.com (running xntpd version 2)
+
+will cause version 1 packets to be sent to the host address 130.43.2.2.
+If you are testing xntpd against previous version servers you will need
+to be careful about this. Note that, as indicated in the RFC 1305
+specification, there is no longer support for the original NTP
+specification, popularly called NTP Version 0.
+
+There are a few other items to watch when converting an ntpd
+configuration file for use with xntpd. The first is to reconsider the
+precision entry from the configuration file, if there is one. There was
+a time when the precision claimed by a server was mostly commentary,
+with no particularly useful purpose. This is no longer the case,
+however, and so changing the precision a server claims should only be
+done with some consideration as to how this alters the performance of
+the server. The default precision claimed by xntpd will be right for
+most situations. A section later on will deal with when and how it is
+appropriate to change a server's precision without doing things you
+don't intend.
+
+Second, note that in the example configuration file above numeric
+addresses are used in the peer and server declarations. It is also
+possible to use names requiring resolution instead, but only if some
+additional configuration is done (xntpd doesn't include the resolver
+routines itself, and requires that a second program be used to do name
+resolution). If you find numeric addresses offensive, see below.
+
+Finally, "passive" and "client" entries in an ntpd configuration file
+have no useful equivalent semantics for xntpd and should be deleted.
+Xntpd won't reset the kernel variable tickadj when it starts, so you can
+remove anything dealing with this in the configuration file. The
+configuration of radio clock peers is done using different language in
+xntpd configuration files, so you will need to delete these entries from
+your ntpd configuration file and see below for the equivalent language.
+
+Traffic Monitoring
+
+Xntpd handles peers whose stratum is higher than the stratum of the
+local server and pollers using client mode by a fast path which
+minimizes the work done in responding to their polls, and normally
+retains no memory of these pollers. Sometimes, however, it is
+interesting to be able to determine who is polling the server, and how
+often, as well as who has been sending other types of queries to the
+server.
+
+To allow this, xntpd implements a traffic monitoring facility which
+records the source address and a minimal amount of other information
+from each packet which is received by the server. This can be enabled by
+adding the following line to the server's configuration file:
+
+ # enable monitoring feature
+
+ monitor yes
+
+The recorded information can be displayed using the xntpdc query
+program, described briefly below.
+
+Address-and-Mask Restrictions
+
+The address-and-mask configuration facility supported by xntpd is quite
+flexible and general, but is not an integral part of the NTP Version 3
+specification. The major drawback is that, while the internal
+implementation is very nice, the user interface sucks. For this reason
+it is probably worth doing an example here. Briefly, the facility works
+as follows. There is an internal list, each entry of which holds an
+address, a mask and a set of flags. On receipt of a packet, the source
+address of the packet is compared to each entry in the list, with a
+match being posted when the following is true:
+
+ (source_addr & mask) == (address & mask)
+
+A particular source address may match several list entries. In this case
+the entry with the most one bits in the mask is chosen. The flags
+associated with this entry are used to control the access.
+
+In the current implementation the flags always add restrictions. In
+effect, an entry with no flags set leaves matching hosts unrestricted.
+An entry can be added to the internal list using a "restrict"
+declaration. The flags associated with the entry are specified
+textually. For example, the "notrust" flag indicates that hosts matching
+this entry, while treated normally in other respects, shouldn't be
+trusted to provide synchronization even if otherwise so enabled. The
+"nomodify" flag indicates that hosts matching this entry should not be
+allowed to do run time configuration. There are many more flags, see the
+xntpd.8 man page.
+
+Now the example. Suppose you are running the server on a host whose
+address is 128.100.100.7. You would like to ensure that run time
+reconfiguration requests can only be made from the local host and that
+the server only ever synchronizes to one of a pair of off-campus servers
+or, failing that, a time source on net 128.100. The following entries in
+the configuration file would implement this policy:
+
+ # by default, don't trust and don't allow modifications
+
+ restrict default notrust nomodify
+
+ # these guys are trusted for time, but no modifications allowed
+
+ restrict 128.100.0.0 mask 255.255.0.0 nomodify
+ restrict 128.8.10.1 nomodify
+ restrict 192.35.82.50 nomodify
+
+ # the local addresses are unrestricted
+
+ restrict 128.100.100.7
+ restrict 127.0.0.1
+
+The first entry is the default entry, which all hosts match and hence
+which provides the default set of flags. The next three entries indicate
+that matching hosts will only have the nomodify flag set and hence will
+be trusted for time. If the mask isn't specified in the restrict
+keyward, it defaults to 255.255.255.255. Note that the address
+128.100.100.7 matches three entries in the table, the default entry
+(mask 0.0.0.0), the entry for net 128.100 (mask 255.255.0.0) and the
+entry for the host itself (mask 255.255.255.255). As expected, the flags
+for the host are derived from the last entry since the mask has the most
+bits set.
+
+The only other thing worth mentioning is that the restrict declarations
+apply to packets from all hosts, including those that are configured
+elsewhere in the configuration file and even including your clock
+pseudopeer(s), in any. Hence, if you specify a default set of
+restrictions which you don't wish to be applied to your configured
+peers, you must remove those restrictions for the configured peers with
+additional restrict declarations mentioning each peer separately.
+
+Authentication
+
+Xntpd supports the optional authentication procedure specified in the
+NTP Version 2 and 3 specifications. Briefly, when an association runs in
+authenticated mode, each packet transmitted has appended to it a 32-bit
+key ID and a 64-bit crypto checksum of the contents of the packet
+computed using either the Data Encryption Standard (DES) or Message
+Digest (MD5) algorithms. Note that while either of these algorithms
+provide sufficient protection from message-modification attacks,
+distribution of the former algorithm implementation is restricted to the
+U.S. and Canada, while the latter presently is free from such
+restrictions. With either algorithm the receiving peer recomputes the
+checksum and compares it with the one included in the packet. For this
+to work, the peers must share at least one encryption key and,
+furthermore, must associate the shared key with the same key ID.
+
+This facility requires some minor modifications to the basic packet
+processing procedures, as required by the specification. These
+modifications are enabled by the "authenticate" configuration
+declaration. In particular, in authenticated mode, peers which send
+unauthenticated packets, peers which send authenticated packets which
+the local server is unable to decrypt and peers which send authenticated
+packets encrypted using a key we don't trust are all marked
+untrustworthy and unsuitable for synchronization. Note that, while the
+server may know many keys (identified by many key IDs), it is possible
+to declare only a subset of these as trusted. This allows the server to
+share keys with a client which requires authenticated time and which
+trusts the server but which is not trusted by the server. Also, some
+additional configuration language is required to specify the key ID to
+be used to authenticate each configured peer association. Hence, for a
+server running in authenticated mode, the configuration file might look
+similar to the following:
+
+ # peer configuration for 128.100.100.7
+ # (expected to operate at stratum 2)
+ # fully authenticated this time
+
+ peer 128.100.49.105 key 22 # suzuki.ccie.utoronto.ca
+ peer 128.8.10.1 key 4 # umd1.umd.edu
+ peer 192.35.82.50 key 6 # lilben.tn.cornell.edu
+ authenticate yes # enable authentication
+ keys /usr/local/bin/ntp.keys # path for key file
+ trustedkey 1 2 14 15 # define trusted keys
+ requestkey 15 # key (7) for accessing server variables
+ controlkey 15 # key (6) for accessing server variables
+
+ #authdelay 0.000047 # authentication delay (Sun4c/50 IPX DES)
+ authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
+
+There are a couple of previously unmentioned things in here. The
+"authenticate yes" line enables authentication processing, while the
+"keys /usr/local/bin/ntp.keys" specifies the path to the keys file (see
+below and the xntpd.8 man page for detaiils of the file format). The
+"trustedkey" declaration identifies those keys that are known to be
+uncompromised; the remainder presumably represent the expired or
+possibly compromised keys. Both sets of keys must be declared by key
+identifier in the ntp.keys file described below. This provides a way to
+retire old keys while minimrequestkey 15izing the frequency of delicate
+key-distribution procedures. The "requestkey 15" line establishes the
+key to be used for mode-6 control messages as specified in RFC 1305 and
+used by the ntpq utility program, while the "controlkey 15" establishes
+the key to be used for mode-7 private control messages used by the
+xntpdc utility program these keys are used to prevent unauthorized
+modification of daemon variables.
+
+The "authdelay" declaration is an estimate of the amount of processing
+time taken between the freezing of a transmit timestamp and the actual
+transmission of the packet when authentication is enabled (i.e. more or
+less the time it takes for the DES or MD5 routine to encrypt a single
+block), and is used as a correction for the transmit timestamp. This can
+be computed for your CPU by the authspeed program included in the
+authstuff directory in the xntp3 distribution. The usage is illustrated
+to the following:
+
+ # for DES keys
+
+ authspeed -n 30000 auth.samplekeys
+
+ # for MD5 keys
+
+ authspeed -nd 30000 auth.samplekeys
+
+Additional utility programs included in the authstuff directory can be
+used to generate random keys, certify implementation correctness and
+display sample keys. As a general rule, keys should be chosen randomly,
+except possibly the request and control keys, which must be entered by
+the user as a password.
+
+The ntp.keys file contains the list of keys and associated key IDs the
+server knows about (for obvious reasons this file is better left
+unreadable by anyone except the server). The contents of this file might
+look like:
+
+ # ntp keys file (ntp.keys)
+
+ 1 N 29233E0461ECD6AE # des key in NTP format
+ 2 M RIrop8KPPvQvYotM # md5 key as an ASCII random string
+ 14 M sundial # md5 key as an ASCII string
+ 15 A sundial # des key as an ASCII string
+
+ # the following 3 keys are identical
+
+ 10 A SeCReT
+ 10 N d3e54352e5548080
+ 10 S a7cb86a4cba80101
+
+In the keys file the first token on each line indicates the key ID, the
+second token the format of the key and the third the key itself. There
+are four key formats. An "A" indicates a DES key written as a 1-to-8
+character string in 7-bit ASCII representation, with each character
+standing for a key octet (like a Unix password). An "S" indicates a DES
+key written as a hex number in the DES standard format, with the low
+order bit (LSB) of each octet being the (odd) parity bit. An "N"
+indicates a DES key again written as a hex number, but in NTP standard
+format with the high order bit of each octet being the (odd) parity bit
+(confusing enough?). An "M" indicates an MD5 key written as a 1-to-31
+character ASCII string in the "A" format. Note that, because of the
+simple tokenizing routine, the characters ' ', '#', '\t', '\n' and '\0'
+can't be used in either a DES or MD5 ASCII key. Everything else is fair
+game, though. Key 0 (zero) is used for special purposes and should not
+appear in this file.
+
+The big trouble with the authentication facility is the keys file. It is
+a maintenance headache and a security problem. This should be fixed some
+day. Presumably, this whole bag of worms goes away if/when a generic
+security regime for the Internet is established.
+
+Query Programs
+
+Three utility query programs are included with the xntp3 distribution,
+ntpq, ntptrace and xntpdc. Ntpq is a rather handy program which sends
+queries and receives responses using NTP standard mode-6 control
+messages. Since it uses the standard control protocol specified in RFC
+1305, it may be used with NTP Version 2 and Version 3 implementations
+for both Unix and Fuzzball, but not Version 1 implementations. It is
+most useful to query remote NTP implementations to assess timekeeping
+accuracy and expose bugs in configuration or operation.
+
+Ntptrace can be used to display the current synchronization path from a
+selected host through possibly intervening servers to the primary source
+of synchronization, usually a radio clock. It works with both version 2
+and version 3 servers, but not version 1.
+
+Xnptdc is a horrid program which uses NTP private mode-7 control
+messages to query local or remote servers. The format and and contents
+of these messages are specific to xntpd. The program does allow
+inspection of a wide variety of internal counters and other state data,
+and hence does make a pretty good debugging tool, even if it is
+frustrating to use. The other thing of note about xntpdc is that it
+provides a user interface to the run time reconfiguration facility.
+
+See the respective man pages for details on the use of these programs.
+The primary reason for mentioning them here is to point out an
+inconsistancy which can be awfully annoying if it catches you, and which
+is worth keeping firmly in mind. Both xntpdc and xntpd demand that
+anything which has dimensions of time be specified in units of seconds,
+both in the configuration file and when doing run time reconfiguration.
+Both programs also print the values in seconds. Ntpq on the other hand,
+obeys the standard by printing all time values in milliseconds. This
+makes the process of looking at values with ntpq and then changing them
+in the configuration file or with xntpdc very prone to errors (by three
+orders of magnitude). I wish this problem didn't exist, but xntpd and
+its love of seconds predate the mode-6 protocol and the latter's
+(Fuzzball-inspired) millisecond orientation, making the inconsistancy
+irresolvable without considerable work.
+
+Run Time Reconfiguration
+
+Xntpd was written specifically to allow its configuration to be fully
+modifiable at run time. Indeed, the only way to configure the server is
+at run time. The configuration file is read only after the rest of the
+server has been initialized into a running, but default unconfigured,
+state. This facility was included not so much for the benefit of Unix,
+where it is handy but not strictly essential, but rather for dedicated
+platforms where the feature is more important for maintenance.
+Nevertheless, run time configuration works very nicely for Unix servers
+as well.
+
+Nearly all of the things it is possible to configure in the
+configuration file may be altered via NTP mode-7 messages using the
+xntpdc program. Mode-6 messages may also provide some limited
+configuration functionality (though the only thing you can currently do
+with mode-6 messages is set the leap-second warning bits) and the ntpq
+program provides generic support for the latter. The leap bits that can be
+set in the leap_warning variable (up to one month ahead) and in the
+leap_indication variable have a slightly different encoding than the
+usual interpretation:
+
+ Value Action
+ 00 The daemon passes the leap bits of its
+ synchronisation source (usual mode of operation)
+ 01/10 A leap second is added/deleted
+ 11 Leap information from the sychronisation source
+ is ignored (thus LEAP_NOWARNING is passed on)
+
+Mode-6 and mode-7 messages which would modify the configuration of the
+server are required to be authenticated using standard NTP
+authentication. To enable the facilities one must, in addition to
+specifying the location of a keys file, indicate in the configuration
+file the key IDs to be used for authenticating reconfiguration commands.
+Hence the following fragment might be added to a configuration file to
+enable the mode-6 (ntpq) and mode-7 (xntpdc) facilities in the daemon:
+
+ # specify mode-6 and mode-7 trusted keys
+
+ requestkey 65535 # for mode-7 requests
+ controlkey 65534 # for mode-6 requests
+
+If the "requestkey" and/or the "controlkey" configuration declarations
+are omitted from the configuration file, the corresponding run time
+reconfiguration facility is disabled.
+
+The query programs require the user to specify a key ID and a key to use
+for authenticating requests to be sent. The key ID provided should be
+the same as the one mentioned in the configuration file, while the key
+should match that corresponding to the key ID in the keys file. As the
+query programs prompt for the key as a password, it is useful to make
+the request and control authentication keys typable (in ASCII format)
+from the keyboard.
+
+Name Resolution
+
+Xntpd includes the cability to specify host names requiring resolution
+in "peer" and "server" declarations in the configuration file. There are
+several reasons why this was not permitted in the past. Chief among
+these is the fact that name service is unreliable and the interface to
+the Unix resolver routines is synchronous. The hangups and delays
+resulting from name-resolver clanking can be unacceptable once the NTP
+server is running (and remember it is up and running before the
+configuration file is read). However, it is advantageous to resolve time
+server names, since their addresses are occasionally changed.
+
+Instead of running the resolver itself the daemon can defer this task to
+a separate program, xntpres. When the daemon comes across a "peer" or
+"server" entry with a non-numeric host address it records the relevant
+information in a temporary file and continues on. When the end of the
+configuration file has been reached and one or more entries requiring
+name resolution have been found, the server runs an instance of xntpres
+with the temporary file as an argument. The server then continues on
+normally but with the offending peers/servers omitted from its
+configuration.
+
+When xntpres successfully resolves a name from this file, it configures
+the associated entry into the server using the same mode-7 run time
+reconfiguration facility that xntpdc uses. If temporary resolver
+failures occur, xntpres will periodically retry the offending requests
+until a definite response is received. The program will continue to run
+until all entries have been resolved.
+There are several configuration requirements if xntpres is to be used.
+The path to the xntpres program must be made known to the daemon via a
+"resolver" configuration entry, and mode-7 run time reconfiguration must
+be enabled. The following fragment might be used to accomplish this:
+
+ # specify host name resolver data
+
+ resolver /local/etc/xntpres
+ keys /etc/ntp.keys
+ requestkey 65535
+
+Note that xntpres sends packets to the server with a source address of
+127.0.0.1. You should obviously avoid "restrict" modification requests
+from this address or xntpres will fail.
+
+Dealing with Frequency Tolerance Violations (Tickadj and Friends)
+
+The NTP Version 3 specification RFC 1305 calls for a maximum oscillator
+frequency tolerance of +-100 parts-per-million (ppm), which is
+representative of those components suitable for use in relatively
+inexpensive workstation platforms. For those platforms meeting this
+tolerance, NTP will automatically compensate for the frequency errors of
+the individual oscillator and no further adjustments are required,
+either to the configuration file or to various kernel variables.
+
+However, in the case of certain notorious platforms, in particular Sun
+4s, the 100-ppm tolerance is routinely violated. In such cases it may be
+necessary to adjust the values of certain kernel variables; in
+particular, "tick" and "tickadj". The variable tick is the increment in
+microseconds added to the system time on each interval-timer interrupt,
+while the variable tickadj is used by the time adjustment code as a slew
+rate. When the time is being adjusted via a call to the system routine
+adjtime(), the kernel increases or reduces tick by tickadj microseconds
+until the specified adjustment has been completed. Unfortunately, in
+most Unix implementations the tick increment must be either zero or
+plus/minus exactly tickadj microseconds, meaning that adjustments are
+truncated to be an integral multiple of tickadj (this latter behaviour
+is a misfeature, and is the only reason the xntpd code needs to concern
+itself with the internal implementation of adjtime() at all). In
+addition, the stock Unix implementation considers it an error to request
+another adjustment before a prior one has completed.
+
+Thus, to make very sure it avoids problems related to the roundoff, the
+xntpd daemon reads the values of tick and tickadj from /dev/kmem when it
+starts. It then ensures that all adjustments given to adjtime() are an
+even multiple of tickadj microseconds and computes the largest
+adjustment that can be completed in the adjustment interval (using both
+the value of tickadj and the value of tick) so it can avoid exceeding
+this limit.
+
+Unfortunately, the value of tickadj set by default is almost always too
+large for xntpd. NTP operates by continuously making small adjustments
+to the clock, usually at one-second intervals. If tickadj is set too
+large, the adjustments will disappear in the roundoff; while, if tickadj
+is too small, NTP will have difficulty if it needs to make an occasional
+large adjustment. While the daemon itself will read the kernel's values
+of tick and tickadj, it will not change the values, even if they are
+unsuitable. You must do this yourself before the daemon is started,
+either with adb or, in the running kernel only, with the tickadj program
+included in the util directory of the xntp3 distribution. Note that the
+latter program will also computes an optimal value of tickadj for NTP
+use based on the kernel's value of tick.
+
+The tickadj program can reset several other kernel variables if asked.
+It can also change the value of tick if asked, this being necessary on a
+few machines with very broken clocks, like Sun 4s. With these machines
+it should also set the value of the kernel dosynctodr variable to zero.
+This variable controls whether to synchronize the system clock to the
+time-of-day clock, something you really don't want to be happen when
+xntpd is trying to keep it under control.
+
+In order to maintain reasonable correctness bounds, as well as
+reasonably good accuracy with acceptable polling intervals, xntpd will
+complain if the frequency error is greater than 100 ppm. For machines
+with a value of tick in the 10-ms range, a change of one in the value of
+tick will change the frequency by about 100 ppm. In order to determine
+the value of tick for a particular CPU, disconnect the machine from all
+sources of time (dosynctodr = 0) and record its actual time compared to
+an outside source (eyeball-and-wristwatch will do) over a day or more.
+Multiply the time change over the day by 0.116 and add or subtract the
+result to tick, depending on whether the CPU is fast or slow. An example
+call to tickadj useful on Sun 4s is:
+
+ tickadj -t 9999 -a 5 -s
+
+which sets tick 100 ppm fast, tickadj to 5 microseconds and turns off
+the clock/calendar chip fiddle. This line can be added to the rc.local
+configuration file to automatically set the kernel variables at boot
+time.
+
+All this stuff about diddling kernel variables so the NTP daemon will
+work is really silly. If vendors would ship machines with clocks that
+kept reasonable time and would make their adjtime() system call apply
+the slew it is given exactly, independent of the value of tickadj, all
+this could go away.
+
+Tuning Your Subnet
+
+There are several parameters available for tuning the NTP subnet for
+maximum accuracy and minimum jitter. Two important parameters are the
+the "precision" and "prefer" configuration declarations. The precision
+declaration specifies the number of significant bits of the system clock
+representation relative to one second. For instance, the default value
+of -6 corresponds to 1/64 second or about 16 milliseconds.
+
+The NTP protocol makes use of the precision parameter in several places.
+It is included in packets sent to peers and is used by them to calculate
+the maximum absolute error and maximum statistical error. When faced
+with selecting one of several servers of the same stratum and about the
+same network path delay for synchronization purposes, clients will
+usually prefer to synchronize to those servers claiming the smallest
+(most negative) precision, since this maximizes the accuracy and
+minimizes the jitter apparent to application programs running on the
+client platform. Therefore, when the maximum attainable accuracy is
+required, it is important that every platform configure an accurate
+value for the precision variable. This can be done using the optional
+"precision" declaration in the configuration file:
+
+ # precision declaration
+
+ precision -18 # for microsecond clocks (Sun 4s, DEC 5000/240)
+
+When more than one eligible server exists, the NTP clock-selection and
+combining algorithms act to winnow out all except the "best" set of
+servers using several criteria based on differences between the readings
+of different servers and between successive readings of the same server.
+The result is usually a set of surviving servers that are apparently
+statistically equivalent in accuracy, jitter and stability. The
+population of survivors remaining in this set depends on the individual
+server characteristics measured during the selection process and may
+vary from time to time as the result of normal statistical variations.
+In LANs with high speed RISC-based time servers, the population can
+become somewhat unstable, with individual servers popping in and out of
+the surviving population, generally resulting in a regime called
+clockhopping.
+
+When only the smallest residual jitter can be tolerated, it may be
+convenient to elect one of the servers at each stratum level as the
+preferred one using the keyword "prefer" on the configuration
+declaration for the selected server:
+
+ # prefered server declaration
+
+ peer 128.4.1.1 prefer # preferred server
+
+The preferred server will always be included in the surviving
+population, regardless of its characteristics and as long as it survives
+preliminary sanity checks and validation procedures.
+
+The most useful application of the prefer keyword is in high speed LANs
+equipped with precision radio clocks, such as a GPS receiver. In order
+to insure robustness, the hosts need to include outside peers as well as
+the GPS-equipped server; however, as long as that server is running, the
+synchronization preference should be that server. The keyword should
+normally be used in all cases in order to prefer an attached radio
+clock. It is probably inadvisable to use this keyword for peers outside
+the LAN, since it interferes with the carefully crafted judgement of the
+selection and combining algorithms.
+
+Provisions for Leap Seconds and Accuracy Metrics
+
+Xntpd understands leap seconds and will attempt to take appropriate
+action when one occurs. In principle, every host running xntpd will
+insert a leap second in the local timescale in precise synchronization
+with UTC. This requires that the leap-warning bits be manually activated
+some time prior to the occurance of a leap second at the primary
+(stratum 1) servers. Subsequently, these bits are propagated throughout
+the subnet depending on these servers by the NTP protocol itself and
+automatically implemented by xntpd and the time-conversion routines of
+each host. The implementation is independent of the idiosyncracies of
+the particular radio clock, which vary widely among the various devices,
+as long as the idiosyncratic behavior does not last for more than about
+20 minutes following the leap. Provisions are included to modify the
+behavior in cases where this cannot be guaranteed.
+
+While provisions for leap seconds have been carefully crafted so that
+correct timekeeping immediately before, during and after the occurance
+of a leap second is scrupulously correct, stock Unix systems are mostly
+inept in responding to the available information. This caveat goes also
+for the maximum-error and statistical-error bounds carefully calculated
+for all clients and servers, which could be very useful for application
+programs needing to calibrate the delays and offsets to achieve a near-
+simulataneous commit procedure, for example. While this information is
+maintained in the xntpd data structures, there is at present no way for
+application programs to access it. This may be a topic for further
+development.
+
+Clock Support Overview
+
+Xntpd was designed to support radio (and other external) clocks and does
+some parts of this function with utmost care. Clocks are treated by the
+protocol as ordinary NTP peers, even to the point of referring to them
+with an (invalid) IP host address. Clock addresses are of the form
+127.127.t.u, where t specifies the particular type of clock (i.e. refers
+to a particular clock driver) and u is a unit number whose
+interpretation is clock-driver dependent. This is analogous to the use
+of major and minor device numbers by Unix and permits multiple
+instantiations of clocks of the same type on the same server, should
+such magnificant redundancy be required.
+
+Because clocks look much like peers, both configuration file syntax and
+run time reconfiguration commands can be be used to control clocks in
+the same way as ordinary peers. Clocks are configured via "server"
+declarations in the configuration file, can be started and stopped using
+xntpdc and are subject to address-and-mask restrictions much like a
+normal peer, should this stretch of imagination ever be useful. As a
+concession to the need to sometimes transmit additional information to
+clock drivers, an additional configuration file is available: the
+"fudge" statement. This enables one to specify the values two time
+quantities, two integral values and two flags, the use of which is
+dependent on the particular clock driver. For example, to configure a
+PST radio clock which can be accessed through the serial device
+/dev/pst1, with propagation delays to WWV and WWVH of 7.5 and 26.5
+milliseconds, respectively, on a machine with an imprecise system clock
+and with the driver set to disbelieve the radio clock once it has gone
+30 minutes without an update, one might use the following configuration
+file entries:
+
+ # radio clock fudge fiddles
+
+ server 127.127.3.1
+ fudge 127.127.3.1 time1 0.0075 time2 0.0265
+ fudge 127.127.3.1 value2 30 flag1 1
+
+Additional information on the interpretation of these data with respect
+to various radio clock drivers is given in the xntpd.8 man page.
+
+Towards the Ultimate Tick
+
+This section consideres issues in providing precision time
+synchronization in NTP subnets which need the highest quality time
+available in the present technology. These issues are important in
+subnets supporting real-time services such as distributed multimedia
+conferencing and wide-are experiment control and monitoring.
+
+In the Internet of today synchronization paths often span continents and
+oceans with moderate to high variations in delay due to traffic spasms.
+NTP is specifically designed to minimize timekeeping jitter due to delay
+variations using intricately crafted filtering and selection algorithms;
+however, in cases where these variations are as much as a second or
+more, the residual jitter following these algorithms may still be
+excessive. Sometimes, as in the case of some isolated NTP subnets where
+a local source of precision time is available, such as a 1-pps signal
+produced by a calibrated cesium clock, it is possible to remove the
+jitter and retime the local clock oscillator of the NTP server. This has
+turned out to be a useful feature to improve the synchronization quality
+of time distributed in remote places where radio clocks are not
+available. In these cases special features of the xntp3 distribution are
+used together with the 1-pps signal to provide a jitter-free timing
+signal, while NTP itself is used to provide the coarse timing and
+resolve the seconds numbering.
+
+Most available radio clocks can provide time to an accuracy in the order
+of milliseconds, depending on propagation conditions, local noise levels
+and so forth. However, as a practical matter, all clocks can
+occasionally display errors significantly exceeding nominal
+specifications. Usually, the algorithms used by NTP for ordinary network
+peers, as well as radio clock "peers" will detect and discard these
+errors as discrepancies between the disciplined local clock oscillator
+and the decoded time message produced by the radio clock. Some radio
+clocks can produce a special 1-pps signal which can be interfaced to the
+server platform in a number of ways and used to substantially improve
+the (disciplined) clock oscillator jitter and wander characteristics by
+at least an order of magnitude. Using these features it is possible to
+achieve accuracies in the order of 100 microseconds with a fast RISC-
+based platform.
+
+There are three ways to implement 1-pps support, depending on the radio
+clock model, platform model and serial line interface. Each of these
+requires circuitry to convert the TTL signal produced by most clocks to
+the the EIA levels used by most serial interfaces. An example of a
+device designed to do this is presented in the gadget subdirectory
+included in the xntp3 distribtuion. Besides being useful for this
+purpose, this device includes an inexpensive modem designed for use with
+the Canadian CHU time/frequency radio station.
+
+In order to select the appropriate implementation, it is important to
+understand the underlying 1-pps mechanism used by xntpd. The 1-pps
+suport depends on a continuous source of 1-pps pulses used to calculate
+an offset within +-500 milliseconds relative to the local clock. The
+serial timecode produced by the radio or the time determined by NTP in
+absence of the radio is used to adjust the local clock within +-128
+milliseconds of the actual time. As long as the local clock is within
+this interval the 1-pps support is used to discipline the local clock
+and the timecode used only to verify that the local clock is in fact
+within the interval. Outside this interval the 1-pps support is disabled
+and the timecode used directly to control the local clock.
+
+The first method of implementation uses a dedicated serial port and
+either the bsd line discipline or System V streams module, which can be
+found in the kernel directory of the xntp3 distribution. This method can
+be used with any radio clock or in the absence of any clock. The line
+discipline and streams modules take receive timestamps in the kernel,
+specifically the interrupt routine of the serial port hardware driver.
+Using this method the port is dedicated to serve the 1-pps signal and
+cannot be used for other purposes. Instructions for implementing the
+feature, which requires rebuilding the kernel, are included in the
+modules themselves. Note that xndpd must be compiled with the -DPPSDEV
+compiler switch in this case. There is an inherent error in this method
+due to the latency of the interrupt system and remaining serial-line
+protocol modules in the order of a millisecond with Sun 4s. While the
+jitter in this latency is unavoidable, the systematic component can be
+calibrated out using a special configuration declaration:
+
+ # pps delay and baud rate
+
+ pps delay .0017 baud 19200 # pps delay (ms) and baud rate
+
+Note that the delay defaults to zero and the baud to 38400.
+
+The second method uses mechanisms embedded in the radio clock driver,
+which call the 1-pps support directly and do not require a dedicated
+serial port. Currently, only the DCF77 (German radio time service)
+driver uses this method. Instructions for implementing this are given in
+README files in the xntp3 distribution.
+
+The third method and the most accurate and intrusive of all uses the
+carrier-detect modem-control lead monitored by the serial port driver.
+This method can be used with any radio clock and 1-pps interface
+mentioned above. It requires in addition to a special streams module,
+replacement of the kernel high resolution time-of-day clock routine.
+This method is applicable only to Sun 4 platforms running SunOS 4.1.1
+and then only with either of the two onboard serial ports. It does not
+work with other platforms, operating systems or external (SBus) serial
+multiplexors.
+
+Swatting Bugs
+
+Let's say you have compiled and installed the code and put up an
+apparently relevant configuration file. In many Unix systems the xntpd
+daemon and utility programs (ntpq, ntptrace and xntpdc) are usually
+installed in the /usr/local directory along with the key file
+(ntp.keys), while the configuration file (ntp.conf) and drift file
+(ntp.drift) are installed in the /etc directory. The daemon can is
+usually started from the rc.local shell script at system boot time, but
+could be started (and stopped) at other times for debugging, etc. How do
+you verify that the daemon can form associations with remote peers and
+verify correct synchronization? For this you need the ntpq utility
+described in the ntpq.8 man page.
+
+After starting the daemon, run the ntpq program using the -n switch,
+which will avoid possible distractions due to name resolutions. Use the
+peer command to display a billboard showing the status of configured
+peers and possibly other clients poking the daemon. After operating for
+a few minutes, the display should be something like:
+
+ remote refid st when poll reach delay offset disp
+========================================================================
++128.4.2.6 132.249.16.1 2 131 256 373 9.89 16.28 23.25
+*128.4.1.20 .WWVB. 1 137 256 377 280.62 21.74 20.23
+-128.8.2.88 128.8.10.1 2 49 128 376 294.14 5.94 17.47
++128.4.2.17 .WWVB. 1 173 256 377 279.95 20.56 16.40
+
+The hosts shown in the "remote" column should agree with the entries in
+the configuration file, plus any peers not mentioned in the file at the
+same or lower than your stratum that happen to be configured to peer
+with you. The "refid" entry shows the current source of synchronization
+for that peer, while the "st" reveals its stratum and the "poll" entry
+the polling interval, in seconds. The "when" entry shows the time since
+the peer was last heard, in seconds, while the "reach" entry shows the
+status of the reachability register (see specification), which is in
+octal format. The remaining entries show the latest delay, offset and
+dispersion computed for the peer, in milliseconds.
+
+*** This section incomplete. Soon.
+
+status=0664 leap_none, sync_ntp, 6 events, event_peer/strat_chg
+system="UNIX", leap=00, stratum=2, rootdelay=280.62,
+rootdispersion=45.26, peer=11673, refid=128.4.1.20,
+reftime=af00bb42.56111000 Fri, Jan 15 1993 4:25:38.336, poll=8,
+clock=af00bbcd.8a5de000 Fri, Jan 15 1993 4:27:57.540, phase=21.147,
+freq=13319.46, compliance=2
+
+status=7414 reach, auth, sel_sync, 1 event, event_reach
+srcadr=128.4.2.6, srcport=123, dstadr=128.4.2.7, dstport=123, keyid=1,
+stratum=2, precision=-10, rootdelay=362.00, rootdispersion=21.99,
+refid=132.249.16.1,
+reftime=af00bb44.849b0000 Fri, Jan 15 1993 4:25:40.517,
+delay= 9.89, offset= 16.28, dispersion=23.25, reach=373, valid=8,
+hmode=2, pmode=1, hpoll=8, ppoll=10, leap=00, flash=0x0,
+org=af00bb48.31a90000 Fri, Jan 15 1993 4:25:44.193,
+rec=af00bb48.305e3000 Fri, Jan 15 1993 4:25:44.188,
+xmt=af00bb1e.16689000 Fri, Jan 15 1993 4:25:02.087,
+filtdelay= 16.40 9.89 140.08 9.63 9.72 9.22 10.79 122.99,
+filtoffset= 13.24 16.28 -49.19 16.04 16.83 16.49 16.95 -39.43,
+filterror= 16.27 20.17 27.98 31.89 35.80 39.70 43.61 47.52
+
+ind assID status conf reach auth condition last_event cnt
+===========================================================
+ 1 11670 7414 no yes ok synchr. reachable 1
+ 2 11673 7614 no yes ok sys.peer reachable 1
+ 3 11833 7314 no yes ok outlyer reachable 1
+ 4 11868 7414 no yes ok synchr. reachable 1
+
+Parting Shots
+
+There are several undocumented programs which are useful if you are
+trying to set up a clock. They can be found in the clockstuff directory
+of the xntp3 distribution. The most useful of these is the propdelay
+program, which can compute high frequency radio propagation delays
+between any two points whose latitude and longitude are known. The
+program understands something about the phenomena which allow high
+frequency radio propagation to occur, and will generally provide a
+better estimate than a calculation based on the great circle distance.
+The other two programs in the directory are clktest, which allows one to
+exercise the generic clock line discipline, and chutest, which runs the
+basic reduction algorithms used by the daemon on data received from a
+serial port.
diff --git a/usr.sbin/xntpd/doc/ntpdate.8 b/usr.sbin/xntpd/doc/ntpdate.8
new file mode 100644
index 0000000..bf6832d
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntpdate.8
@@ -0,0 +1,142 @@
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 1993
+.Dt NTPDATE 8
+.Os
+.Sh NAME
+.Nm ntpdate
+.Nd set the date and time via NTP
+.Sh SYNOPSIS
+.Nm
+.Op Fl bds
+.Op Fl o Ar version
+.Op Fl a Ar key#
+.Op Fl e Ar authdelay
+.Op Fl k Ar keyfile
+.Op Fl p Ar samples
+.Op Fl t Ar timeout
+.Ar server ...
+.Sh DESCRIPTION
+.Nm Ntpdate
+sets the local date and time by polling the Network Time Protocol
+server(s) on the host(s) given as 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 the
+standard NTP clock filter and selection algorithms are applied to select
+the best of these. Typically,
+.Nm
+can be inserted in the
+.Pa /etc/rc.local
+startup up script to set the time of day at boot time and/or can be run
+from time\-to\-time via
+.Xr cron 8 .
+Note that
+.Nm Ns 's
+reliability and precision will improve dramatically with greater numbers
+of servers. While a single server may be used, better performance and
+greater resistance to insanity on the part of any one server
+will be obtained by providing at least three or four servers, if not more.
+.Pp
+Time adjustments are made by
+.Nm
+in one of two ways. If
+.Nm
+determines your clock is off by more than 0.5 seconds it will simply
+step the time by calling
+.Xr settimeofday 2 .
+If the error is less than 0.5 seconds, however, it will by default slew
+the clock's time via a call to
+.Xr adjtime 2
+with the offset. The latter technique is less disruptive and more
+accurate when the offset is small, and works quite well when
+.Nm
+is run by
+.Xr cron 8
+every hour or two. The adjustment made in the latter
+case is actually 50% larger than the measured offset since this will
+tend to keep a badly drifting clock more accurate (at some expense to
+stability, though this tradeoff is usually advantageous). At boot time,
+however, it is usually better to always step the time. This can be forced
+in all cases by specifying the
+.Fl b
+switch on the command line. The
+.Fl s
+switch tells
+.Nm
+to log its actions via the
+.Xr syslog 3
+facility rather than to the standard output, a useful option when
+running the program from
+.Xr cron 8 .
+.Pp
+The
+.Fl d
+flag may be used to determine what
+.Nm
+will do without it actually doing it. Information useful for general
+debugging will also be printed. By default
+.Nm
+claims to be an NTP version 3 implementation in its outgoing packets. As
+some older software will decline to respond to version 3 queries, the
+.Fl o Ar version
+switch can be used to force the program to poll as a version 2 or 1
+implementation instead.
+.Pp
+The number of samples
+.Nm
+acquires from each server can be set to between 1 and 8 inclusive
+using the
+.Fl p
+switch. The default is 4. The time it will spend waiting for a
+response can be set using the
+.Fl t
+switch, and will be rounded to a multiple of 0.2 seconds. The default
+is 1 second, a value suitable for polling across a LAN.
+.Pp
+.Nm Ntpdate
+will authenticate its transactions if need be. The
+.Fl a
+switch specifies that all packets should be authenticated using the
+key number indicated. The
+.Fl k
+switch allows the name of the file from which the keys may be read
+to be modified from the default of
+.Pa /etc/ntp.keys .
+This file should be in the format described in
+.Xr xntpd 8 .
+The
+.Fl e
+option allows the specification of an authentication processing delay,
+in seconds (see
+.Xr xntpd 8
+for details). This number is usually small enough to be negligible for
+.Nm Ns 's
+purposes, though specifying a value may improve timekeeping on very slow
+CPU's.
+.Pp
+.Nm Ntpdate
+will decline to set the date if an NTP server daemon (e.g.
+.Xr xntpd 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.
+.Sh FILES
+.Bl -tag -width /etc/ntp.keys -compact
+.It Pa /etc/ntp.keys
+contains the encryption keys used by
+.Nm Ns .
+.El
+.Sh SEE ALSO
+.Xr xntpd 8
+.Sh HISTORY
+Written by
+.An Dennis Ferguson
+at the University of Toronto
+.Sh BUGS
+The technique used for improving accuracy by compensating for clock
+oscillator errors sucks, but doing better would require the program
+to save state from previous runs.
diff --git a/usr.sbin/xntpd/doc/ntpq.8 b/usr.sbin/xntpd/doc/ntpq.8
new file mode 100644
index 0000000..e2290c3
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntpq.8
@@ -0,0 +1,480 @@
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 1993
+.Dt NTPQ 8
+.Os
+.Sh NAME
+.Nm ntpq
+.Nd standard Network Time Protocol query program
+.Sh SYNOPSIS
+.Nm
+.Op Fl inp
+.Op Fl c Ar command
+.Op Ar host ...
+.Sh DESCRIPTION
+.Nm Ntpq
+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.
+.Nm Ntpq
+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
+.Ar 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
+.Ar localhost
+when no other host is specified.
+.Nm Ntpq
+will prompt for commands if the standard input is a terminal device.
+.Pp
+.Nm Ntpq
+uses NTP mode 6 packets to communicate with the NTP server, and hence
+can be used to query any compatable 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.
+.Nm Ntpq
+makes one attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable time out time.
+.Pp
+Command line options are described following. 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.
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar command
+The following argument is interpreted as an interactive format
+.Ar 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
+.Em peers
+interactive command.
+.El
+.Sh INTERNAL COMMANDS
+.Pp
+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
+.Qq > ,
+followed by a file name, to the command line.
+.Pp
+A number of interactive format commands are executed entirely within the
+.Nm
+program itself and do not result in NTP mode 6 requests being sent to a
+server. These are described following.
+.Pp
+.Bl -tag -width indent
+.It ? Op Ar command_keyword
+A
+.Qq ?
+by itself will print a list of all the command keywords
+known to this incarnation of
+.Nm Ns .
+A
+.Qq ?
+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 timeout Ar milliseconds
+Specify a time out period for responses to server queries. The default
+is about 5000 milliseconds. Note that since
+.Nm
+retries each query once after a time out the total waiting time for a
+time out will be twice the time out value set.
+.It 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 time
+stamps in authenticated requests, so this command may be obsolete.
+.It host Ar hostname
+Set the host to which future queries will be sent.
+.Ar Hostname
+may be either a host name or a numeric
+address.
+.It Xo poll
+.Op Ar #
+.Op Ar verbose
+.Xc
+Poll the current server in client mode. The first argument is the number
+of times to poll (default is 1) while the second argument may be given
+to obtain a more detailed output of the results. This command is
+currently just wishful thinking.
+.It keyid Ar #
+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 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 hostnames Ar yes | Ar no
+If
+.Ar yes
+is specified, host names are printed in information
+displays. If
+.Ar no
+is given, numeric addresses are printed
+instead. The default is
+.Ar yes
+unless modified using the command line
+.Fl n
+switch.
+.It raw
+Cause all output from query commands is printed as received from the
+remote server. The only formating/intepretation done on the data is to
+transform nonascii data into a printable (but barely understandable)
+form.
+.It cooked
+Cause output from query commands to be
+.Qq cooked Ns .
+Variables
+which are recognized by the server will have their values reformatted
+for human consumption. Variables which
+.Nm
+thinks should have a decodeable value but didn't are marked with a
+trailing
+.Qq ? Ns .
+.It ntpversion Ar 1 | Ar 2 | Ar 3
+Set 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 authenticate Ar yes | Ar no
+Normally
+.Nm
+does not authenticate requests unless they are write requests. The
+command
+.Em 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 peer display.
+.It Xo addvars
+.Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...
+.Xc
+.It Xo rmvars
+.Aq variable_name Ns
+.Op ,...
+.Xc
+.It clearvars
+The data carried by NTP mode 6 messages consists of a list of items of
+the form
+.Xo Aq variable_name Ns
+.Pf = Aq value
+.Xc
+where the
+.Qq = Ns Aq value
+is ignored, and can be omitted, in requests
+to the server to read variables.
+.Nm Ntpq
+maintains an internal list in which data to be included in control
+messages can be assembled, and sent using the
+.Em readlist
+and
+.Em writelist
+commands described below. The
+.Em 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
+.Em rmvars
+command can be used to remove individual variables from the list, while
+the
+.Em clearlist
+command removes all variables from the list.
+.It debug Ar more | Ar less | Ar off
+Turn internal query program debugging on and off.
+.It quit
+Exit
+.Nm Ns .
+.El
+.Sh CONTROL MESSAGE COMMANDS
+Each peer known to an NTP server has a 16 bit integer
+.Em 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
+.Em peers
+command, which will send a preprogrammed series of messages to obtain
+the data it needs, and the
+.Em mreadlist
+and
+.Em mreadvar
+commands, which will iterate over a range of associations.
+.Bl -tag -width indent
+.It associations
+Obtain and print 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. Note that the data returned by the \*(L"associations\*(R"
+command is cached internally in
+.Nm Ns .
+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
+.Em &index
+may be used as an alternative.
+.It lassocations
+Obtain and print a list of association identifiers and peer statuses
+for all associations for which the server is maintaining state. This
+command differs from the
+.Em 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
+.Em associations
+command is used, but are included in the
+output of
+.Em lassociations Ns .
+.It passociations
+Print association data concerning in\-spec peers from the internally
+cached list of associations. This command performs identically to the
+.Em associations
+except that it displays the internally stored
+data rather than making a new query.
+.It lpassociations
+Print data for all associations, including out\-of\-spec client
+associations, from the internally cached list of associations. This
+command differs from
+.Em passociations
+only when dealing with fuzzballs.
+.It pstatus Ar assocID
+Send 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 pidgin English.
+.It Xo readvar
+.Op Ar assocID Ns
+.Pf [ Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+Request 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 rv
+.Op Ar assocID Ns
+.Pf [ Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+An easy\-to\-type short form for the
+.Em readvar
+command.
+.It Xo writevar
+.Ar assocID
+.Aq variable_name Ns
+.Pf = Ns Aq value Ns
+.Op ,...
+.Xc
+Like the
+.Em readvar
+request, except the specified variables are written instead of read.
+.It readlist Op Ar assocID
+Request 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 rl Op Ar assocID
+An easy\-to\-type short form of the
+.Em readlist
+command.
+.It writelist Op Ar assocID
+Like the
+.Em readlist
+request, except the internal list variables are written instead of read.
+.It Xo mreadvar
+.Ar assocID assocID [
+.Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+Like the
+.Em 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
+.Em associations
+command.
+.It Xo mrv
+.Ar assocID assocID [
+.Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+An easy\-to\-type short form of the
+.Em mreadvar
+command.
+.It mreadlist Ar assocID assocID
+Like the
+.Em 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
+.Em associations
+command.
+.It mrl Ar assocID assocID
+An easy\-to\-type short form of the
+.Em mreadlist
+command.
+.It Xo clockvar
+.Op Ar assocID Ns
+.Pf [ Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+Request 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
+.Qq 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 Xo cv
+.Op Ar assocID Ns
+.Pf [ Aq variable_name Ns
+.Op = Ns Aq value Ns
+.Op ,...]
+.Xc
+An easy\-to\-type short form of the
+.Em clockvar
+command.
+.It peers
+Obtain a list of in\-spec 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 the refID 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
+seconds.
+.Pp
+The character in the left margin indicates the fate of this peer in the
+clock selection process. The codes mean: <sp> discarded due to high
+stratum and/or failed sanity checks;
+.Qq x
+designated falsticker
+by the intersection algorithm;
+.Qq .
+culled from the end of the
+candidate list;
+.Qq -
+discarded by the clustering algorithm;
+.Qq +
+included in the final selection set;
+.Qq #
+selected
+for synchronization but distance exceeds maximum;
+.Qq *
+selected
+for synchronization; and
+.Qq o
+selected for synchronization, pps
+signal in use.
+.Pp
+Note that since the
+.Em peers
+command depends on the ability to parse the values in the responses it
+gets it may fail to work from time to time with servers which poorly
+control the data formats.
+.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
+.Qq REFCLK(<implementation number>, <parameter>) .
+On
+.Qq hostnames no
+only IP\-addresses will be displayed.
+.It lpeers
+Like
+.Em 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 opeers
+An old form of the
+.Em peers
+command with the reference ID
+replaced by the local interface address.
+.El
+.Sh HISTORY
+Written by
+.An Dennis Ferguson
+at the University of Toronto.
+.Sh BUGS
+The
+.Em peers
+command is non\-atomic and may occasionally result in spurious error
+messages about invalid associations occurring and terminating the
+command.
+.Pp
+The timeout time is a fixed constant, which means you wait a long time
+for time outs since it assumes sort of a worst case. The program should
+improve the time out estimate as it sends queries to a particular host,
+but doesn't.
diff --git a/usr.sbin/xntpd/doc/ntptrace.8 b/usr.sbin/xntpd/doc/ntptrace.8
new file mode 100644
index 0000000..ab66988
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntptrace.8
@@ -0,0 +1,65 @@
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 1993
+.Dt NTPTRACE 8
+.Os
+.Sh NAME
+.Nm ntptrace
+.Nd "trace a chain of NTP hosts back to their master time source"
+.Sh SYNOPSIS
+.Nm ntptrace
+.Op Fl vdn
+.Op Fl r Ar retries
+.Op Fl t Ar timeout
+.Op Ar server
+.Sh DESCRIPTION
+.Nm Ntptrace
+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 Ns :
+.Bd -literal
+% ntptrace
+localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2.bozo.com: stratum 2, offset 0.0124263, synch distance 0.115784
+h.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's stratum,
+the time offset between that host and the local host
+(as measured by
+.Nm Ns ;
+this is why it is not always zero for
+.Dq localhost ) ,
+the host's
+.Dq synchronization distance ,
+and (only for stratum-1 servers) the reference clock ID. All times
+are given in seconds. (Synchronization distance is a measure of the
+goodness of the clock's time.)
+.Sh OPTIONS
+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 xntpd 8 ,
+.Xr xntpdc 8
+.Sh BUGS
+This program makes no attempt to improve accuracy by doing multiple
+samples.
diff --git a/usr.sbin/xntpd/doc/tickadj.8 b/usr.sbin/xntpd/doc/tickadj.8
new file mode 100644
index 0000000..e510bfd
--- /dev/null
+++ b/usr.sbin/xntpd/doc/tickadj.8
@@ -0,0 +1,124 @@
+.\" $Id: tickadj.8,v 1.5 1998/01/22 07:26:08 charnier Exp $
+.\"
+.Dd December 21, 1993
+.Dt TICKADJ 8
+.Os
+.Sh NAME
+.Nm tickadj
+.Nd fiddle time\-related variables in the kernel
+.Sh SYNOPSIS
+.Nm tickadj
+.Op Fl Adkpqs
+.Op Fl a Ar new_tickadj
+.Op Fl t Ar new_tick
+.Sh DESCRIPTION
+The
+.Nm
+program reads, and optionally modifies, several time\-keeping\-related
+variables in the running kernel, via
+.Pa /dev/kmem .
+The particular variables it is concerned with are
+.Em tick ,
+which is the number of microseconds added to the system time during a
+clock interrupt,
+.Em tickadj ,
+which sets the slew rate and resolution used by the
+.Xr adjtime 2
+system call, and
+.Em dosynctodr ,
+which indicates to the kernels on some machines whether they should internally
+adjust the system clock to keep it in line with time\-of\-day clock
+or not.
+.Pp
+By default, with no arguments,
+.Nm
+reads the variables of interest in the kernel and prints them. At the
+same time it determines an
+.Qq optimal
+value for the value of the
+.Em tickadj
+variable if the intent is to run the
+.Xr xntpd 8
+Network Time Protocol daemon, and prints this as well. Since the operation
+of
+.Nm
+when reading the kernel mimics the operation of similar parts of the
+.Xr xntpd 8
+program fairly closely, this is useful for doing debugging of problems
+with
+.Xr xntpd 8 Ns .
+.Pp
+Various flags may be specified to change the variables of interest in
+the running kernel:
+.Bl -tag -width indent
+.It Fl a Ar new_tickadj
+Allow one to set the variable
+.Em tickadj
+to the value specified as an argument.
+.It Fl A
+Cause
+.Em tickadj
+to be modified, but instead will set it to the internally computed
+.Qq optimal
+value.
+.It Fl d
+Add debugging information.
+.It Fl k
+Use
+.Pa /dev/kmem
+instead of
+.Pa /kernel .
+This is the default.
+.It Fl p
+Tell
+.Nm
+to set the value of the variable
+.Em noprintf
+to one.
+.It Fl q
+Tell
+.Nm
+to shut up about everything except errors. Normally
+.Nm
+is quite verbose about what it is doing.
+.It Fl s
+Tell
+.Nm
+to set the value of the variable
+.Em dosynctodr
+to zero, a prerequisite for running the
+.Xr xntpd 8
+daemon under SunOS 4.0.
+.It Fl t Ar new_tick
+May be used to reset the kernel's value of
+.Em tick ,
+a capability which is useful on machines with very broken clocks.
+.El
+.Pp
+Note that
+.Nm
+should be run with some caution when being used for the first time on
+different types of machines. The operations which
+.Nm
+trys to perform are not guaranteed to work on all Unix machines.
+.Sh FILES
+.Bl -tag -width /dev/kmem -compact
+.It Pa /kernel
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr xntpd 8
+.Sh HISTORY
+Written by
+.An Dennis Ferguson
+at the University of Toronto.
+.Sh BUGS
+Fiddling with kernel variables at run time as a part of ordinary
+operations is a hideous practice which is only necessary to make
+up for deficiencies in the implementation of
+.Xr adjtime 2
+in many kernels and/or brokenness of the system clock in some
+vendors' kernels. It would be much better if the kernels were fixed
+and the
+.Nm
+program went away.
diff --git a/usr.sbin/xntpd/doc/xntpd.8 b/usr.sbin/xntpd/doc/xntpd.8
new file mode 100644
index 0000000..f1096bc
--- /dev/null
+++ b/usr.sbin/xntpd/doc/xntpd.8
@@ -0,0 +1,1075 @@
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 1993
+.Dt XNTPD 8
+.Os
+.Sh NAME
+.Nm xntpd
+.Nd Network Time Protocol daemon
+.Sh SYNOPSIS
+.Nm xntpd
+.Op Fl abdm
+.Op Fl c Ar conffile
+.Op Fl e Ar authdelay
+.Op Fl f Ar driftfile
+.Op Fl k Ar keyfile
+.Op Fl p Ar pidfile
+.Op Fl r Ar broadcastdelay
+.Op Fl s Ar statsdir
+.Op Fl t Ar trustedkey
+.Op Fl v Ar variable
+.Op Fl V Ar variable
+.Sh DESCRIPTION
+.Nm Xntpd
+is a daemon which sets and maintains a Unix system time\-of\-day in
+agreement with Internet standard time servers.
+.Nm Xntpd
+is a complete implementation of the Network Time Protocol (NTP) version
+3 standard, as defined by RFC 1305, but also retains compatibility with
+version 1 and 2 servers as defined by RFC 1059 and RFC 1119,
+respectively.
+.Nm Xntpd
+does all computations in fixed point arithmetic and requires no floating
+point code. The computations done in the protocol and clock adjustment
+code are carried out with high precision and with attention to the
+details which might introduce systematic bias into the computations, to
+try to maintain an accuracy suitable for synchronizing with even the
+most precise external time source.
+.Pp
+Ordinarily,
+.Nm
+reads its configuration from a configuration file at startup time. The
+default configuration file name is
+.Pa /etc/ntp.conf ,
+although this may be overridden from the command line. It is also
+possible to specify a working, although limited,
+.Nm
+configuration entirely on the command line, obviating the need for a
+configuration file. This may be particularly appropriate when
+.Nm
+is to be configured as a broadcast or multicast client, with all peers
+being determined by listening to broadcasts at run time. Various
+internal
+.Nm
+variables can be displayed and configuration options altered while the
+daemon is running through use of the
+.Xr ntpq 8
+and
+.Xr xntpdc 8
+programs.
+.Pp
+The daemon can operate in any of several modes, including symmetric
+active/passive, client/server and broadcast/multicast. A
+broadcast/multicast client can automatically discover remote servers,
+compute one-way delay correction factors and configure itself
+automatically. This makes it possible to deploy a fleet of workstations
+without specifying a configuration file or configuration details
+specific to its environment.
+.Pp
+The following command line arguments are understood by
+.Nm
+(see the configuration file description for a more complete functional
+description):
+.Bl -tag -width indent
+.It Fl a
+Run in
+.Qq "authenticate"
+mode.
+.It Fl b
+Listen for broadcast NTP and sync to this if available.
+.It Fl c
+Specify an alternate configuration file.
+.It Fl d
+Specify debugging mode. This flag may occur multiple times, with each
+occurance indicating greater detail of display.
+.It Fl e
+Specify the time (in seconds) it takes to compute the NTP encryption
+field on this computer.
+.It Fl f Ar driftfile
+Specify the location of the drift file.
+.It Fl k
+Specify the location of the file which contains the NTP authentication
+keys.
+.It Fl m
+Listen for multicast messages and synchronize to them if available
+(requires multicast kernel).
+.It Fl p
+Specify the name of the file to record the daemon's process id.
+.It Fl r
+Ordinarily, the daemon automatically compensates for the network delay
+between the broadcast/multicast server and the client; if the
+calibration procedure fails, use the specified the default delay (in
+seconds).
+.It Fl s
+Specify the directory to be used for creating statistics files.
+.It Fl t Ar trustedkey
+Add a key number to the trusted key list.
+.It Fl v
+Add a system variable.
+.It Fl V
+Add a system variable listed by default.
+.El
+.Sh "CONFIGURATION OPTIONS"
+.Nm Xntpd Ns 's
+configuration file format is similar to other Unix configuration files.
+Comments begin with a
+.Qq #
+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. These 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. Optional arguments are
+delimited by
+.Qq []
+in the following descriptions, while
+alternatives are separated by
+.Qq \&| .
+.Pp
+.Bl -tag -width indent
+.It Xo peer
+.Ar host_address
+.Op key Ar #
+.Op version Ar #
+.Op prefer
+.Xc
+.It Xo server
+.Ar host_address
+.Op key Ar #
+.Op version Ar #
+.Op prefer
+.Xc
+.It Xo broadcast
+.Ar host_address
+.Op key Ar #
+.Op version Ar #
+.Op ttl Ar #
+.Xc
+.Pp
+These three commands specify various time servers to be used and/or time
+services to be provided. The
+.Em peer
+command specifies that the local server is to operate in
+.Qq symmetric active
+mode with the remote server
+.Ar host_address
+named in the command. In this mode the local server can be synchronized
+to the remote server and, in addition, the remote server can be
+synchronized by the local server. This is useful in a network of servers
+where, depending on various failure scenarios, either the local or
+remote server host may be the better source of time. The
+.Em server
+command specifies that the local server is to operate in
+.Qq client
+mode with the remote server named in the command. In
+this mode the local server can be synchronized to the remote server, but
+the remote server can never be synchronized to the local server. The
+.Em broadcast
+command specifies that the local server is to operate in
+.Qq broadcast
+mode where the local server sends periodic broadcast
+messages to a client population at the broadcast/multicast address named
+in the command. Ordinarily, this specification applies only to the local
+server operating as a transmitter; for operation as a broadcast client,
+see the
+.Em broadcastclient
+or
+.Em multicastclient
+commands elsewhere in this document. In this mode the
+.Ar host_address
+is usually the broadcast address on [one of] the local network[s] or a
+multicast address assigned to NTP. The Numbers Czar has assigned the
+address 224.0.1.1 to NTP; this is presently the only number that should
+be used. Note that the use of multicast features requires a multicast
+kernel, which is not yet ubiquitous in vendor products.
+.Pp
+The
+.Ar key
+option, when included, indicates that all packets sent to the address
+are to include authentication fields encrypted using the specified key
+number (the range of which is that of an unsigned 32 bit integer). The
+default is to not include an encryption field. The
+.Ar version
+option allows one to specify the version number to be used for outgoing
+NTP packets. Versions 1, 2, and 3 are the choices, version 3 is the
+default. The
+.Ar prefer
+option marks the host as a preferred host. All other things being equal,
+this host will be chosen for synchronization among a set of correctly
+operating hosts. The
+.Ar ttl
+option is used only with the broadcast mode. It specifies the time-to-live
+(TTL) to use on multicast packets. Selection of the proper value,
+which defaults to 127, is something of a black art and must be
+coordinated with the network admistrator(s).
+.It broadcastclient
+This directs the local server to listen for broadcast messages on the
+local network, in order to discover other servers on the same subnet.
+Upon hearing a broadcast message for the first time, the local server
+measures the nominal network delay using a brief client/server exchange
+with the remote server, then enters the
+.Qq broadcastclient
+mode,
+in which it listens for and synchronizes to succeeding broadcast
+messages. Note that, in order to avoid accidental or malicious
+disruption in this mode, both the local and remote servers must operate
+using authentication with the same trusted key and key identifier.
+.It multicastclient Op Ar IP address ...
+This command is used in the same way as the
+.Em broadcastclient
+command, but operates using IP multicasting. Support for this function
+requires a multicast kernel and the use of authentication. If one or
+more IP addresses are given, the server joins the respective multicast
+group(s). If none are given, the IP address assigned to NTP (224.0.1.1)
+is assumed.
+.It driftfile Ar filename
+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 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. The
+file contains a single floating point value equal to the offset in
+parts-per-million (ppm). Note that the file is updated by first writing
+the current drift value into a temporary file and then using
+.Xr rename 3
+to replace the old version. This implies that
+.Nm
+must have write permission for the directory the drift file is located
+in, and that file system links, symbolic or otherwise, should probably
+be avoided.
+.It enable Ar auth|bclient|pll|monitor|stats Op Ar ...
+Provide a way to enable various server options. Flags not mentioned are
+unaffected. The
+.Ar auth
+flag causes 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 disable (off). The
+.Ar bclient
+flag causes the server
+to listen for a message from a broadcast or multicast server, following
+which an association is automatically instantiated for that server. The
+default for this flag is disable (off). The
+.Ar pll
+flag enables
+the server to adjust its local clock, with default enable (on). If not
+set, 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. The
+.Ar monitor
+flag enables the
+monitoring facility (see elsewhere), with default enable (on). The
+.Ar stats
+flag enables statistics facility filegen (see
+description elsewhere.), with default enable (on).
+.It disable Ar auth|bclient|pll|monitor|stats Op Ar ...
+Provide a way to disable various server options. Flags not mentioned
+are unaffected. The flags presently available are described under the
+enable command.
+.El
+.Sh "AUTHENTICATION OPTIONS"
+.Bl -tag -width indent
+.It keys Ar filename
+This command specifies the name of a file which contains the encryption
+keys and key identifiers used by
+.Nm
+when operating in authenticated mode. The format of this file is
+described later in this document.
+.It trustedkey Ar # Op Ar # ...
+This command is used to specify the encryption key identifiers which are
+trusted for the purposes of authenticating peers suitable for
+sychonization. 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
+arguments are 32 bit unsigned integers. Note, however, that NTP key 0 is
+fixed and globally known. If meaningful authentication is to be
+performed the 0 key should not be trusted.
+.It requestkey Ar #
+This command specifies the key identifier to use with the
+.Xr xntpdc 8
+program, which is useful to diagnose and repair problems that affect
+.Nm
+operation. The operation of the
+.Nm xntpdc
+program are specific to this particular implementation of
+.Nm
+and can
+be expected to work only with this and previous versions of the daemon.
+Requests from a remote
+.Nm xntpdc
+program which affect the state of the
+local server must be authenticated, which requires bot the remote
+program and local server share a common key and key identifier. The
+argument to this command is a 32 bit unsigned integer. If no
+.Em controlkey
+command is included in the configuration file, or if the keys don't
+match, such requests will be ignored.
+.It controlkey Ar #
+This command specifies the key identifier to use with the
+.Xr ntpq 8
+program, which is useful to diagnose and repair problems that affect
+.Nm
+operation. The operation of the
+.Nm ntpq
+program and
+.Nm
+conform to those specified in RFC 1305. Requests from a remote
+.Nm ntpq
+program which affect the state of the local server must be
+authenticated, which requires bot the remote program and local server
+share a common key and key identifier. The argument to this command is a
+32 bit unsigned integer. If no
+.Em requestkey
+command is included in the configuration file, or if the keys don't
+match, such requests will be ignored.
+.It authdelay Ar seconds
+Indicate the amount of time it takes to encrypt an NTP authentication
+field on the local computer. This value is used to correct transmit
+timestamps when the authentication is used on outgoing packets. The
+value usually lies somewhere in the range 0.0001 seconds to 0.003
+seconds, though it is very dependent on the CPU speed of the host
+computer. The value is usually computed using the
+.Em authspeed
+program included with the distribution.
+.El
+.Sh "ACCESS CONTROL OPTIONS"
+.Bl -tag -width indent
+.It Xo restrict
+.Ar address
+.Op mask Ar numeric_mask
+.Op Ar flag
+.Op Ar ...
+.Xc
+.Nm Xntpd
+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. The
+.Em mask
+argument defaults
+to 255.255.255.255, meaning that the
+.Em 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
+.Em address
+is normally
+given in dotted\-quad format, the text string
+.Em default ,
+with no
+mask option, may be used to indicate the default entry.
+.Pp
+In the current implementation, flags always restrict 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 catagories, 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 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 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 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 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 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 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 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 notrust
+Treat these hosts normally in other respects, but never use them as
+synchronization sources.
+.It 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
+.Qq client_limit
+hosts that have
+shown up at the server and that have been active during the last
+.Qq client_limit_period
+seconds are accepted. Requests from other
+clients from the same net are rejected. Only time request packets are
+taken into account.
+.Qq Private ,
+.Qq control ,
+and
+.Qq broadcast
+packets are not subject to client limitation and
+therefore are not contributing to client count. History of clients is
+kept using the monitoring capability of
+.Nm Ns .
+Thus, monitoring is active as long as there is a restriction entry with
+the
+.Qq limited
+flag. The default value for
+.Qq client_limit
+is 3. The default value for
+.Qq client_limit_period
+is 3600 seconds.
+.It 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
+.Qq ntpport
+and
+.Pf non\- Qq ntpport
+may be specified. The
+.Qq ntpport
+is considered more specific and is sorted later in the
+list.
+.El
+.Pp
+Default restriction list entries, with the flags
+.Qq ignore ,
+.Qq 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).
+.Pp
+The restriction facility was added to allow the current access policies
+of the time servers running on the NSFnet backbone to be implemented
+with
+.Nm
+as well. 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.
+.It clientlimit Ar limit
+Set
+.Qq client_limit
+to
+.Ar limit ,
+allow configuration of
+client limitation policy. This variable defines the number of clients
+from the same network that are allowed to use the server.
+.It clientperiod Ar period
+Set
+.Qq client_limit_period ,
+allow configuration of client
+limitation policy. This variable specifies the number of seconds after
+which a client is considered inactive and thus no longer is counted for
+client limit restriction.
+.El
+.Sh "MONITORING OPTIONS"
+.Bl -tag -width indent
+.It statsdir Ar /directory path/
+Indicate the full path of a directory where statistics files should be
+created (see below). This keyword allows the (otherwise constant)
+filegen filename prefix to be modified for file generation sets used for
+handling statistics logs (see
+.Em filegen
+statement below).
+.It statistics Ar name Ns ...
+Enable writing of statistics records. Currently, three kinds of
+statistics are supported:
+.Bl -tag -width indent
+.It loopstats
+Enable 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
+.Qq loopstats :
+.Dl 48773 10847.650 0.0001307 17.3478 2
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight). The next three fields show
+time offset in seconds, frequency offset in parts-per-million and time
+constant of the clock-discipline algorithm at each update of the clock.
+.It peerstats
+Enable recording of peer statistics information. This includes
+statistics records of all peers of a NTP server and of the 1-pps signal,
+where present and configured. Each valid update appends a line of the
+following form to the current element of a file generation set named
+.Qq peerstats :
+.Dl 48773 10847.650 127.127.4.1 9714 -0.001605 0.00000 0.00142
+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 dispersion, all in seconds.
+.It clockstats
+Enable recording of clock driver statistics information. Each update
+received from a clock driver outputs a line of the following form to the
+file generation set named
+.Qq clockstats :
+.Dl 49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+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
+.Tn 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.
+.El
+.Pp
+Statistic files are managed using file generation sets (see
+.Em filegen
+below). The information obtained by enabling statistics recording allows
+analysis of temporal properties of a
+.Nm
+server. It is usually only useful to primary servers or maybe main
+campus servers.
+.It Xo filegen
+.Ar name
+.Op file Ar filename
+.Op type Ar typename
+.Op flag Ar flagval
+.Op link | nolink
+.Op enable | disable
+.Xc
+Configure setting of generation file set
+.Ar name .
+Generation file sets provide a means for handling files that are
+continously 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
+.Em 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 risc of desturbing the operation of
+.Nm Ns .
+(Most important: they can be removed to free space for new data
+produced.) Filenames of set members are built from three elements:
+.Bl -tag -width indent
+.It prefix
+This is a constant filename path. It is not subject to modifications via
+the
+.Em filegen
+statement. 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
+.Qq loopstats
+and
+.Qq peerstats
+filegens can be configured using the
+.Em statsdir
+statement explained above.
+.It filename
+This string is directly concatenated to the
+.Em prefix
+mentioned above (no intervening
+.Sq /
+(slash)). This can be
+modified using the
+.Ar file
+argument to the
+.Em filegen
+statement. No
+.Qq ..
+elements are allowed in this component to
+prevent filenames referring to parts outside the filesystem hierarchy
+denoted by
+.Em prefix .
+.It suffix
+This part is reflects individual elements of a file set. It is generated
+according to the
+.Em type
+of a file set as explained below.
+.El
+.Pp
+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
+.Nm
+server. This type does not perform any changes to file set members
+during runtime, however it provides an easy way of seperating files
+belonging to different
+.Nm
+server incarnations. The set member filename is built by appending a dot
+.Pf ( Sq \&. Ns )
+to concatentated
+.Em prefix
+and
+.Em filename
+strings, and appending the decimal representation of the process id of
+the
+.Nm
+server process.
+.It day
+One file generation set element is created per day. The term
+.Em day
+is based on
+.Tn UTC .
+A day is defined as the period between 00:00 and 24:00
+.Tn UTC .
+The file set
+member suffix consists of a dot
+.Pf ( Qq \&. Ns )
+and a day specification in
+the form
+.Aq YYYYMMDD .
+.Em YYYY
+is a 4 digit year number (e.g. 1992).
+.Em MM
+is a two digit month number.
+.Em DD
+is a two digit day number. Thus, all information written at December
+10th, 1992 would end up in a file named
+.Qq <prefix><filename>.19921210 .
+.It week
+Any file set member contains data related to a certain week of a year.
+The term
+.Em week
+is definied by computing
+.Qq 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 four digit year number,
+the letter
+.Qq W ,
+and a two digit week number. For example,
+information from Jamuary, 10th 1992 would end up in a file with suffix
+.Qq .1992W1 .
+.It month
+One generation file set element is generated per month. The file name
+suffix consists of a dot, a four digit year number, and a two 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
+.Qq a ,
+and an eight 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.
+.El
+.Pp
+Information is only written to a file generation set when this set is
+.Qq enabled .
+Output is prevented by specifying
+.Qq disabled .
+.Pp
+It is convenient to be able to access the
+.Em current
+element of a file generation set by a fixed name. This feature is
+enabled by specifying
+.Qq link
+and disabled using
+.Qq nolink .
+If
+.Qq 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
+.Qq C ,
+and the pid
+of the
+.Nm
+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.
+.El
+.Sh "MISCELLANEOUS OPTIONS"
+.Bl -tag -width indent
+.It precision Ar #
+This command specifies the nominal precision of the local clock. The
+value is an integer approximately equal to the base 2 logarithm of the
+local timekeeping precision in seconds. Normally, the daemon determines
+the precision automatically at startup, so this command is necessary
+only in special cases when the precision cannot be determined
+automatically.
+.It 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 local and remote servers. 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 Xo trap
+.Ar host_address
+.Op port Ar port_number
+.Op interface Ar interface_addess
+.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 which is that of the local interface the message is sent
+through. Note that on a multihomed host the interface used may vary from
+time to time with routing changes.
+.Pp
+The trap receiver will generally log event messages and other
+information from the server in a log file. While such monitor programs
+may also request their own trap dynamically, configuring a trap receiver
+will ensure that no messages are lost when the server is started.
+.It Xo setvar
+.Ar variable
+.Op Ar default
+.Xc
+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 <name>=<value> is followed by the
+.Em default
+keyword the variable will be listed as part of the default system
+variables
+.Pf ( Nm ntpq
+rv command). 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 overide any variables defined via
+the
+.Em setvar
+mechanism.
+.Pp
+There are three special variables that contain the names of all variable
+of the same group. The
+.Em sys_var_list
+holds the names of all system variables. The
+.Em peer_var_list
+holds the names of all peer variables and the
+.Em clock_var_list
+hold the names of the reference clock variables.
+.It monitor Ar yes|no
+.It authenticate Ar yes|no
+These commands have been superseded by the
+.Em enable
+and
+.Em disable
+commands. They are listed here for historical purposes.
+.El
+.Sh "AUTHENTICATION KEY FILE FORMAT"
+The
+.Tn NTP
+standard specifies an extension allowing verification of the
+authenticity of received NTP packets, and to provide an indication of
+authenticity in outgoing packets. This is implemented in
+.Nm
+using the DES or MD5 algorithms to compute a digital signature, or
+message-digest. The specification allows any one of possibly 4 billion
+keys, numbered with 32 bit key identifiers, to be used to authenticate
+an association. The servers involved in an association must agree on the
+key and key identifier used to authenticate their data, though they must
+each learn the key and key identifer independently. 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).
+.Nm Xntpd
+reads its keys from a file specified using the
+.Fl k
+command line option or the
+.Em keys
+statement in the configuration file. While key number 0 is fixed by the
+.Tn 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
+.Dl keyno type key
+.Pp
+where
+.Em keyno
+is a positive integer,
+.Em type
+is a single
+character which defines the format the key is given in, and
+.Em key
+is the key itself.
+.Pp
+The key may be given in one of three different formats, controlled by
+the
+.Em type
+character. The three key types, and corresponding
+formats, are listed following:
+.Bl -tag -width indent
+.It S
+The
+.Em key
+is a 64 bit hexadecimal number in the format specified
+in the DES document, that is the high order 7 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
+.Em 0101010101010101 .
+.It N
+The
+.Em key
+is a 64 bit hexadecimal number in the format specified
+in the
+.Tn 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
+.Em 8080808080808080 .
+.It A
+The
+.Em key
+is a 1\-to\-8 character
+.Tn ASCII
+string. A key is formed from this by using the lower order 7 bits of the
+.Tn ASCII
+representation of
+each character in the string, with zeroes being added on the right when
+necessary to form a full width 56 bit key, in the same way that
+encryption keys are formed from Unix passwords.
+.It M
+The
+.Em key
+is a 1\-to\-8 character
+.Tn 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
+One of the keys may be chosen,
+by way of the configuration file
+.Em requestkey
+statement, to authenticate run time configuration requests made using
+the
+.Xr xntpdc 8
+program. The latter program obtains the key from the terminal as a
+password, so it is generally appropriate to specify the key chosen to be
+used for this purpose in
+.Tn ASCII
+format.
+.Sh PRIMARY CLOCK SUPPORT
+.Nm Xntpd
+can be optionally compiled to include support for a number of types of
+reference clocks. 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
+in the U.S. The interface between the computer and the timecode receiver
+is device dependent and will vary, but is often a serial port.
+.Pp
+Support for the various reference clock drivers is conditionally
+compiled using the compiler define codes described elsewhere. An attempt
+to configure a reference clock when specific support is not available or
+the hardware port has not been appropriately configured results in a
+scolding remark to the system log file, but is otherwise non hazardous.
+.Pp
+For the purposes of configuration,
+.Nm
+treats reference clocks in a manner analogous to normal
+.Tn NTP
+peers as
+much as possible. Reference clocks are referred to by address, much as a
+normal peer is, though an invalid IP address is used to distinguish them
+from normal peers. Reference clock addresses are of the form
+.Em 127.127.t.u
+where
+.Em t
+is an integer denoting the clock type and
+.Em u
+indicates the type\-specific unit number. Reference clocks are
+configured using a
+.Em server
+statement in the configuration file where the
+.Em host_address
+is the clock address. The
+.Em key,
+.Em version
+and
+.Em ttl
+options are not used for reference clock support; however, the
+.Em prefer
+option can be useful to persuade the server to cherish a reference clock
+with somewhat more enthusiasm than other reference clocks or peers, if
+this is advisable. Clock addresses may generally be used anywhere in the
+configuration file a normal IP address can be used, for example, in
+.Em restrict
+statements, although such use would normally be considered strange.
+.Pp
+Reference clock support provides the
+.Em fudge
+command, which can be used to configure reference clocks in special
+ways. Following is the generic format that applies to this command:
+.Pp
+fudge
+.Ar 127.127.t.u
+.Op time1 Ar secs
+.Op time2 Ar secs
+.Op stratum Ar int
+.Op refid Ar int
+.Op flag1 Ar 0|1
+.Op flag2 Ar 0|1
+.Op flag3 Ar 0|1
+.Op flag4 Ar 0|1
+.Pp
+The
+.Em time1
+and
+.Em time2
+options are specified in fixed point seconds and used in some clock
+drivers as calibration constants. By convention, and unless indicated
+otherwise,
+.Em time1
+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. The specified offset is in addition to the propagation delay
+provided by other means, such as internal DIPswitches. The
+.Em stratum
+option is a number in the range zero to 15 and is used to assign a
+nonstandard operating stratum to the clock. The
+.Em refid
+option is an
+.Tn ASCII
+string in the range one to four characters and is
+used to assign a nonstandard reference identifier to the clock. Finally,
+the four binary flags
+.Em flag1,
+.Em flag2,
+.Em flag3
+and
+.Em flag4
+are used for customizing the clock driver. The interpretation of these
+values, and whether they are used at all, is a function of the needs of
+the particular clock driver. However, by convention, and unless
+indicated otherwise,
+.Em flag3
+is used to attach the ppsclock streams module to the configured driver,
+while
+.Em flag4
+is used to enable recording verbose monitoring data to the clockstats
+file configured with the
+.Em filegen
+command. Further information on the ppsclock streams module is in the
+.Pa README
+file in the
+.Pa ./kernel
+directory in the current xntp3 program
+distribution. Further information on this feature is available in the
+.Pa ./scripts/stats
+directory in the same distribution.
+.Pp
+Ordinarily, the stratum of a reference clock is by default zero. Since
+the
+.Nm
+daemon adds one to the stratum of each peer, a primary server ordinarily
+displays stratum one. In order to provide engineered backups, it is
+often useful to specify the reference clock stratum as greater than
+zero. The
+.Em stratum
+option is used for this purpose. Also, in cases involving both a
+reference clock and a 1-pps discipline signal, it is useful to specify
+the reference clock identifier as other than the default, depending on
+the driver. The
+.Em refid
+option is used for this purpose. Except where noted, these options apply
+to all clock drivers.
+.Pp
+.Nm Xntpd
+on Unix machines currently supports several different types of clock
+hardware plus a special pseudo\-clock used for backup or when no other
+clock source is available. In the case of most of the clock drivers,
+support for a 1-pps precision timing signal is available as described in
+the
+.Pa README
+file in the
+.Pa ./doc
+directory of the xntp3 progam distribution.
+The clock drivers, and the addresses used to configure them, are
+described in the
+.Pa README.refclocks
+in the
+.Pa doc
+directory of the current
+program distribution.
+.Sh VARIABLES
+Most variables used by the
+.Tn NTP
+protocol can be examined with the
+.Nm xntpdc
+(mode 7 messages) and the
+.Nm ntpq
+(mode 6 messages). Currently very few variables can be modified via mode
+6 messages. These variables are either created with the
+.Em setvar
+directive or the leap warning variables. The leap warning bits that can
+be set in the
+.Em leapwarning
+variable (up to one month ahead). Both, the
+.Em leapwarning
+and the
+.Em leapindication
+variable, have a slightly different encoding than the usual
+.Em leap
+bits interpretation:
+.Bl -tag -width indent
+.It 00
+The daemon passes the leap bits of its synchronisation source (usual
+mode of operation).
+.It 01/10
+A leap second is added/deleted (operator forced leap second).
+.It 11
+Leap information from the sychronisation source is ignored (thus
+LEAP_NOWARNING is passed on).
+.El
+.Sh FILES
+.Bl -tag -width /etc/ntp.drift -compact
+.It /etc/ntp.conf
+the default name of the configuration file
+.It /etc/ntp.drift
+the conventional name of the drift file
+.It /etc/ntp.keys
+the conventional name of the key file
+.El
+.Sh SEE ALSO
+.Xr ntpdate 8 ,
+.Xr ntpq 8 ,
+.Xr xntpdc 8
+.Sh HISTORY
+Written by
+.An Dennis Ferguson
+at the University of Toronto. Text amended by
+.An David Mills
+at the University of Delaware.
+.Sh BUGS
+.Nm Xntpd
+has gotten rather fat. While not huge, it has gotten larger than might
+be desireable 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/xntpd/doc/xntpdc.8 b/usr.sbin/xntpd/doc/xntpdc.8
new file mode 100644
index 0000000..b2ad6ff
--- /dev/null
+++ b/usr.sbin/xntpd/doc/xntpdc.8
@@ -0,0 +1,674 @@
+.\"
+.\" $Id$
+.\"
+.Dd December 21, 1993
+.Dt XNTPDC 8
+.Os
+.Sh NAME
+.Nm xntpdc
+.Nd query/control program for the Network Time Protocol daemon
+.Sh SYNOPSIS
+.Nm xntpdc
+.Op Fl ilnps
+.Op Fl c Ar command
+.Op Ar host ...
+.Sh DESCRIPTION
+.Nm Xntpdc
+is used to query the
+.Xr xntpd 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 start up using
+.Nm xntpd Ns 's
+configuration file may also be specified at run time using
+.Nm Ns .
+.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
+.Tn NTP
+servers
+running on each of the hosts given as command line arguments, or on
+.Ar localhost
+by default. If no request options are given,
+.Nm
+will attempt to read commands from the standard input and execute these
+on the
+.Tn NTP
+server running on the first host given on the command line,
+again defaulting to
+.Ar localhost
+when no other host is specified.
+.Nm Xntpdc
+will prompt for commands if the standard input is a terminal device.
+.Pp
+.Nm Xntpdc
+uses
+.Tn NTP
+mode 7 packets to communicate with the
+.Tn NTP
+server, and hence
+can be used to query any compatable server on the network which permits
+it. Note that since
+.Tn NTP
+is a UDP protocol this communication will be
+somewhat unreliable, especially over large distances in terms of network
+topology.
+.Nm Xntpdc
+makes no attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable time out time.
+.Pp
+Command line options are described following. 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.
+.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 l
+Obtain a list of peers which are known to the server(s). This switch is
+equivalent to
+.Qq -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
+.Qq -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
+.Qq -c dmpeers .
+.El
+.Sh 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
+.Qq > ,
+followed by a
+file name, to the command line.
+.Pp
+A number of interactive format commands are executed entirely within the
+.Nm
+program itself and do not result in
+.Tn NTP
+mode 7 requests being sent to a
+server. These are described following:
+.Pp
+.Bl -tag -width indent
+.It Xo ?
+.Op Ar command_keyword
+.Xc
+A
+.Em ?
+by itself will print a list of all the command keywords
+known to this incarnation of
+.Nm Ns .
+A
+.Em ?
+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 help Ar command_keyword
+A synonym for the
+.Em ?
+command.
+.It timeout Ar millseconds
+Specify a time out period for responses to server queries. The default
+is about 8000 milliseconds.
+.It 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.
+.It host Ar hostname
+Set the host to which future queries will be sent.
+.Ar Hostname
+may be either a host name or a numeric (dotted quad) dmaddress.
+.It keyid Ar #
+This command allows the specification of a key number to be used to
+authenticate configuration requests. This must correspond to the key
+number the server has been configured to use for this purpose.
+.It 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
+.Tn NTP
+server for this purpose if such requests are to be successful.
+.It hostnames Ar yes|no
+If
+.Ar yes
+is specified, host names are printed in information
+displays. If
+.Ar no
+is given, numeric addresses are printed
+instead. The default is
+.Ar yes
+unless modified using the command line
+.Fl n
+switch.
+.It quit
+Exit
+.Nm Ns .
+.El
+.Sh QUERY COMMANDS
+Query commands result in
+.Tn NTP
+mode 7 packets containing requests for
+information being sent to the server. These are
+.Qq read\-only
+commands in that they make no modification of the server configuration
+state.
+.Bl -tag -width indent
+.It listpeers
+Obtain and print a brief list of the peers for which the server is
+maintaining state. These should include all configured peer associations
+as well as those peers whose stratum is such that they are considered by
+the server to be possible future synchronization candidates.
+.It peers
+Obtain 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. In addition, the character in the left margin indicates the
+mode this peer entry is operating in. A
+.Qq +
+denotes symmetric
+active, a
+.Qq -
+indicates symmetric passive, a
+.Qq =
+means
+the remote server is being polled in client mode, a
+.Qq ^
+indicates that the server is broadcasting to this address, a
+.Qq ~
+denotes that the remote peer is sending broadcasts and a
+.Qq *
+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
+.Qq REFCLK(<implementation number>, <parameter>) .
+On
+.Qq hostnames no
+only IP\-addresses will be displayed.
+.It dmpeers
+A slightly different peer summary list. Identical to the output of the
+.Em 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
+.Qq \&.
+indicates that this peer was cast off
+in the falseticker detection, while a
+.Qq +
+indicates that the
+peer made it through. A
+.Qq *
+denotes the peer the server is
+currently synchronizing with.
+.It Xo showpeer
+.Ar peer_address
+.Op Ar addr2
+.Op Ar addr3
+.Op Ar addr4
+.Xc
+Show a detailed display of the current peer variables for one or more
+peers. Most of these values are described in the
+.Tn NTP
+Version 2 specification.
+.It Xo pstats
+.Ar peer_address
+.Op Ar addr2
+.Op Ar addr3
+.Op Ar addr4
+.Xc
+Show per\-peer statistic counters associated with the specified peer(s).
+.It Xo clockinfo
+.Ar clock_peer_address
+.Op Ar addr2
+.Op Ar addr3
+.Op Ar addr4
+.Xc
+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 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 loopinfo Op Ar oneline|multiline
+Print the values of selected loop filter variables. The loop filter is
+the part of
+.Tn NTP
+which deals with adjusting the local system clock. The
+.Qq offset
+is the last offset given to the loop filter by the
+packet processing code. The
+.Qq frequency
+is the frequency error
+of the local clock in parts-per-million (ppm). The
+.Qq time_const
+controls the
+.Qq stiffness
+of the phase-lock loop and thus the speed at
+which it can adapt to oscillator drift. The
+.Qq watchdog timer
+value is the number of seconds which have elapsed since the last sample
+offset was given to the loop filter. The
+.Ar oneline
+and
+.Ar multiline
+options specify the format in which this information
+is to be printed, with
+.Ar multiline
+as the default.
+.It 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
+.Tn NTP
+Version 3 specification, RFC 1305. The
+.Qq system flags
+show various system flags, some of which can be set and cleared by the
+.Qq enable
+and
+.Qq disable
+configuration commands,
+respectively. The
+.Qq 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
+.Qq tick
+may be incorrect. The
+.Qq broadcastdelay
+shows the default broadcast delay, as set by
+the
+.Qq broadcastdelay
+configuration command, while the
+.Qq authdelay
+shows the default authentication delay, as set by
+the
+.Qq authdelay
+configuration command.
+.It sysstats
+Print statistics counters maintained in the protocol module.
+.It memstats
+Print statistics counters related to memory allocation
+code.
+.It iostats
+Print statistics counters maintained in the input\-output module.
+.It timerstats
+Print statistics counters maintained in the timer/event queue support
+code.
+.It 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 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 Xo clkbug
+.Ar clock_peer_address
+.Op Ar addr2
+.Op Ar addr3
+.Op Ar addr4
+.Xc
+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
+.Sh RUNTIME CONFIGURATION REQUESTS
+All requests which cause state changes in the server are authenticated
+by the server using a configured
+.Tn 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 Ns .
+This can be done using the
+.Em keyid
+and
+.Em 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 addpeer
+.Ar peer_address
+.Op Ar keyid
+.Op Ar version#
+.Op Ar 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
+.Ar 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 addserver
+.Ar peer_address
+.Op Ar keyid
+.Op Ar version#
+.Op Ar prefer
+.Xc
+Identical to the
+.Em addpeer
+command, except that the operating mode is client.
+.It Xo broadcast
+.Ar peer_address
+.Op Ar keyid
+.Op Ar version#
+.Xc
+Identical to the
+.Em 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
+.Tn NTP .
+If a multicast address, a
+multicast-capable kernel is required.
+.It Xo unconfig
+.Ar peer_address
+.Op Ar addr2
+.Op Ar addr3
+.Op Ar addr4
+.Xc
+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 fudge
+.Ar peer_address
+.Op Ar time1
+.Op Ar time2
+.Op Ar stratum
+.Op Ar refid
+.Xc
+This command provides a way to set certain data for a reference clock.
+See the source listing for further information.
+.It Xo enable
+.Ar auth|bclient|pll|monitor|stats
+.Op Ar ...
+.Xc
+Provide a way to enable various server options. Flags not mentioned are
+unaffected. The
+.Ar auth
+flag causes 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 disable (off). The
+.Ar bclient
+flag causes the server
+to listen for a message from a broadcast or multicast server, following
+which an association is automatically instantiated for that server. The
+default for this flag is disable (off). The
+.Ar pll
+flag enables
+the server to adjust its local clock, with default enable (on). If not
+set, 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
+.Tn NTP
+is used only to provide
+synchronization to other clients. The
+.Ar monitor
+flag enables the
+monitoring facility (see elsewhere), with default disable (off). The
+.Ar stats
+flag enables statistics facility filegen (see
+description elsewhere.), with default enable (on).
+.It Xo disable
+.Ar auth|bclient|pll|monitor|stats
+.Op Ar ...
+.Xc
+Provide a way to disable various server options. Flags not mentioned
+are unaffected. The flags presently available are described under the
+enable command.
+.It Xo restrict
+.Ar address
+.Ar mask
+.Ar flag
+.Op Ar flag
+.Xc
+Cause flag(s) to be added to an existing restrict list entry, or adds a
+new entry to the list with the specified flag(s). The possible choices
+for the flags arguments are given in the following list:
+.Bl -tag -width indent
+.It 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 noquery
+Ignore all
+.Tn NTP
+mode 7 packets (i.e. information queries and
+configuration requests) from the source. Time service is not affected.
+.It nomodify
+Ignore all
+.Tn NTP
+mode 7 packets which attempt to modify the state of the
+server (i.e. run time reconfiguration). Queries which return information
+are permitted.
+.It 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 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 noserve
+Ignore
+.Tn NTP
+packets whose mode is other than 7. In effect, time service
+is denied, though queries may still be permitted.
+.It 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 notrust
+Treat these hosts normally in other respects, but never use them as
+synchronization sources.
+.It 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
+.Qq client_limit
+hosts that have
+shown up at the server and that have been active during the last
+.Qq client_limit_period
+seconds are accepted. Requests from other
+clients from the same net are rejected. Only time request packets are
+taken into account.
+.Qq Private ,
+.Qq control ,
+and
+.Qq broadcast
+packets are not subject to client limitation and
+therefore are not contributing to client count. History of clients is
+kept using the monitoring capability of
+.Xr xntpd 8 .
+Thus, monitoring is active as long as there is a restriction entry with
+the
+.Ar limited
+flag. The default value for
+.Qq client_limit
+is 3. The default value for
+.Qq client_limit_period
+is 3600
+seconds. Currently both variables are not runtime configurable.
+.It 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
+.Tn NTP
+UDP port (123). Both
+.Em ntpport
+and
+.Pf non\- Em ntpport
+may be specified. The
+.Em ntpport
+is considered more specific and is sorted later in the list.
+.El
+.It Xo unrestrict
+.Ar address
+.Ar mask
+.Ar flag
+.Op Ar flag
+.Xc
+Remove the specified flag(s) from the restrict list entry indicated
+by the
+.Ar address
+and
+.Ar mask
+arguments.
+.It Xo delrestrict
+.Ar address
+.Ar mask
+.Op Ar ntpport
+.Xc
+Delete the matching entry from the restrict list.
+.It monitor Ar yes|no
+Enable or disable the monitoring facility. Note that a
+.Em monitor Ar no
+command followed by a
+.Em monitor Ar yes
+command is a good way of resetting the packet counts.
+.It readkeys
+Cause 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
+.Nm xntpd
+configuration file). This allows encryption keys to be changed without
+restarting the server.
+.It Xo trustkey
+.Ar keyid
+.Op Ar keyid
+.Op Ar keyid
+.Op Ar keyid
+.Xc
+Add one or more keys to the trusted key list. When authentication is
+enabled, peers whose time is to be trusted must be authenticated using a
+trusted key.
+.It Xo untrustkey
+.Ar keyid
+.Op Ar keyid
+.Op Ar keyid
+.Op Ar keyid
+.Xc
+Remove one or more keys from the trusted key list.
+.It authinfo
+Return information concerning the authentication module, including
+known keys and counts of encryptions and decryptions which have been
+done.
+.It setprecision Ar precision_value
+Set the precision which the server advertises to the specified value.
+This should be a negative integer in the range -4 through -20.
+.It traps
+Display the traps set in the server. See the source listing for further
+information.
+.It Xo 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 clrtrap
+.Ar address
+.Op Ar port
+.Op Ar interface
+.Xc
+Clear a trap for asynchronous messages. See the source listing for
+further information.
+.It reset Ar ...
+Clear the statistics counters in various modules of the server. See the
+source listing for further information.
+.El
+.Sh SEE ALSO
+.Xr xntpd 8
+.Sh HISTORY
+Written by
+.An Dennis Ferguson
+at the University of Toronto.
+.Sh BUGS
+.Nm Xntpdc
+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/xntpd/include/README b/usr.sbin/xntpd/include/README
new file mode 100644
index 0000000..73d0620
--- /dev/null
+++ b/usr.sbin/xntpd/include/README
@@ -0,0 +1,21 @@
+README file for directory ./include of the NTP Version 3 distribution
+
+This directory contains the include files used by most programs in this
+distribution. The ./sys directory in this directory contains system
+header files used by the clock discipline and STREAMS modules in the
+../kernel directory.
+
+Note that multicast support (MCAST define) requires the header file
+/usr/include/netinet/in.h for the particular architecture to be in place.
+This file is constructed during the installation process in older systems;
+it is already in place for those machines that support multicast ex box.
+The file in.h included in this distribution is for Suns; the files for
+other systems can be found in the multicast distribtutions for each
+architecture separately.
+
+If the precision-time kernel (KERNEL_PLL define) is configured, the
+installation process requires the header file /usr/include/sys/timex.h
+for the particular architecture to be in place. The file timex.h included
+in this distribution is for Suns; the files for other systems can be
+found in the kernel distributions available from the manufacturer's
+representatives.
diff --git a/usr.sbin/xntpd/include/in.h b/usr.sbin/xntpd/include/in.h
new file mode 100644
index 0000000..c18d569
--- /dev/null
+++ b/usr.sbin/xntpd/include/in.h
@@ -0,0 +1,256 @@
+/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */
+
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at 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'' without express or implied warranty.
+ */
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981.
+ */
+
+#ifndef _netinet_in_h
+#define _netinet_in_h
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP 0 /* dummy for IP */
+#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* group control protocol */
+#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
+#define IPPROTO_ST 5 /* st */
+#define IPPROTO_TCP 6 /* tcp */
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#define IPPROTO_PUP 12 /* pup */
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#define IPPROTO_IDP 22 /* xns idp */
+#define IPPROTO_HELLO 63 /* "hello" routing protocol */
+#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */
+#define IPPROTO_OSPF 89 /* Open SPF IGP */
+
+#define IPPROTO_RAW 255 /* raw IP packet */
+#define IPPROTO_MAX 256
+
+/*
+ * Port/socket numbers: network standard functions
+ */
+#define IPPORT_ECHO 7
+#define IPPORT_DISCARD 9
+#define IPPORT_SYSTAT 11
+#define IPPORT_DAYTIME 13
+#define IPPORT_NETSTAT 15
+#define IPPORT_FTP 21
+#define IPPORT_TELNET 23
+#define IPPORT_SMTP 25
+#define IPPORT_TIMESERVER 37
+#define IPPORT_NAMESERVER 42
+#define IPPORT_WHOIS 43
+#define IPPORT_MTP 57
+
+/*
+ * Port/socket numbers: host specific functions
+ */
+#define IPPORT_TFTP 69
+#define IPPORT_RJE 77
+#define IPPORT_FINGER 79
+#define IPPORT_TTYLINK 87
+#define IPPORT_SUPDUP 95
+
+/*
+ * UNIX TCP sockets
+ */
+#define IPPORT_EXECSERVER 512
+#define IPPORT_LOGINSERVER 513
+#define IPPORT_CMDSERVER 514
+#define IPPORT_EFSSERVER 520
+
+/*
+ * UNIX UDP sockets
+ */
+#define IPPORT_BIFFUDP 512
+#define IPPORT_WHOSERVER 513
+#define IPPORT_ROUTESERVER 520 /* 520+1 also used */
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ * Ports > IPPORT_USERRESERVED are reserved
+ * for servers, not necessarily privileged.
+ */
+#define IPPORT_RESERVED 1024
+#define IPPORT_USERRESERVED 5000
+
+/*
+ * Link numbers
+ */
+#define IMPLINK_IP 155
+#define IMPLINK_LOWEXPER 156
+#define IMPLINK_HIGHEXPER 158
+
+/*
+ * Internet address
+ * This definition contains obsolete fields for compatibility
+ * with SunOS 3.x and 4.2bsd. The presence of subnets renders
+ * divisions into fixed fields misleading at best. New code
+ * should use only the s_addr field.
+ */
+struct in_addr {
+ union {
+ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+ struct { u_short s_w1,s_w2; } S_un_w;
+ u_long S_addr;
+ } S_un;
+#define s_addr S_un.S_addr /* should be used for all code */
+#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */
+#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */
+#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */
+#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */
+#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST 0x00ffffff
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST 0x0000ffff
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST 0x000000ff
+
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IN_MULTICAST(i) IN_CLASSD(i)
+
+#define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+
+#define INADDR_ANY (u_long)0x00000000
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */
+
+#define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */
+
+#define IN_LOOPBACKNET 127 /* official! */
+
+/*
+ * Define a macro to stuff the loopback address into an Internet address
+ */
+#define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET;}
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ short sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ */
+#define IP_OPTIONS 1 /* set/get IP per-packet options */
+#define IP_MULTICAST_IF 2 /* set/get IP multicast interface */
+#define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */
+#define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#if !defined(vax) && !defined(ntohl) && !defined(i386)
+/*
+ * Macros for number representation conversion.
+ */
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#endif
+
+#if !defined(ntohl) && (defined(vax) || defined(i386))
+u_short ntohs(), htons();
+u_long ntohl(), htonl();
+#endif
+
+#ifdef KERNEL
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
+struct in_addr in_makeaddr();
+u_long in_netof(), in_lnaof();
+#endif
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(i386)
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif BYTE_ORDER
+
+/*
+ * Macros for number representation conversion.
+ */
+#if BYTE_ORDER==LITTLE_ENDIAN
+#define NTOHL(d) ((d) = ntohl((d)))
+#define NTOHS(d) ((d) = ntohs((d)))
+#define HTONL(d) ((d) = htonl((d)))
+#define HTONS(d) ((d) = htons((d)))
+#else
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#define NTOHL(d)
+#define NTOHS(d)
+#define HTONL(d)
+#define HTONS(d)
+#endif
+
+#endif /*!_netinet_in_h*/
diff --git a/usr.sbin/xntpd/include/l_stdlib.h b/usr.sbin/xntpd/include/l_stdlib.h
new file mode 100644
index 0000000..221682e
--- /dev/null
+++ b/usr.sbin/xntpd/include/l_stdlib.h
@@ -0,0 +1,284 @@
+/*
+ * Proto types for machines that are not ANSI and POSIX compliant.
+ * This is optionaly
+ */
+
+#ifndef _l_stdlib_h
+#define _l_stdlib_h
+
+#if defined(NTP_POSIX_SOURCE)
+#include <stdlib.h>
+#endif
+
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif
+#endif
+#endif
+
+/*
+ * Unprottyped library functions for SunOS 4.x.x
+ */
+#ifdef SYS_SUNOS4
+extern void closelog P((void));
+extern void openlog P((char *, int, int));
+extern void syslog P((int, char *, ...));
+extern int setlogmask P((int));
+
+extern char * getpass P((char *));
+
+extern int setpriority P((int ,int ,int));
+
+extern long strtol P((char *, char **, int));
+
+#if !defined(NTP_POSIX_SOURCE)
+extern int atoi P((char *));
+extern int dup2 P((int, int));
+extern int execve P((char *, char **,char **));
+extern int fork P((void));
+extern int getdtablesize P((void));
+extern int qsort P((void *, int , int,
+ int (*compar)(void *, void *)));
+extern int rand P((void));
+extern int setpgrp P((int, int));
+extern void srand P((unsigned int));
+extern void bcopy P((char *, char *, int));
+#endif
+
+#ifndef bzero /* XXX macro prototyping clash */
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+#endif
+extern char *mktemp P((char *));
+
+extern int tolower P((int));
+
+extern int isatty P((int));
+
+extern unsigned sleep P((unsigned ));
+extern unsigned int alarm P((unsigned int));
+extern int pause P((void));
+
+extern int getpid P((void));
+extern int getppid P((void));
+
+extern int close P((int));
+extern int ioctl P((int, int, char *));
+extern int read P((int, void *, unsigned));
+extern int rename P((char *, char *));
+extern int write P((int, const void *, unsigned));
+extern int unlink P((const char *));
+extern int link P((const char *, const char *));
+
+#ifdef FILE
+extern int fclose P((FILE *));
+extern int fflush P((FILE *));
+extern int fprintf P((FILE *, char *, ...));
+extern int fscanf P((FILE *, char *, ...));
+extern int fputs P((char *, FILE *));
+extern int fputc P((char, FILE *));
+extern int fread P((char *, int, int, FILE *));
+extern int printf P((char *, ...));
+extern int setbuf P((FILE *, char *));
+extern int setvbuf P((FILE *, char *, int, int));
+extern int scanf P((char *, ...));
+extern int sscanf P((char *, char *, ...));
+extern int vsprintf P((char *, char *, ...));
+extern int _flsbuf P((int, FILE *));
+extern int _filbuf P((FILE *));
+extern void perror P((char *));
+#ifndef NTP_POSIX_SOURCE
+extern int setlinebuf P((FILE *));
+#endif
+#endif
+
+#ifdef _ntp_string_h
+#ifdef NTP_POSIX_SOURCE /* these are builtins */
+#ifndef NTP_NEED_BOPS /* but may be emulated by bops */
+extern char *memcpy();
+extern char *memset();
+extern int memcmp();
+#endif
+#endif
+#endif
+
+#ifdef _sys_socket_h
+extern int bind P((int, struct sockaddr *, int));
+extern int connect P((int, struct sockaddr *, int));
+extern int sendto P((int, char *, int, int, struct sockaddr *, int));
+extern int setsockopt P((int, int, int, char *, int));
+extern int socket P((int, int, int));
+extern int recvfrom P((int, char *, int, int, struct sockaddr *, int *));
+#endif /* _sys_socket_h */
+
+#ifdef _ntp_select_h
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+#ifdef _sys_time_h
+extern int adjtime P((struct timeval *, struct timeval *));
+extern int setitimer P((int , struct itimerval *, struct itimerval *));
+#ifdef SYSV_TIMEOFDAY
+extern int gettimeofday P((struct timeval *));
+extern int settimeofday P((struct timeval *));
+#else /* ! SYSV_TIMEOFDAY */
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+#endif /* SYSV_TIMEOFDAY */
+#endif /* _sys_time_h */
+
+#ifdef __time_h
+extern time_t time P((time_t *));
+#endif
+
+#ifdef __setjmp_h
+extern int setjmp P((jmp_buf));
+extern void longjmp P((jmp_buf, int));
+#endif
+
+#ifdef _sys_resource_h
+extern int getrusage P((int, struct rusage *));
+#endif
+
+#ifdef _nlist_h
+extern int nlist P((char *, struct nlist *));
+#endif
+
+#endif /* SYS_SUNOS4 */
+
+/*
+ * Unprototyped library functions for DEC OSF/1
+ */
+#ifdef SYS_DECOSF1
+#ifndef _MACHINE_ENDIAN_H_
+#define _MACHINE_ENDIAN_H_
+extern u_short htons P((u_short));
+extern u_short ntohs P((u_short));
+extern U_LONG htonl P((U_LONG));
+extern U_LONG ntohl P((U_LONG));
+#endif /* _MACHINE_ENDIAN_H_ */
+
+/*
+extern char * getpass P((char *));
+*/
+extern char * mktemp P((char *));
+#ifndef SYS_IX86OSF1
+extern int ioctl P((int, u_long, char *));
+extern void bzero P((char *, int));
+#endif
+
+#ifdef SOCK_DGRAM
+extern int bind P((int, const struct sockaddr *, int));
+extern int connect P((int, const struct sockaddr *, int));
+extern int socket P((int, int, int));
+extern int sendto P((int, const void *, int, int, const struct sockaddr *, int));
+extern int setsockopt P((int, int, int, const void *, int));
+extern int recvfrom P((int, void *, int, int, struct sockaddr *, int *));
+#endif /* SOCK_STREAM */
+
+#ifdef _ntp_select_h
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+#endif /* DECOSF1 */
+
+/*
+ * Unprototyped library functions for Ultrix
+ */
+#ifdef SYS_ULTRIX
+extern int close P((int));
+extern char * getpass P((char *));
+extern int getpid P((void));
+extern int ioctl P((int, int, char *));
+extern char *mktemp P((char *));
+extern int unlink P((const char *));
+extern int link P((const char *, const char *));
+
+extern void closelog P((void));
+extern void syslog P((int, char *, ...));
+#ifndef LOG_DAEMON
+extern void openlog P((char *, int));
+#else
+extern void openlog P((char *, int, int));
+#endif
+
+extern int setpriority P((int ,int ,int ));
+
+#ifdef SOCK_DGRAM
+extern int bind P((int, struct sockaddr *, int));
+extern int connect P((int, struct sockaddr *, int));
+extern int socket P((int, int, int));
+extern int sendto P((int, char *, int, int, struct sockaddr *, int));
+extern int setsockopt P((int, int, int, char *, int));
+extern int recvfrom P((int, char *, int, int, struct sockaddr *, int *));
+#endif /* SOCK_STREAM */
+
+#ifdef _TIME_H_
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+extern int adjtime P((struct timeval *, struct timeval *));
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+extern int setitimer P((int , struct itimerval *, struct itimerval *));
+#endif /* _TIME_H_ */
+
+#ifdef N_UNDF
+extern int nlist P((char *, struct nlist *));
+#endif
+
+#ifndef bzero /* XXX macro prototyping clash */
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+#endif
+
+#ifndef NTP_POSIX_SOURCE
+extern int atoi P((char *));
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+extern int execve P((char *, char **,char **));
+extern int fork P((void));
+extern int getdtablesize P((void));
+extern int ran P((void));
+extern int rand P((void));
+extern void srand P((unsigned int));
+#ifdef _TIME_H_
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+#endif
+#endif
+
+#ifdef _RESOURCE_H_
+extern int getrusage P((int, struct rusage *));
+#endif
+
+#endif /* SYS_ULTRIX */
+
+#if defined(__convex__)
+extern char * getpass P((char *));
+#endif
+
+#ifdef SYS_IRIX4
+extern char * getpass P((char *));
+#endif /* IRIX4 */
+
+#ifdef SYS_VAX
+extern char * getpass P((char *));
+#endif /* VAX */
+
+#ifdef SYS_DOMAINOS
+extern char * getpass P((char *));
+#endif /* SYS_DOMAINOS */
+
+#ifdef SYS_BSD
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#endif
+
+#endif /* l_stdlib_h */
+
diff --git a/usr.sbin/xntpd/include/md5.h b/usr.sbin/xntpd/include/md5.h
new file mode 100644
index 0000000..82b43e1
--- /dev/null
+++ b/usr.sbin/xntpd/include/md5.h
@@ -0,0 +1,56 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "ntp_types.h"
+
+/* typedef a 32-bit type */
+typedef unsigned LONG UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ();
+void MD5Update ();
+void MD5Final ();
+
+/*
+ ***********************************************************************
+ ** End of md5.h **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/include/mx4200.h b/usr.sbin/xntpd/include/mx4200.h
new file mode 100644
index 0000000..13058de
--- /dev/null
+++ b/usr.sbin/xntpd/include/mx4200.h
@@ -0,0 +1,40 @@
+
+/* records transmitted from extern CDU to MX 4200 */
+#define PMVXG_S_INITMODEA 0 /* initialization/mode part A */
+#define PMVXG_S_INITMODEB 1 /* initialization/mode part B*/
+#define PMVXG_S_SATHEALTH 2 /* satellite health control */
+#define PMVXG_S_DIFFNAV 3 /* differential navigation control */
+#define PMVXG_S_PORTCONF 7 /* control port configuration */
+#define PMVXG_S_GETSELFTEST 3 /* self test (request results) */
+#define PMVXG_S_RTCMCONF 16 /* RTCM port configuration */
+#define PMVXG_S_PASSTHRU 17 /* equipment port pass-thru config */
+#define PMVXG_S_RESTART 18 /* restart control */
+#define PMVXG_S_OSCPARAM 19 /* oscillator parameter */
+#define PMVXG_S_DOSELFTEST 20 /* self test (activate a test) */
+#define PMVXG_S_TRECOVCONF 23 /* time recovery configuration */
+#define PMVXG_S_RAWDATASEL 24 /* raw data port data selection */
+#define PMVXG_S_EQUIPCONF 26 /* equipment port configuration */
+#define PMVXG_S_RAWDATACONF 27 /* raw data port configuration */
+
+/* records transmitted from MX 4200 to external CDU */
+#define PMVXG_D_STATUS 0 /* status */
+#define PMVXG_D_POSITION 1 /* position */
+#define PMVXG_D_OPDOPS 3 /* (optimum) DOPs */
+#define PMVXG_D_MODEDATA 4 /* mode data */
+#define PMVXG_D_SATPRED 5 /* satellite predictions */
+#define PMVXG_D_SATHEALTH 6 /* satellite health status */
+#define PMVXG_D_UNRECOG 7 /* unrecognized request response */
+#define PMVXG_D_SIGSTRLOC 8 /* sig strength & location (sats 1-4) */
+#define PMVXG_D_SPEEDHEAD 11 /* speed/heading data */
+#define PMVXG_D_OSELFTEST 12 /* (old) self-test results */
+#define PMVXG_D_SIGSTRLOC2 18 /* sig strength & location (sats 5-8) */
+#define PMVXG_D_OSCPARAM 19 /* oscillator parameter */
+#define PMVXG_D_SELFTEST 20 /* self test results */
+#define PMVXG_D_PHV 21 /* position, height & velocity */
+#define PMVXG_D_DOPS 22 /* DOPs */
+#define PMVXG_D_SOFTCONF 30 /* software configuration */
+#define PMVXG_D_DIFFGPSMODE 503 /* differential gps moding */
+#define PMVXG_D_TRECOVUSEAGE 523 /* time recovery usage */
+#define PMVXG_D_RAWDATAOUT 524 /* raw data port data output */
+#define PMVXG_D_TRECOVRESULT 828 /* time recovery results */
+#define PMVXG_D_TRECOVOUT 830 /* time recovery output message */
diff --git a/usr.sbin/xntpd/include/ntp.h b/usr.sbin/xntpd/include/ntp.h
new file mode 100644
index 0000000..5eca0e8
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp.h
@@ -0,0 +1,706 @@
+/*
+ * ntp.h - NTP definitions for the masses
+ */
+
+#include "ntp_types.h"
+
+/*
+ * How to get signed characters. On machines where signed char works,
+ * use it. On machines where signed char doesn't work, char had better
+ * be signed.
+ */
+#if !defined(S_CHAR_DEFINED)
+#if defined(NO_SIGNED_CHAR_DECL)
+typedef char s_char;
+#else
+typedef signed char s_char;
+#endif
+#ifdef sequent
+#undef SO_RCVBUF
+#undef SO_SNDBUF
+#endif
+#endif
+
+/*
+ * NTP protocol parameters. See section 3.2.6 of the specification.
+ */
+#define NTP_VERSION ((u_char)3) /* current version number */
+#define NTP_OLDVERSION ((u_char)1) /* oldest credible version */
+#define NTP_PORT 123 /* included for sake of non-unix machines */
+#define NTP_MAXSTRATUM ((u_char)15) /* max stratum, infinity a la Bellman-Ford */
+#define NTP_MAXAGE 86400 /* one day in seconds */
+#define NTP_MAXSKEW 1 /* 1 sec, skew after NTP_MAXAGE w/o updates */
+#define NTP_SKEWINC 49170 /* skew increment for clock updates (l_f) */
+#define NTP_SKEWFACTOR 16 /* approximation of factor for peer calcs */
+#define NTP_MAXDISTANCE (1 * FP_SECOND) /* max. rootdelay for synchr. */
+#define NTP_MINDPOLL 6 /* log2 default min poll interval (64 s) */
+#define NTP_MAXDPOLL 10 /* log2 default max poll interval (~17 m) */
+#define NTP_MINPOLL 4 /* log2 min poll interval (16 s) */
+#define NTP_MAXPOLL 14 /* log2 max poll interval (~4.5 h) */
+#define NTP_MINCLOCK 3 /* minimum for outlyer detection */
+#define NTP_MAXCLOCK 10 /* maximum select list size */
+#define NTP_MINDISPERSE (FP_SECOND / 100) /* min dispersion (u_fp 10 ms) */
+#define NTP_MAXDISPERSE (FP_SECOND * 16) /* max dispersion (u_fp 16 s) */
+#define NTP_DISPFACTOR 20 /* MAXDISPERSE as a shift (u_fp 16 s) */
+#define NTP_WINDOW 8 /* reachability register size */
+#define NTP_SHIFT 8 /* 8 suitable for crystal time base */
+#define NTP_MAXKEY 65535 /* maximum authentication key number */
+#define NTP_MAXD 3 /* log2 estimated error averaging factor */
+
+/*
+ * Loop filter parameters. See section 5.1 of the specification.
+ *
+ * Note that these are appropriate for a crystal time base. If your
+ * system clock is line frequency controlled you should read the
+ * specification for appropriate modifications. Note that the
+ * loop filter code will have to change if you change CLOCK_MAX
+ * to be greater than or equal to 500 ms.
+ *
+ * Note these parameters have been rescaled for a time constant range from
+ * 0 through 10, with 2 corresoponding to the old time constant of 0.
+ */
+#define CLOCK_MINSTEP 900 /* step timeout (sec) */
+#define CLOCK_ADJ 0 /* log2 adjustment interval (1 sec) */
+#define CLOCK_DSCALE 20 /* skew reg. scale: unit is 2**-20 ~= 1 ppm */
+#define CLOCK_FREQ 16 /* log2 frequency weight (65536) */
+#define CLOCK_PHASE 6 /* log2 phase weight (64) */
+#define CLOCK_LIMIT 30 /* time constant adjust threshold */
+#define CLOCK_G 2 /* log2 frequency averaging factor */
+#define CLOCK_MAXSEC 800 /* max update interval for pll */
+
+#define CLOCK_MAX_FP 0x000020c5 /* max clock offset (s_fp 128 ms) */
+#define CLOCK_MAX_F 0x20c49ba6 /* max clock offset (l_fp 128 ms) */
+#define CLOCK_MAX_I 0x00000000 /* both fractional and integral parts */
+
+#define CLOCK_WAYTOOBIG 1000 /* if clock 1000 sec off, forget it */
+
+/*
+ * Event timers are actually implemented as a sorted queue of expiry
+ * times. The queue is slotted, with each slot holding timers which
+ * expire in a 2**(NTP_MINPOLL-1) (8) second period. The timers in
+ * each slot are sorted by increasing expiry time. The number of
+ * slots is 2**(NTP_MAXPOLL-(NTP_MINPOLL-1)), or 512, to cover a time
+ * period of 2**NTP_MAXPOLL (16384) seconds into the future before
+ * wrapping.
+ */
+#define EVENT_TIMEOUT CLOCK_ADJ
+
+struct event {
+ struct event *next; /* next in chain */
+ struct event *prev; /* previous in chain */
+ struct peer *peer; /* peer this counter belongs to */
+ void (*event_handler)(); /* routine to call to handle event */
+ u_long event_time; /* expiry time of counter */
+};
+
+#define TIMER_SLOTTIME (1<<(NTP_MINPOLL-1))
+#define TIMER_NSLOTS (1<<(NTP_MAXPOLL-(NTP_MINPOLL-1)))
+#define TIMER_SLOT(t) (((t) >> (NTP_MINPOLL-1)) & (TIMER_NSLOTS-1))
+
+/*
+ * TIMER_ENQUEUE() puts stuff on the timer queue. It takes as
+ * arguments (ea), an array of event slots, and (iev), the event
+ * to be inserted. This one searches the hash bucket from the
+ * end, and is about optimum for the timing requirements of
+ * NTP peers.
+ */
+#define TIMER_ENQUEUE(ea, iev) \
+ do { \
+ register struct event *ev; \
+ \
+ ev = (ea)[TIMER_SLOT((iev)->event_time)].prev; \
+ while (ev->event_time > (iev)->event_time) \
+ ev = ev->prev; \
+ (iev)->prev = ev; \
+ (iev)->next = ev->next; \
+ (ev)->next->prev = (iev); \
+ (ev)->next = (iev); \
+ } while(0)
+
+/*
+ * TIMER_INSERT() also puts stuff on the timer queue, but searches the
+ * bucket from the top. This is better for things that do very short
+ * time outs, like clock support.
+ */
+#define TIMER_INSERT(ea, iev) \
+ do { \
+ register struct event *ev; \
+ \
+ ev = (ea)[TIMER_SLOT((iev)->event_time)].next; \
+ while (ev->event_time != 0 && \
+ ev->event_time < (iev)->event_time) \
+ ev = ev->next; \
+ (iev)->next = ev; \
+ (iev)->prev = ev->prev; \
+ (ev)->prev->next = (iev); \
+ (ev)->prev = (iev); \
+ } while(0)
+
+/*
+ * Remove an event from the queue.
+ */
+#define TIMER_DEQUEUE(ev) \
+ do { \
+ if ((ev)->next != 0) { \
+ (ev)->next->prev = (ev)->prev; \
+ (ev)->prev->next = (ev)->next; \
+ (ev)->next = (ev)->prev = 0; \
+ } \
+ } while (0)
+
+/*
+ * The interface structure is used to hold the addresses and socket
+ * numbers of each of the interfaces we are using.
+ */
+struct interface {
+ int fd; /* socket this is opened on */
+ int bfd; /* socket for receiving broadcasts */
+ struct sockaddr_in sin; /* interface address */
+ struct sockaddr_in bcast; /* broadcast address */
+ struct sockaddr_in mask; /* interface mask */
+ char name[8]; /* name of interface */
+ int flags; /* interface flags */
+ int last_ttl; /* last TTL specified */
+ long received; /* number of incoming packets */
+ long sent; /* number of outgoing packets */
+ long notsent; /* number of send failures */
+};
+
+/*
+ * Flags for interfaces
+ */
+#define INT_BROADCAST 1 /* can broadcast out this interface */
+#define INT_BCASTOPEN 2 /* broadcast socket is open */
+#define INT_LOOPBACK 4 /* the loopback interface */
+#define INT_MULTICAST 8 /* multicasting enabled */
+
+/*
+ * Define flasher bits (tests 1 through 8 in packet procedure)
+ * These reveal the state at the last grumble from the peer and are
+ * most handy for diagnosing problems, even if not strictly a state
+ * variable in the spec. These are recorded in the peer structure.
+ */
+#define TEST1 0x01 /* duplicate packet received */
+#define TEST2 0x02 /* bogus packet received */
+#define TEST3 0x04 /* protocol unsynchronized */
+#define TEST4 0x08 /* peer delay/dispersion bounds check */
+#define TEST5 0x10 /* peer authentication failed */
+#define TEST6 0x20 /* peer clock unsynchronized */
+#define TEST7 0x40 /* peer stratum out of bounds */
+#define TEST8 0x80 /* root delay/dispersion bounds check */
+
+/*
+ * The peer structure. Holds state information relating to the guys
+ * we are peering with. Most of this stuff is from section 3.2 of the
+ * spec.
+ */
+struct peer {
+ struct peer *next;
+ struct peer *ass_next; /* link pointer in associd hash */
+ struct sockaddr_in srcadr; /* address of remote host */
+ struct interface *dstadr; /* pointer to address on local host */
+ struct refclockproc *procptr; /* pointer to reference clock sutuff */
+ u_char leap; /* leap indicator */
+ u_char hmode; /* association mode with this peer */
+ u_char pmode; /* peer's association mode */
+ u_char stratum; /* stratum of remote peer */
+ s_char precision; /* peer's clock precision */
+ u_char ppoll; /* peer poll interval */
+ u_char hpoll; /* local host poll interval */
+ u_char minpoll; /* min local host poll interval */
+ u_char maxpoll; /* max local host poll interval */
+ u_char version; /* version number */
+ u_char flags; /* peer flags */
+ u_char cast_flags; /* flags MDF_?CAST */
+ u_char flash; /* peer flashers (for maint) */
+ u_char refclktype; /* reference clock type */
+ u_char refclkunit; /* reference clock unit number */
+ u_char sstclktype; /* clock type for system status word */
+ s_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ struct event event_timer; /* event queue entry */
+ U_LONG keyid; /* encription key ID */
+ U_LONG pkeyid; /* keyid used to encrypt last message */
+ u_short associd; /* association ID, a unique integer */
+ u_char ttl; /* time to live (multicast) */
+/* **Start of clear-to-zero area.*** */
+/* Everything that is cleared to zero goes below here */
+ u_char valid; /* valid counter */
+#define clear_to_zero valid
+ u_char reach; /* reachability, NTP_WINDOW bits */
+ u_char unreach; /* unreachable count */
+ u_short filter_nextpt; /* index into filter shift register */
+ s_fp filter_delay[NTP_SHIFT]; /* delay part of shift register */
+ l_fp filter_offset[NTP_SHIFT]; /* offset part of shift register */
+ s_fp filter_soffset[NTP_SHIFT]; /* offset in s_fp format, for disp */
+ l_fp org; /* originate time stamp */
+ l_fp rec; /* receive time stamp */
+ l_fp xmt; /* transmit time stamp */
+/* ***End of clear-to-zero area.*** */
+/* Everything that is cleared to zero goes above here */
+ u_char filter_order[NTP_SHIFT]; /* we keep the filter sorted here */
+#define end_clear_to_zero filter_order[0]
+ u_fp filter_error[NTP_SHIFT]; /* error part of shift register */
+ long update; /* base sys_clock for skew calc.s */
+ s_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+ s_fp soffset; /* fp version of above */
+ s_fp synch; /* synch distance from above */
+ u_fp selectdisp; /* select dispersion */
+ s_fp estbdelay; /* broadcast offset */
+
+ /*
+ * statistic counters
+ */
+ u_long timereset; /* time stat counters were reset */
+ u_long sent; /* number of updates sent */
+ u_long received; /* number of frames received */
+ u_long timereceived; /* last time a frame received */
+ u_long timereachable; /* last reachable/unreachable event */
+ u_long processed; /* processed by the protocol */
+ u_long badauth; /* bad credentials detected */
+ u_long bogusorg; /* rejected due to bogus origin */
+ u_long oldpkt; /* rejected as duplicate packet */
+ u_long seldisptoolarge; /* too much dispersion for selection */
+ u_long selbroken; /* broken NTP detected in selection */
+ u_long seltooold; /* too long since sync in selection */
+ u_char candidate; /* position after candidate selection */
+ u_char select; /* position at end of falseticker sel */
+ u_char was_sane; /* set to 1 if it passed sanity check */
+ u_char correct; /* set to 1 if it passed correctness check */
+ u_char last_event; /* set to code for last peer error */
+ u_char num_events; /* num. of events which have occurred */
+};
+
+/*
+ * Values for peer.leap, sys_leap
+ */
+#define LEAP_NOWARNING 0x0 /* normal, no leap second warning */
+#define LEAP_ADDSECOND 0x1 /* last minute of day has 61 seconds */
+#define LEAP_DELSECOND 0x2 /* last minute of day has 59 seconds */
+#define LEAP_NOTINSYNC 0x3 /* overload, clock is free running */
+
+/*
+ * Values for peer.mode
+ */
+#define MODE_UNSPEC 0 /* unspecified (probably old NTP version) */
+#define MODE_ACTIVE 1 /* symmetric active */
+#define MODE_PASSIVE 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client mode */
+#define MODE_SERVER 4 /* server mode */
+#define MODE_BROADCAST 5 /* broadcast mode */
+#define MODE_CONTROL 6 /* control mode packet */
+#define MODE_PRIVATE 7 /* implementation defined function */
+
+#define MODE_BCLIENT 8 /* a pseudo mode, used internally */
+#define MODE_MCLIENT 9 /* multicast mode, used internally */
+
+/*
+ * Values for peer.stratum, sys_stratum
+ */
+#define STRATUM_REFCLOCK ((u_char)0) /* stratum claimed by primary clock */
+#define STRATUM_PRIMARY ((u_char)1) /* host has a primary clock */
+#define STRATUM_INFIN ((u_char)NTP_MAXSTRATUM) /* infinity a la Bellman-Ford */
+/* A stratum of 0 in the packet is mapped to 16 internally */
+#define STRATUM_PKT_UNSPEC ((u_char)0) /* unspecified in packet */
+#define STRATUM_UNSPEC ((u_char)(NTP_MAXSTRATUM+(u_char)1)) /* unspecified */
+
+/*
+ * Values for peer.flags
+ */
+#define FLAG_CONFIG 0x1 /* association was configured */
+#define FLAG_AUTHENABLE 0x2 /* this guy needs authentication */
+#define FLAG_MCAST1 0x4 /* multicast client/server mode */
+#define FLAG_MCAST2 0x8 /* multicast client mode */
+#define FLAG_AUTHENTIC 0x10 /* last message was authentic */
+#define FLAG_REFCLOCK 0x20 /* this is actually a reference clock */
+#define FLAG_SYSPEER 0x40 /* this is one of the selected peers */
+#define FLAG_PREFER 0x80 /* this is the preferred peer */
+
+/*
+ * Definitions for the clear() routine. We use memset() to clear
+ * the parts of the peer structure which go to zero. These are
+ * used to calculate the start address and length of the area.
+ */
+#define CLEAR_TO_ZERO(p) ((char *)&((p)->clear_to_zero))
+#define END_CLEAR_TO_ZERO(p) ((char *)&((p)->end_clear_to_zero))
+#define LEN_CLEAR_TO_ZERO (END_CLEAR_TO_ZERO((struct peer *)0) \
+ - CLEAR_TO_ZERO((struct peer *)0))
+/*
+ * Reference clock identifiers (for pps signal)
+ */
+#define PPSREFID (U_LONG)"PPS " /* used when pps controls stratum > 1 */
+
+/*
+ * Reference clock types. Added as necessary.
+ */
+#define REFCLK_NONE 0 /* unknown or missing */
+#define REFCLK_LOCALCLOCK 1 /* external (e.g., lockclock) */
+#define REFCLK_GPS_TRAK 2 /* TRAK 8810 GPS Receiver */
+#define REFCLK_WWV_PST 3 /* PST/Traconex 1020 WWV/H */
+#define REFCLK_WWVB_SPECTRACOM 4 /* Spectracom 8170/Netclock WWVB */
+#define REFCLK_GOES_TRUETIME 5 /* TrueTime 468-DC GOES */
+#define REFCLK_IRIG_AUDIO 6 /* IRIG-B audio decoder */
+#define REFCLK_CHU 7 /* scratchbuilt CHU (Canada) */
+#define REFCLK_PARSE 8 /* generic driver (usually DCF77,GPS) */
+#define REFCLK_GPS_MX4200 9 /* Magnavox MX4200 GPS */
+#define REFCLK_GPS_AS2201 10 /* Austron 2201A GPS */
+#define REFCLK_OMEGA_TRUETIME 11 /* TrueTime OM-DC OMEGA */
+#define REFCLK_IRIG_TPRO 12 /* KSI/Odetics TPRO-S IRIG */
+#define REFCLK_ATOM_LEITCH 13 /* Leitch CSD 5300 Master Clock */
+#define REFCLK_MSF_EES 14 /* EES M201 MSF Receiver */
+#define REFCLK_GPSTM_TRUETIME 15 /* TrueTime GPS/TM-TMD Receiver */
+#define REFCLK_IRIG_BANCOMM 16 /* Bancomm GPS/IRIG Interface */
+#define REFCLK_GPS_DATUM 17 /* Datum Programmable Time System */
+#define REFCLK_NIST_ACTS 18 /* NIST Auto Computer Time Service */
+#define REFCLK_WWV_HEATH 19 /* Heath GC1000 WWV/WWVH Receiver */
+#define REFCLK_GPS_NMEA 20 /* NMEA based GPS clock */
+#define REFCLK_GPS_MOTO 21 /* Motorola GPS clock */
+#define REFCLK_ATOM_PPS 22 /* 1-PPS Clock Discipline */
+#define REFCLK_MAX 24 /* maximum index (room to expand) */
+
+/*
+ * We tell reference clocks from real peers by giving the reference
+ * clocks an address of the form 127.127.t.u, where t is the type and
+ * u is the unit number. We define some of this here since we will need
+ * some sanity checks to make sure this address isn't interpretted as
+ * that of a normal peer.
+ */
+#define REFCLOCK_ADDR 0x7f7f0000 /* 127.127.0.0 */
+#define REFCLOCK_MASK 0xffff0000 /* 255.255.0.0 */
+
+#define ISREFCLOCKADR(srcadr) ((SRCADR(srcadr) & REFCLOCK_MASK) \
+ == REFCLOCK_ADDR)
+
+/*
+ * Macro for checking for invalid addresses. This is really, really
+ * gross, but is needed so no one configures a host on net 127 now that
+ * we're encouraging it the the configuration file.
+ */
+#define LOOPBACKADR 0x7f000001
+#define LOOPNETMASK 0xff000000
+
+#define ISBADADR(srcadr) (((SRCADR(srcadr) & LOOPNETMASK) \
+ == (LOOPBACKADR & LOOPNETMASK)) \
+ && (SRCADR(srcadr) != LOOPBACKADR))
+
+/*
+ * Utilities for manipulating addresses and port numbers
+ */
+#define NSRCADR(src) ((src)->sin_addr.s_addr) /* address in net byte order */
+#define NSRCPORT(src) ((src)->sin_port) /* port in net byte order */
+#define SRCADR(src) (ntohl(NSRCADR((src)))) /* address in host byte order */
+#define SRCPORT(src) (ntohs(NSRCPORT((src)))) /* host port */
+
+/*
+ * NTP packet format. The mac field is optional. It isn't really
+ * an l_fp either, but for now declaring it that way is convenient.
+ * See Appendix A in the specification.
+ *
+ * Note that all u_fp and l_fp values arrive in network byte order
+ * and must be converted (except the mac, which isn't, really).
+ */
+struct pkt {
+ u_char li_vn_mode; /* contains leap indicator, version and mode */
+ u_char stratum; /* peer's stratum */
+ u_char ppoll; /* the peer polling interval */
+ s_char precision; /* peer clock precision */
+ s_fp rootdelay; /* distance to primary clock */
+ u_fp rootdispersion; /* clock dispersion */
+ U_LONG refid; /* reference clock ID */
+ l_fp reftime; /* time peer clock was last updated */
+ l_fp org; /* originate time stamp */
+ l_fp rec; /* receive time stamp */
+ l_fp xmt; /* transmit time stamp */
+
+#define MIN_MAC_LEN (sizeof(U_LONG) + 8) /* DES */
+#define MAX_MAC_LEN (sizeof(U_LONG) + 16) /* MD5 */
+
+ U_LONG keyid; /* key identification */
+ u_char mac[MAX_MAC_LEN-sizeof(U_LONG)];/* message-authentication code */
+ /*l_fp mac;*/
+};
+
+/*
+ * Packets can come in two flavours, one with a mac and one without.
+ */
+#define LEN_PKT_NOMAC (sizeof(struct pkt) - MAX_MAC_LEN)
+
+/*
+ * Minimum size of packet with a MAC: has to include at least a key number.
+ */
+#define LEN_PKT_MAC (LEN_PKT_NOMAC + sizeof(U_LONG))
+
+/*
+ * Stuff for extracting things from li_vn_mode
+ */
+#define PKT_MODE(li_vn_mode) ((u_char)((li_vn_mode) & 0x7))
+#define PKT_VERSION(li_vn_mode) ((u_char)(((li_vn_mode) >> 3) & 0x7))
+#define PKT_LEAP(li_vn_mode) ((u_char)(((li_vn_mode) >> 6) & 0x3))
+
+/*
+ * Stuff for putting things back into li_vn_mode
+ */
+#define PKT_LI_VN_MODE(li, vn, md) \
+ ((u_char)((((li) << 6) & 0xc0) | (((vn) << 3) & 0x38) | ((md) & 0x7)))
+
+
+/*
+ * Dealing with stratum. 0 gets mapped to 16 incoming, and back to 0
+ * on output.
+ */
+#define PKT_TO_STRATUM(s) ((u_char)(((s) == (STRATUM_PKT_UNSPEC)) ?\
+ (STRATUM_UNSPEC) : (s)))
+
+#define STRATUM_TO_PKT(s) ((u_char)(((s) == (STRATUM_UNSPEC)) ?\
+ (STRATUM_PKT_UNSPEC) : (s)))
+
+/*
+ * Format of a recvbuf. These are used by the asynchronous receive
+ * routine to store incoming packets and related information.
+ */
+
+/*
+ * the maximum length NTP packet is a full length NTP control message with
+ * the maximum length message authenticator. I hate to hard-code 468 and 12,
+ * but only a few modules include ntp_control.h...
+ */
+#define RX_BUFF_SIZE (468+12+MAX_MAC_LEN)
+
+struct recvbuf {
+ struct recvbuf *next; /* next buffer in chain */
+ union {
+ struct sockaddr_in X_recv_srcadr;
+ caddr_t X_recv_srcclock;
+ } X_from_where;
+#define recv_srcadr X_from_where.X_recv_srcadr
+#define recv_srcclock X_from_where.X_recv_srcclock
+ struct sockaddr_in srcadr; /* where packet came from */
+ struct interface *dstadr; /* interface datagram arrived thru */
+ int fd; /* fd on which it was received */
+ l_fp recv_time; /* time of arrival */
+ void (*receiver)(); /* routine to receive buffer */
+ int recv_length; /* number of octets received */
+ union {
+ struct pkt X_recv_pkt;
+ char X_recv_buffer[RX_BUFF_SIZE];
+ } recv_space;
+#define recv_pkt recv_space.X_recv_pkt
+#define recv_buffer recv_space.X_recv_buffer
+};
+
+
+/*
+ * Event codes. Used for reporting errors/events to the control module
+ */
+#define PEER_EVENT 0x80 /* this is a peer event */
+
+#define EVNT_UNSPEC 0
+#define EVNT_SYSRESTART 1
+#define EVNT_SYSFAULT 2
+#define EVNT_SYNCCHG 3
+#define EVNT_PEERSTCHG 4
+#define EVNT_CLOCKRESET 5
+#define EVNT_BADDATETIM 6
+#define EVNT_CLOCKEXCPT 7
+
+#define EVNT_PEERIPERR (1|PEER_EVENT)
+#define EVNT_PEERAUTH (2|PEER_EVENT)
+#define EVNT_UNREACH (3|PEER_EVENT)
+#define EVNT_REACH (4|PEER_EVENT)
+#define EVNT_PEERCLOCK (5|PEER_EVENT)
+
+/*
+ * Clock event codes
+ */
+#define CEVNT_NOMINAL 0
+#define CEVNT_TIMEOUT 1
+#define CEVNT_BADREPLY 2
+#define CEVNT_FAULT 3
+#define CEVNT_PROP 4
+#define CEVNT_BADDATE 5
+#define CEVNT_BADTIME 6
+#define CEVNT_MAX CEVNT_BADTIME
+
+/*
+ * Very misplaced value. Default port through which we send traps.
+ */
+#define TRAPPORT 18447
+
+
+/*
+ * To speed lookups, peers are hashed by the low order bits of the remote
+ * IP address. These definitions relate to that.
+ */
+#define HASH_SIZE 32
+#define HASH_MASK (HASH_SIZE-1)
+#define HASH_ADDR(src) ((SRCADR((src))^(SRCADR((src))>>8)) & HASH_MASK)
+
+
+/*
+ * The poll update procedure takes an extra argument which controls
+ * how a random perturbation is applied to peer.timer. The choice is
+ * to not randomize at all, to randomize only if we're going to update
+ * peer.timer, and to randomize no matter what (almost, the algorithm
+ * is that we apply the random value if it is less than the current
+ * timer count).
+ */
+#define POLL_NOTRANDOM 0 /* don't randomize */
+#define POLL_RANDOMCHANGE 1 /* if you change, change randomly */
+#define POLL_MAKERANDOM 2 /* randomize next interval */
+
+
+/*
+ * How we randomize polls. The poll interval is a power of two.
+ * We chose a random value which is between 1/4 and 3/4 of the
+ * poll interval we would normally use and which is an even multiple
+ * of the EVENT_TIMEOUT. The random number routine, given an argument
+ * spread value of n, returns an integer between 0 and (1<<n)-1. This
+ * is shifted by EVENT_TIMEOUT and added to the base value.
+ */
+#define RANDOM_SPREAD(poll) ((poll) - (EVENT_TIMEOUT+1))
+#define RANDOM_POLL(poll, rval) ((((rval)+1)<<EVENT_TIMEOUT) + (1<<((poll)-2)))
+
+/*
+ * min, min3 and max. Makes it easier to transliterate the spec without
+ * thinking about it.
+ */
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#define min3(a,b,c) min(min((a),(b)), (c))
+
+
+/*
+ * Configuration items. These are for the protocol module (proto_config())
+ */
+#define PROTO_BROADCLIENT 1
+#define PROTO_PRECISION 2
+#define PROTO_AUTHENTICATE 3
+#define PROTO_BROADDELAY 4
+#define PROTO_AUTHDELAY 5
+#define PROTO_MULTICAST_ADD 6
+#define PROTO_MULTICAST_DEL 7
+#define PROTO_PLL 8
+#define PROTO_PPS 9
+#define PROTO_MONITOR 10
+#define PROTO_FILEGEN 11
+
+/*
+ * Configuration items for the loop filter
+ */
+#define LOOP_DRIFTCOMP 1 /* set frequency offset */
+#define LOOP_PPSDELAY 2 /* set pps delay */
+#define LOOP_PPSBAUD 3 /* set pps baud rate */
+
+/*
+ * Configuration items for the stats printer
+ */
+#define STATS_FREQ_FILE 1 /* configure drift file */
+#define STATS_STATSDIR 2 /* directory prefix for stats files */
+#define STATS_PID_FILE 3 /* configure xntpd PID file */
+
+#define MJD_1970 40587 /* MJD for 1 Jan 1970 */
+
+/*
+ * Default parameters. We use these in the absense of something better.
+ */
+#define DEFPRECISION (-7) /* default precision (~10 ms) */
+#define DEFBROADDELAY 0x00000100 /* default broadcast offset */
+ /* (~4 ms as s_fp) */
+#define DEFAUTHDELAY 0x00080000 /* default authentcation delay */
+ /* (~100 us as l_fp.u_f) */
+#define INADDR_NTP 0xe0000101 /* NTP multicast address 224.0.1.1 */
+/*
+ * Structure used optionally for monitoring when this is turned on.
+ */
+struct mon_data {
+ struct mon_data *hash_next; /* next structure in hash list */
+ struct mon_data *mru_next; /* next structure in MRU list */
+ struct mon_data *mru_prev; /* previous structure in MRU list */
+ struct mon_data *fifo_next; /* next structure in FIFO list */
+ struct mon_data *fifo_prev; /* previous structure in FIFO list */
+ u_long lastdrop; /* last time dropped due to RES_LIMIT*/
+ u_long lasttime; /* last time data updated */
+ u_long firsttime; /* time structure initialized */
+ u_long count; /* count we have seen */
+ U_LONG rmtadr; /* address of remote host */
+ struct interface *interface; /* interface on which this arrived */
+ u_short rmtport; /* remote port last came from */
+ u_char mode; /* mode of incoming packet */
+ u_char version; /* version of incoming packet */
+ u_char cast_flags; /* flags MDF_?CAST */
+};
+
+#define MDF_UCAST 1 /* unicast packet */
+#define MDF_MCAST 2 /* multicast packet */
+#define MDF_BCAST 4 /* broadcast packet */
+#define MDF_LCAST 8 /* local packet */
+
+/*
+ * Values used with mon_enabled to indicate reason for enabling monitoring
+ */
+#define MON_OFF 0x00 /* no monitoring */
+#define MON_ON 0x01 /* monitoring explicitly enabled */
+#define MON_RES 0x02 /* implicit monitoring for RES_LIMITED */
+/*
+ * Structure used for restrictlist entries
+ */
+struct restrictlist {
+ struct restrictlist *next; /* link to next entry */
+ U_LONG addr; /* host address (host byte order) */
+ U_LONG mask; /* mask for address (host byte order) */
+ u_long count; /* number of packets matched */
+ u_short flags; /* accesslist flags */
+ u_short mflags; /* match flags */
+};
+
+/*
+ * Access flags
+ */
+#define RES_IGNORE 0x1 /* ignore if matched */
+#define RES_DONTSERVE 0x2 /* don't give him any time */
+#define RES_DONTTRUST 0x4 /* don't trust if matched */
+#define RES_NOQUERY 0x8 /* don't allow queries if matched */
+#define RES_NOMODIFY 0x10 /* don't allow him to modify server */
+#define RES_NOPEER 0x20 /* don't allocate memory resources */
+#define RES_NOTRAP 0x40 /* don't allow him to set traps */
+#define RES_LPTRAP 0x80 /* traps set by him are low priority */
+#define RES_LIMITED 0x100 /* limit per net number of clients */
+
+#define RES_ALLFLAGS \
+ (RES_IGNORE|RES_DONTSERVE|RES_DONTTRUST|RES_NOQUERY\
+ |RES_NOMODIFY|RES_NOPEER|RES_NOTRAP|RES_LPTRAP|RES_LIMITED)
+
+/*
+ * Match flags
+ */
+#define RESM_INTERFACE 0x1 /* this is an interface */
+#define RESM_NTPONLY 0x2 /* match ntp port only */
+
+/*
+ * Restriction configuration ops
+ */
+#define RESTRICT_FLAGS 1 /* add flags to restrict entry */
+#define RESTRICT_UNFLAG 2 /* remove flags from restrict entry */
+#define RESTRICT_REMOVE 3 /* remove a restrict entry */
+
+
+/*
+ * Experimental alternate selection algorithm identifiers
+ */
+#define SELECT_1 1
+#define SELECT_2 2
+#define SELECT_3 3
+#define SELECT_4 4
+#define SELECT_5 5
+
+/*
+ * Endpoint structure for the select algorithm
+ */
+struct endpoint {
+ s_fp val; /* offset of endpoint */
+ int type; /* interval entry/exit */
+};
diff --git a/usr.sbin/xntpd/include/ntp_calendar.h b/usr.sbin/xntpd/include/ntp_calendar.h
new file mode 100644
index 0000000..461aee4
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_calendar.h
@@ -0,0 +1,80 @@
+/*
+ * ntp_calendar.h - definitions for the calendar time-of-day routine
+ */
+
+#include "ntp_types.h"
+
+struct calendar {
+ u_short year; /* year (A.D.) */
+ u_short yearday; /* day of year, 1 = January 1 */
+ u_char month; /* month, 1 = January */
+ u_char monthday; /* day of month */
+ u_char hour; /* hour of day, midnight = 0 */
+ u_char minute; /* minute of hour */
+ u_char second; /* second of minute */
+};
+
+/*
+ * Days in each month. 30 days hath September...
+ */
+#define JAN 31
+#define FEB 28
+#define FEBLEAP 29
+#define MAR 31
+#define APR 30
+#define MAY 31
+#define JUN 30
+#define JUL 31
+#define AUG 31
+#define SEP 30
+#define OCT 31
+#define NOV 30
+#define DEC 31
+
+/*
+ * We deal in a 4 year cycle starting at March 1, 1900. We assume
+ * we will only want to deal with dates since then, and not to exceed
+ * the rollover day in 2036.
+ */
+#define SECSPERMIN (60) /* seconds per minute */
+#define MINSPERHR (60) /* minutes per hour */
+#define HRSPERDAY (24) /* hours per day */
+#define DAYSPERYEAR (365) /* days per year */
+
+#define SECSPERDAY (SECSPERMIN*MINSPERHR*HRSPERDAY)
+#define SECSPERYEAR (365 * SECSPERDAY) /* regular year */
+#define SECSPERLEAPYEAR (366 * SECSPERDAY) /* leap year */
+
+#define MAR1900 ((JAN+FEB) * SECSPERDAY) /* no leap year in 1900 */
+#define DAYSPERCYCLE (365+365+365+366) /* 3 normal years plus leap */
+#define SECSPERCYCLE (DAYSPERCYCLE*SECSPERDAY)
+#define YEARSPERCYCLE 4
+
+/*
+ * Gross hacks. I have illicit knowlege that there won't be overflows
+ * here, the compiler often can't tell this.
+ */
+#define TIMES60(val) ((((val)<<4) - (val))<<2) /* *(16 - 1) * 4 */
+#define TIMES24(val) (((val)<<4) + ((val)<<3)) /* *16 + *8 */
+#define TIMES7(val) (((val)<<3) - (val)) /* *8 - *1 */
+#define TIMESDPERC(val) (((val)<<10) + ((val)<<8) \
+ + ((val)<<7) + ((val)<<5) \
+ + ((val)<<4) + ((val)<<2) + (val)) /* *big* hack */
+
+/*
+ * Another big hack. Cycle 22 started on March 1, 1988. This is
+ * STARTCYCLE22 seconds after the start of cycle 0.
+ */
+#define CYCLE22 (22)
+#define STARTCYCLE22 (u_long)(0xa586b500) /* 2777068800 */
+#define MAR1988 (u_long)(STARTCYCLE22 + (u_long)MAR1900)
+
+/*
+ * The length of January + February in leap and non-leap years.
+ */
+#define JANFEBNOLEAP ((JAN+FEB) * SECSPERDAY)
+#define JANFEBLEAP ((JAN+FEBLEAP) * SECSPERDAY)
+
+extern void caljulian P((u_long, struct calendar *));
+extern u_long caltontp P((const struct calendar *));
+
diff --git a/usr.sbin/xntpd/include/ntp_control.h b/usr.sbin/xntpd/include/ntp_control.h
new file mode 100644
index 0000000..1124bb0
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_control.h
@@ -0,0 +1,253 @@
+/*
+ * ntp_control.h - definitions related to NTP mode 6 control messages
+ */
+
+#include "ntp_types.h"
+
+struct ntp_control {
+ u_char li_vn_mode; /* leap, version, mode */
+ u_char r_m_e_op; /* response, more, error, opcode */
+ u_short sequence; /* sequence number of request */
+ u_short status; /* status word for association */
+ u_short associd; /* association ID */
+ u_short offset; /* offset of this batch of data */
+ u_short count; /* count of data in this packet */
+ u_char data[(480 + MAX_MAC_LEN)]; /* data + auth */
+};
+
+/*
+ * Length of the control header, in octets
+ */
+#define CTL_HEADER_LEN 12
+#define CTL_MAX_DATA_LEN 468
+
+
+/*
+ * Limits and things
+ */
+#define CTL_MAXTRAPS 3 /* maximum number of traps we allow */
+#define CTL_TRAPTIME (60*60) /* time out traps in 1 hour */
+#define CTL_MAXAUTHSIZE 64 /* maximum size of an authen'ed req */
+
+/*
+ * Decoding for the r_m_e_op field
+ */
+#define CTL_RESPONSE 0x80
+#define CTL_ERROR 0x40
+#define CTL_MORE 0x20
+#define CTL_OP_MASK 0x1f
+
+#define CTL_ISRESPONSE(r_m_e_op) (((r_m_e_op) & 0x80) != 0)
+#define CTL_ISMORE(r_m_e_op) (((r_m_e_op) & 0x20) != 0)
+#define CTL_ISERROR(r_m_e_op) (((r_m_e_op) & 0x40) != 0)
+#define CTL_OP(r_m_e_op) ((r_m_e_op) & CTL_OP_MASK)
+
+/*
+ * Opcodes
+ */
+#define CTL_OP_UNSPEC 0
+#define CTL_OP_READSTAT 1
+#define CTL_OP_READVAR 2
+#define CTL_OP_WRITEVAR 3
+#define CTL_OP_READCLOCK 4
+#define CTL_OP_WRITECLOCK 5
+#define CTL_OP_SETTRAP 6
+#define CTL_OP_ASYNCMSG 7
+#define CTL_OP_UNSETTRAP 31
+
+/*
+ * {En,De}coding of the system status word
+ */
+#define CTL_SST_TS_UNSPEC 0 /* time source unspecified */
+#define CTL_SST_TS_ATOM 1 /* time source calibrated atomic */
+#define CTL_SST_TS_LF 2 /* time source VLF or LF radio */
+#define CTL_SST_TS_HF 3 /* time source HF radio */
+#define CTL_SST_TS_UHF 4 /* time source UHF radio */
+#define CTL_SST_TS_LOCAL 5 /* time source LOCAL */
+#define CTL_SST_TS_NTP 6 /* time source NTP */
+#define CTL_SST_TS_UDPTIME 7 /* time source UDP/TIME */
+#define CTL_SST_TS_WRSTWTCH 8 /* time source is wristwatch */
+#define CTL_SST_TS_TELEPHONE 9 /* time source is telephone modem */
+#define CTL_SST_TS_PPS 0x20 /* time source is PPS signal */
+
+#define CTL_SYS_MAXEVENTS 15
+
+#define CTL_SYS_STATUS(li, source, nevnt, evnt) \
+ (((((unsigned short)(li))<< 14)&0xc000) | \
+ (((source)<<8)&0x3f00) | \
+ (((nevnt)<<4)&0x00f0) | \
+ ((evnt)&0x000f))
+
+#define CTL_SYS_LI(status) (((status)>>14) & 0x3)
+#define CTL_SYS_SOURCE(status) (((status)>>8) & 0x3f)
+#define CTL_SYS_NEVNT(status) (((status)>>4) & 0xf)
+#define CTL_SYS_EVENT(status) ((status) & 0xf)
+
+/*
+ * {En,De}coding of the peer status word
+ */
+#define CTL_PST_CONFIG 0x80
+#define CTL_PST_AUTHENABLE 0x40
+#define CTL_PST_AUTHENTIC 0x20
+#define CTL_PST_REACH 0x10
+#define CTL_PST_UNSPEC 0x08
+
+#define CTL_PST_SEL_REJECT 0 /* rejected */
+#define CTL_PST_SEL_SANE 1 /* passed sanity checks */
+#define CTL_PST_SEL_CORRECT 2 /* passed correctness checks */
+#define CTL_PST_SEL_SELCAND 3 /* passed candidate checks */
+#define CTL_PST_SEL_SYNCCAND 4 /* passed outlyer checks */
+#define CTL_PST_SEL_DISTSYSPEER 5 /* selected, distance exceeded */
+#define CTL_PST_SEL_SYSPEER 6 /* selected */
+#define CTL_PST_SEL_PPS 7 /* selected, pps signal override */
+
+#define CTL_PEER_MAXEVENTS 15
+
+#define CTL_PEER_STATUS(status, nevnt, evnt) \
+ ((((status)<<8) & 0xff00) | \
+ (((nevnt)<<4) & 0x00f0) | \
+ ((evnt) & 0x000f))
+
+#define CTL_PEER_STATVAL(status)(((status)>>8) & 0xff)
+#define CTL_PEER_NEVNT(status) (((status)>>4) & 0xf)
+#define CTL_PEER_EVENT(status) ((status) & 0xf)
+
+/*
+ * {En,De}coding of the clock status word
+ */
+#define CTL_CLK_OKAY 0
+#define CTL_CLK_NOREPLY 1
+#define CTL_CLK_BADFORMAT 2
+#define CTL_CLK_FAULT 3
+#define CTL_CLK_PROPAGATION 4
+#define CTL_CLK_BADDATE 5
+#define CTL_CLK_BADTIME 6
+
+#define CTL_CLK_STATUS(status, event) \
+ ((((status)<<8) & 0xff00) | \
+ ((event) & 0x00ff))
+
+/*
+ * Error code responses returned when the E bit is set.
+ */
+#define CERR_UNSPEC 0
+#define CERR_PERMISSION 1
+#define CERR_BADFMT 2
+#define CERR_BADOP 3
+#define CERR_BADASSOC 4
+#define CERR_UNKNOWNVAR 5
+#define CERR_BADVALUE 6
+#define CERR_RESTRICT 7
+
+#define CERR_NORESOURCE CERR_PERMISSION /* wish there was a different code */
+
+
+/*
+ * System variables we understand
+ */
+#define CS_LEAP 1
+#define CS_STRATUM 2
+#define CS_PRECISION 3
+#define CS_ROOTDELAY 4
+#define CS_ROOTDISPERSION 5
+#define CS_REFID 6
+#define CS_REFTIME 7
+#define CS_POLL 8
+#define CS_PEERID 9
+#define CS_OFFSET 10
+#define CS_DRIFT 11
+#define CS_COMPLIANCE 12
+#define CS_CLOCK 13
+#define CS_LEAPIND 14
+#define CS_LEAPWARNING 15
+#define CS_PROCESSOR 16
+#define CS_SYSTEM 17
+#define CS_KEYID 18
+#define CS_REFSKEW 19
+#define CS_VARLIST 20
+
+#define CS_MAXCODE CS_VARLIST
+
+/*
+ * Peer variables we understand
+ */
+#define CP_CONFIG 1
+#define CP_AUTHENABLE 2
+#define CP_AUTHENTIC 3
+#define CP_SRCADR 4
+#define CP_SRCPORT 5
+#define CP_DSTADR 6
+#define CP_DSTPORT 7
+#define CP_LEAP 8
+#define CP_HMODE 9
+#define CP_STRATUM 10
+#define CP_PPOLL 11
+#define CP_HPOLL 12
+#define CP_PRECISION 13
+#define CP_ROOTDELAY 14
+#define CP_ROOTDISPERSION 15
+#define CP_REFID 16
+#define CP_REFTIME 17
+#define CP_ORG 18
+#define CP_REC 19
+#define CP_XMT 20
+#define CP_REACH 21
+#define CP_VALID 22
+#define CP_TIMER 23
+#define CP_DELAY 24
+#define CP_OFFSET 25
+#define CP_DISPERSION 26
+#define CP_KEYID 27
+#define CP_FILTDELAY 28
+#define CP_FILTOFFSET 29
+#define CP_PMODE 30
+#define CP_RECEIVED 31
+#define CP_SENT 32
+#define CP_FILTERROR 33
+#define CP_FLASH 34
+#define CP_DISP 35
+#define CP_VARLIST 36
+
+#define CP_MAXCODE CP_VARLIST
+
+/*
+ * Clock variables we understand
+ */
+#define CC_TYPE 1
+#define CC_TIMECODE 2
+#define CC_POLL 3
+#define CC_NOREPLY 4
+#define CC_BADFORMAT 5
+#define CC_BADDATA 6
+#define CC_FUDGETIME1 7
+#define CC_FUDGETIME2 8
+#define CC_FUDGEVAL1 9
+#define CC_FUDGEVAL2 10
+#define CC_FLAGS 11
+#define CC_DEVICE 12
+#define CC_VARLIST 13
+
+#define CC_MAXCODE CC_VARLIST
+
+/*
+ * Definition of the structure used internally to hold trap information.
+ * ntp_request.c wants to see this.
+ */
+struct ctl_trap {
+ struct sockaddr_in tr_addr; /* address of trap recipient */
+ struct interface *tr_localaddr; /* interface to send this through */
+ u_long tr_settime; /* time trap was set */
+ u_long tr_count; /* async messages sent to this guy */
+ u_long tr_origtime; /* time trap was originally set */
+ u_long tr_resets; /* count of resets for this trap */
+ u_short tr_sequence; /* trap sequence id */
+ u_char tr_flags; /* trap flags */
+ u_char tr_version; /* version number of trapper */
+};
+
+/*
+ * Flag bits
+ */
+#define TRAP_INUSE 0x1 /* this trap is active */
+#define TRAP_NONPRIO 0x2 /* this trap is non-priority */
+#define TRAP_CONFIGURED 0x4 /* this trap was configured */
diff --git a/usr.sbin/xntpd/include/ntp_datum.h b/usr.sbin/xntpd/include/ntp_datum.h
new file mode 100644
index 0000000..2aa2cb7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_datum.h
@@ -0,0 +1,30 @@
+struct btfp_time /* Structure for reading 5 time words */
+ /* in one ioctl(2) operation. */
+{
+ unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
+};
+
+/***** Simple ioctl commands *****/
+
+#define RUNLOCK _IO('X',19) /* Release Capture Lockout */
+#define RCR0 _IOR('X',22,unsigned int) /* Read control register */
+#define WCR0 _IOW('X',23,unsigned int) /* Write control register */
+
+/***** Compound ioctl commands *****/
+
+/* Read all 5 time words in one call. */
+#define READTIME _IOR('X',32,struct btfp_time)
+#define VMEFD "/dev/btfp0"
+
+ struct vmedate { /* structure returned by get_vmetime.c */
+ unsigned short year;
+ unsigned short doy;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ unsigned long frac;
+ unsigned short status;
+ };
+
+#define PRIO 120 /* set the realtime priority */
+#define NREGS 7 /* number of registers we will use */
diff --git a/usr.sbin/xntpd/include/ntp_filegen.h b/usr.sbin/xntpd/include/ntp_filegen.h
new file mode 100644
index 0000000..9e75bc6
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_filegen.h
@@ -0,0 +1,51 @@
+/*
+ * ntp_filegen.h,v 3.8 1994/05/30 09:48:53 kardel Exp
+ *
+ * definitions for NTP file generations support
+ *
+ *
+ * Copyright (c) 1992
+ * Rainer Pruy Friedrich-Alexander Unuiversitaet Erlangen-Nuernberg
+ *
+ * This code may be modified and used freely
+ * provided the credits remain intact.
+ */
+
+#include "ntp_types.h"
+
+/*
+ * supported file generation types
+ */
+
+#define FILEGEN_NONE 255 /* no generations - use plain file name */
+#define FILEGEN_PID 1 /* one filegen per process incarnation */
+#define FILEGEN_DAY 2 /* one filegen per day */
+#define FILEGEN_WEEK 3 /* one filegen per week */
+#define FILEGEN_MONTH 4 /* one filegen per month */
+#define FILEGEN_YEAR 5 /* one filegen per year */
+#define FILEGEN_AGE 6 /* change filegen each FG_AGE_SECS */
+
+/*
+ * supported file generation flags
+ */
+
+#define FGEN_FLAG_LINK 0x01 /* make a link to base name */
+
+#define FGEN_FLAG_ENABLED 0x80 /* set this to really create files */
+ /* without this, open is suppressed */
+
+typedef struct FILEGEN
+ {
+ FILE *fp; /* file referring to current generation */
+ char *prefix; /* filename prefix and basename to be used*/
+ char *basename; /* for constructing filename of generation file */
+ /* WARNING: must be malloced !!! will be fed to free()*/
+ u_long id; /* id of current generation */
+ u_char type; /* type of file generation */
+ u_char flag; /* flags modifying processing of file generation */
+ } FILEGEN;
+
+extern void filegen_setup P((FILEGEN *, u_long));
+extern void filegen_config P((FILEGEN *, char *, u_int, u_int));
+extern FILEGEN *filegen_get P((char *));
+extern void filegen_register P((char *, FILEGEN *));
diff --git a/usr.sbin/xntpd/include/ntp_fp.h b/usr.sbin/xntpd/include/ntp_fp.h
new file mode 100644
index 0000000..ec657f7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_fp.h
@@ -0,0 +1,316 @@
+/*
+ * ntp_fp.h - definitions for NTP fixed point arithmetic
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "ntp_types.h"
+
+/*
+ * NTP uses two fixed point formats. The first (l_fp) is the "long" format
+ * and is 64 bits long with the decimal between bits 31 and 32. This
+ * is used for time stamps in the NTP packet header (in network byte
+ * order) and for internal computations of offsets (in local host byte
+ * order). We use the same structure for both signed and unsigned values,
+ * which is a big hack but saves rewriting all the operators twice. Just
+ * to confuse this, we also sometimes just carry the fractional part in
+ * calculations, in both signed and unsigned forms. Anyway, an l_fp looks
+ * like:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integral Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fractional Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+typedef struct {
+ union {
+ U_LONG Xl_ui;
+ LONG Xl_i;
+ } Ul_i;
+ union {
+ U_LONG Xl_uf;
+ LONG Xl_f;
+ } Ul_f;
+} l_fp;
+
+#define l_ui Ul_i.Xl_ui /* unsigned integral part */
+#define l_i Ul_i.Xl_i /* signed integral part */
+#define l_uf Ul_f.Xl_uf /* unsigned fractional part */
+#define l_f Ul_f.Xl_f /* signed fractional part */
+
+/*
+ * Fractional precision (of an l_fp) is actually the number of
+ * bits in a long.
+ */
+#define FRACTION_PREC (32)
+
+
+/*
+ * The second fixed point format is 32 bits, with the decimal between
+ * bits 15 and 16. There is a signed version (s_fp) and an unsigned
+ * version (u_fp). This is used to represent synchronizing distance
+ * and synchronizing dispersion in the NTP packet header (again, in
+ * network byte order) and internally to hold both distance and
+ * dispersion values (in local byte order). In network byte order
+ * it looks like:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+typedef LONG s_fp;
+typedef U_LONG u_fp;
+
+/*
+ * A unit second in fp format. Actually 2**(half_the_bits_in_a_long)
+ */
+#define FP_SECOND (0x10000)
+
+/*
+ * Byte order conversions
+ */
+#define HTONS_FP(x) (htonl(x))
+#define HTONL_FP(h, n) do { (n)->l_ui = htonl((h)->l_ui); \
+ (n)->l_uf = htonl((h)->l_uf); } while (0)
+#define NTOHS_FP(x) (ntohl(x))
+#define NTOHL_FP(n, h) do { (h)->l_ui = ntohl((n)->l_ui); \
+ (h)->l_uf = ntohl((n)->l_uf); } while (0)
+#define NTOHL_MFP(ni, nf, hi, hf) \
+ do { (hi) = ntohl(ni); (hf) = ntohl(nf); } while (0)
+#define HTONL_MFP(hi, hf, ni, nf) \
+ do { (ni) = ntohl(hi); (nf) = ntohl(hf); } while (0)
+
+/* funny ones. Converts ts fractions to net order ts */
+#define HTONL_UF(uf, nts) \
+ do { (nts)->l_ui = 0; (nts)->l_uf = htonl(uf); } while (0)
+#define HTONL_F(f, nts) do { (nts)->l_uf = htonl(f); \
+ if ((f) & 0x80000000) \
+ (nts)->l_i = -1; \
+ else \
+ (nts)->l_i = 0; \
+ } while (0)
+
+/*
+ * Conversions between the two fixed point types
+ */
+#define MFPTOFP(x_i, x_f) (((x_i)<<16) | (((x_f)>>16)&0xffff))
+#define LFPTOFP(v) MFPTOFP((v)->l_i, (v)->l_f)
+
+#define UFPTOLFP(x, v) ((v)->l_ui = (u_fp)(x)>>16, (v)->l_uf = (x)<<16)
+#define FPTOLFP(x, v) (UFPTOLFP((x), (v)), (x) < 0 ? (v)->l_ui -= 0x10000 : 0)
+
+/*
+ * Primitive operations on long fixed point values. If these are
+ * reminiscent of assembler op codes it's only because some may
+ * be replaced by inline assembler for particular machines someday.
+ * These are the (kind of inefficient) run-anywhere versions.
+ */
+#define M_NEG(v_i, v_f) /* v = -v */ \
+ do { \
+ if ((v_f) == 0) \
+ (v_i) = -(v_i); \
+ else { \
+ (v_f) = -(v_f); \
+ (v_i) = ~(v_i); \
+ } \
+ } while(0)
+
+#define M_NEGM(r_i, r_f, a_i, a_f) /* r = -a */ \
+ do { \
+ if ((a_f) == 0) { \
+ (r_f) = 0; \
+ (r_i) = -(a_i); \
+ } else { \
+ (r_f) = -(a_f); \
+ (r_i) = ~(a_i); \
+ } \
+ } while(0)
+
+#define M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += (a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } while (0)
+
+#define M_ADD3(r_ovr, r_i, r_f, a_ovr, a_i, a_f) /* r += a, three word */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ lo_tmp = ((r_i) & 0xffff) + ((a_i) & 0xffff); \
+ if (hi_tmp & 0x10000) \
+ lo_tmp++; \
+ hi_tmp = (((r_i) >> 16) & 0xffff) + (((a_i) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_i) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_ovr) += (a_ovr); \
+ if (hi_tmp & 0x10000) \
+ (r_ovr)++; \
+ } while (0)
+
+#define M_SUB(r_i, r_f, a_i, a_f) /* r -= a */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ if ((a_f) == 0) { \
+ (r_i) -= (a_i); \
+ } else { \
+ lo_tmp = ((r_f) & 0xffff) + ((-(a_f)) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) \
+ + (((-(a_f)) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += ~(a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } \
+ } while (0)
+
+#define M_RSHIFTU(v_i, v_f) /* v >>= 1, v is unsigned */ \
+ do { \
+ (v_f) = (U_LONG)(v_f) >> 1; \
+ if ((v_i) & 01) \
+ (v_f) |= 0x80000000; \
+ (v_i) = (U_LONG)(v_i) >> 1; \
+ } while (0)
+
+#define M_RSHIFT(v_i, v_f) /* v >>= 1, v is signed */ \
+ do { \
+ (v_f) = (U_LONG)(v_f) >> 1; \
+ if ((v_i) & 01) \
+ (v_f) |= 0x80000000; \
+ if ((v_i) & 0x80000000) \
+ (v_i) = ((v_i) >> 1) | 0x80000000; \
+ else \
+ (v_i) = (v_i) >> 1; \
+ } while (0)
+
+#define M_LSHIFT(v_i, v_f) /* v <<= 1 */ \
+ do { \
+ (v_i) <<= 1; \
+ if ((v_f) & 0x80000000) \
+ (v_i) |= 0x1; \
+ (v_f) <<= 1; \
+ } while (0)
+
+#define M_LSHIFT3(v_ovr, v_i, v_f) /* v <<= 1, with overflow */ \
+ do { \
+ (v_ovr) <<= 1; \
+ if ((v_i) & 0x80000000) \
+ (v_ovr) |= 0x1; \
+ (v_i) <<= 1; \
+ if ((v_f) & 0x80000000) \
+ (v_i) |= 0x1; \
+ (v_f) <<= 1; \
+ } while (0)
+
+#define M_ADDUF(r_i, r_f, uf) /* r += uf, uf is U_LONG fraction */ \
+ M_ADD((r_i), (r_f), 0, (uf)) /* let optimizer worry about it */
+
+#define M_SUBUF(r_i, r_f, uf) /* r -= uf, uf is U_LONG fraction */ \
+ M_SUB((r_i), (r_f), 0, (uf)) /* let optimizer worry about it */
+
+#define M_ADDF(r_i, r_f, f) /* r += f, f is a LONG fraction */ \
+ do { \
+ if ((f) > 0) \
+ M_ADD((r_i), (r_f), 0, (f)); \
+ else if ((f) < 0) \
+ M_ADD((r_i), (r_f), (-1), (f));\
+ } while(0)
+
+#define M_ISNEG(v_i, v_f) /* v < 0 */ \
+ (((v_i) & 0x80000000) != 0)
+
+#define M_ISHIS(a_i, a_f, b_i, b_f) /* a >= b unsigned */ \
+ (((U_LONG)(a_i)) > ((U_LONG)(b_i)) || \
+ ((a_i) == (b_i) && ((U_LONG)(a_f)) >= ((U_LONG)(b_f))))
+
+#define M_ISGEQ(a_i, a_f, b_i, b_f) /* a >= b signed */ \
+ (((LONG)(a_i)) > ((LONG)(b_i)) || \
+ ((a_i) == (b_i) && ((U_LONG)(a_f)) >= ((U_LONG)(b_f))))
+
+#define M_ISEQU(a_i, a_f, b_i, b_f) /* a == b unsigned */ \
+ ((a_i) == (b_i) && (a_f) == (b_f))
+
+/*
+ * Operations on the long fp format
+ */
+#define L_ADD(r, a) M_ADD((r)->l_ui, (r)->l_uf, (a)->l_ui, (a)->l_uf)
+#define L_SUB(r, a) M_SUB((r)->l_ui, (r)->l_uf, (a)->l_ui, (a)->l_uf)
+#define L_NEG(v) M_NEG((v)->l_ui, (v)->l_uf)
+#define L_ADDUF(r, uf) M_ADDUF((r)->l_ui, (r)->l_uf, (uf))
+#define L_SUBUF(r, uf) M_SUBUF((r)->l_ui, (r)->l_uf, (uf))
+#define L_ADDF(r, f) M_ADDF((r)->l_ui, (r)->l_uf, (f))
+#define L_RSHIFT(v) M_RSHIFT((v)->l_i, (v)->l_uf)
+#define L_RSHIFTU(v) M_RSHIFT((v)->l_ui, (v)->l_uf)
+#define L_LSHIFT(v) M_LSHIFT((v)->l_ui, (v)->l_uf)
+#define L_CLR(v) ((v)->l_ui = (v)->l_uf = 0)
+
+#define L_ISNEG(v) (((v)->l_ui & 0x80000000) != 0)
+#define L_ISZERO(v) ((v)->l_ui == 0 && (v)->l_uf == 0)
+#define L_ISHIS(a, b) ((a)->l_ui > (b)->l_ui || \
+ ((a)->l_ui == (b)->l_ui && (a)->l_uf >= (b)->l_uf))
+#define L_ISGEQ(a, b) ((a)->l_i > (b)->l_i || \
+ ((a)->l_i == (b)->l_i && (a)->l_uf >= (b)->l_uf))
+#define L_ISEQU(a, b) M_ISEQU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
+
+extern char * dofptoa P((u_fp, int, int, int));
+extern char * dolfptoa P((u_long, u_long, int, int, int));
+
+extern int atolfp P((const char *, l_fp *));
+extern int buftvtots P((const char *, l_fp *));
+extern void gettstamp P((l_fp *));
+extern char * fptoa P((s_fp, int));
+extern char * fptoms P((s_fp, int));
+extern char * fptoms P((s_fp, int));
+extern int hextolfp P((const char *, l_fp *));
+extern int mstolfp P((const char *, l_fp *));
+extern char * prettydate P((l_fp *));
+extern char * uglydate P((l_fp *));
+
+extern void get_systime P((l_fp *));
+extern int step_systime P((l_fp *));
+extern int step_systime_real P((l_fp *));
+extern int adj_systime P((l_fp *));
+
+#define lfptoa(_fpv, _ndec) mfptoa((_fpv)->l_ui, (_fpv)->l_uf, (_ndec))
+#define lfptoms(_fpv, _ndec) mfptoms((_fpv)->l_ui, (_fpv)->l_uf, (_ndec))
+
+#define ntoa(_sin) numtoa((_sin)->sin_addr.s_addr)
+#define ntohost(_sin) numtohost((_sin)->sin_addr.s_addr)
+
+#define ufptoa(_fpv, _ndec) dofptoa((_fpv), 0, (_ndec), 0)
+#define ufptoms(_fpv, _ndec) dofptoa((_fpv), 0, (_ndec), 1)
+#define ulfptoa(_fpv, _ndec) dolfptoa((_fpv)->l_ui, (_fpv)->l_uf, 0, (_ndec), 0)
+#define ulfptoms(_fpv, _ndec) dolfptoa((_fpv)->l_ui, (_fpv)->l_uf, 0, (_ndec), 1)
+#define umfptoa(_fpi, _fpf, _ndec) dolfptoa((_fpi), (_fpf), 0, (_ndec), 0)
diff --git a/usr.sbin/xntpd/include/ntp_if.h b/usr.sbin/xntpd/include/ntp_if.h
new file mode 100644
index 0000000..340481a
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_if.h
@@ -0,0 +1,51 @@
+/*
+ * Sockets are not standard.
+ * So hide uglyness in include file.
+ */
+#if defined(SYS_CONVEXOS9)
+#include "/sys/sync/queue.h"
+#include "/sys/sync/sema.h"
+#endif
+
+#if defined(SYS_AIX)
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#if (defined(SOLARIS) && !defined(bsd)) || defined(SYS_SUNOS4)
+#include <sys/sockio.h>
+#endif
+
+#if defined(SYS_UNIXWARE1)
+#include <sys/sockio.h>
+#endif
+
+#if defined(SYS_PTX) || defined(SYS_SINIXM)
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#endif
+
+#if defined(SYS_SVR4)
+#if !defined(USE_STREAMS_DEVICE_FOR_IF_CONFIG)
+#include <sys/sockio.h>
+#else /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+#include <netinet/ip.h>
+#undef SIOCGIFCONF
+#undef SIOCGIFFLAGS
+#undef SIOCGIFADDR
+#undef SIOCGIFBRDADDR
+#undef SIOCGIFNETMASK
+#define SIOCGIFCONF IPIOC_GETIFCONF
+#define SIOCGIFFLAGS IPIOC_GETIFFLAGS
+#define SIOCGIFADDR IPIOC_GETIFADDR
+#define SIOCGIFBRDADDR IPIOC_GETIFBRDADDR
+#define SIOCGIFNETMASK IPIOC_GETIFNETMASK
+#endif /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+
+#endif /* SYS_SVR4 */
+
+#if defined(SYS_FREEBSD)
+#include <sys/time.h>
+#endif
+
+#include <net/if.h>
diff --git a/usr.sbin/xntpd/include/ntp_in.h b/usr.sbin/xntpd/include/ntp_in.h
new file mode 100755
index 0000000..494051f
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_in.h
@@ -0,0 +1,259 @@
+/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */
+
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at 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'' without express or implied warranty.
+ */
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981.
+ */
+
+#ifndef _netinet_in_h
+#define _netinet_in_h
+#define _NETINET_IN_H_
+#define _SYS_IN_INCLUDED
+#define __IN_HEADER
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP 0 /* dummy for IP */
+#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* group control protocol */
+#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
+#define IPPROTO_ST 5 /* st */
+#define IPPROTO_TCP 6 /* tcp */
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#define IPPROTO_PUP 12 /* pup */
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#define IPPROTO_IDP 22 /* xns idp */
+#define IPPROTO_HELLO 63 /* "hello" routing protocol */
+#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */
+#define IPPROTO_OSPF 89 /* Open SPF IGP */
+
+#define IPPROTO_RAW 255 /* raw IP packet */
+#define IPPROTO_MAX 256
+
+/*
+ * Port/socket numbers: network standard functions
+ */
+#define IPPORT_ECHO 7
+#define IPPORT_DISCARD 9
+#define IPPORT_SYSTAT 11
+#define IPPORT_DAYTIME 13
+#define IPPORT_NETSTAT 15
+#define IPPORT_FTP 21
+#define IPPORT_TELNET 23
+#define IPPORT_SMTP 25
+#define IPPORT_TIMESERVER 37
+#define IPPORT_NAMESERVER 42
+#define IPPORT_WHOIS 43
+#define IPPORT_MTP 57
+
+/*
+ * Port/socket numbers: host specific functions
+ */
+#define IPPORT_TFTP 69
+#define IPPORT_RJE 77
+#define IPPORT_FINGER 79
+#define IPPORT_TTYLINK 87
+#define IPPORT_SUPDUP 95
+
+/*
+ * UNIX TCP sockets
+ */
+#define IPPORT_EXECSERVER 512
+#define IPPORT_LOGINSERVER 513
+#define IPPORT_CMDSERVER 514
+#define IPPORT_EFSSERVER 520
+
+/*
+ * UNIX UDP sockets
+ */
+#define IPPORT_BIFFUDP 512
+#define IPPORT_WHOSERVER 513
+#define IPPORT_ROUTESERVER 520 /* 520+1 also used */
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ * Ports > IPPORT_USERRESERVED are reserved
+ * for servers, not necessarily privileged.
+ */
+#define IPPORT_RESERVED 1024
+#define IPPORT_USERRESERVED 5000
+
+/*
+ * Link numbers
+ */
+#define IMPLINK_IP 155
+#define IMPLINK_LOWEXPER 156
+#define IMPLINK_HIGHEXPER 158
+
+/*
+ * Internet address
+ * This definition contains obsolete fields for compatibility
+ * with SunOS 3.x and 4.2bsd. The presence of subnets renders
+ * divisions into fixed fields misleading at best. New code
+ * should use only the s_addr field.
+ */
+struct in_addr {
+ union {
+ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+ struct { u_short s_w1,s_w2; } S_un_w;
+ u_long S_addr;
+ } S_un;
+#define s_addr S_un.S_addr /* should be used for all code */
+#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */
+#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */
+#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */
+#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */
+#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST 0x00ffffff
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST 0x0000ffff
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST 0x000000ff
+
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IN_MULTICAST(i) IN_CLASSD(i)
+
+#define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+
+#define INADDR_ANY (u_long)0x00000000
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */
+
+#define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */
+
+#define IN_LOOPBACKNET 127 /* official! */
+
+/*
+ * Define a macro to stuff the loopback address into an Internet address
+ */
+#define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET;}
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ short sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ */
+#define IP_OPTIONS 1 /* set/get IP per-packet options */
+#define IP_MULTICAST_IF 2 /* set/get IP multicast interface */
+#define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */
+#define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#if !defined(vax) && !defined(ntohl) && !defined(i386)
+/*
+ * Macros for number representation conversion.
+ */
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#endif
+
+#if !defined(ntohl) && (defined(vax) || defined(i386))
+u_short ntohs(), htons();
+u_long ntohl(), htonl();
+#endif
+
+#ifdef KERNEL
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
+struct in_addr in_makeaddr();
+u_long in_netof(), in_lnaof();
+#endif
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(i386)
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif BYTE_ORDER
+
+/*
+ * Macros for number representation conversion.
+ */
+#if BYTE_ORDER==LITTLE_ENDIAN
+#define NTOHL(d) ((d) = ntohl((d)))
+#define NTOHS(d) ((d) = ntohs((d)))
+#define HTONL(d) ((d) = htonl((d)))
+#define HTONS(d) ((d) = htons((d)))
+#else
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#define NTOHL(d)
+#define NTOHS(d)
+#define HTONL(d)
+#define HTONS(d)
+#endif
+
+#endif /*!_netinet_in_h*/
diff --git a/usr.sbin/xntpd/include/ntp_io.h b/usr.sbin/xntpd/include/ntp_io.h
new file mode 100644
index 0000000..ebe20b4
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_io.h
@@ -0,0 +1,25 @@
+/*
+ * POSIX says use <fnct.h> to get O_* symbols and
+ * SEEK_SET symbol form <untisd.h>.
+ */
+#if defined(NTP_POSIX_SOURCE)
+
+/*
+ * POSIX way
+ */
+#include <stdio.h>
+#if defined(HAVE_SIGNALED_IO) && (defined(SYS_AUX2) || defined(SYS_AUX3) || defined(SYS_PTX))
+#include <sys/file.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+#else
+/*
+ * BSD way
+ */
+#include <sys/file.h>
+#include <fcntl.h>
+#if !defined(SEEK_SET) && defined(L_SET)
+#define SEEK_SET L_SET
+#endif
+#endif
diff --git a/usr.sbin/xntpd/include/ntp_machine.h b/usr.sbin/xntpd/include/ntp_machine.h
new file mode 100644
index 0000000..0d4267c
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_machine.h
@@ -0,0 +1,742 @@
+/*
+ * Collect all machine dependent idiosyncrasies in one place.
+ */
+
+#ifndef __ntp_machine
+#define __ntp_machine
+
+/*
+ Various options.
+ They can defined with the DEFS directive in the Config file if they
+ are not defined here.
+
+WHICH NICE
+
+ HAVE_ATT_NICE - Use att nice(priority_change)
+ HAVE_BSD_NICE - Use bsd setprioirty(which, who, priority)
+ HAVE_NO_NICE - Don't have (or use) either
+
+KERNEL MUCKING - If you porting to a new system see xntpd/ntp_unixclock.c and
+ util/tickadj.c to see what these do. This is very system
+ dependent stuff!!!
+
+ HAVE_LIBKVM - Use libkvm to read kernal memory
+ HAVE_READKMEM - Use read to read kernal memory
+ NOKMEM - Don't read kmem
+ HAVE_N_UN - Have u_nn nlist struct.
+
+WHICH SETPGRP TO USE - Not needed if NTP_POSIX_SOURCE is defined since you
+ better of setsid!
+
+ HAVE_ATT_SETPGRP - setpgrp(void) instead of setpgrp(int, int)
+
+
+Signaled IO - Signled IO defines.
+
+ HAVE_SIGNALED_IO - Enable signaled io. Assumes you are going to use SIGIO
+ for tty and udp io.
+ USE_UDP_SIGPOLL - Use SIGPOLL on socket io. This assumes that the
+ sockets routines are defined on top of streams.
+ USE_TTY_SIGPOLL - Use SIGPOLL on tty io. This assumes streams.
+ UDP_BACKWARDS_SETOWN - SunOS 3.5 or Ultirx 2.0 system.
+
+
+WHICH TERMINAL MODEL TO USE - I would assume HAVE_TERMIOS if
+ NTP_POSIX_SOURCE was set but can't. The
+ posix tty driver is too restrictive on most systems.
+ It is defined if you define STREAMS.
+
+ We do not put these defines in the ntp_machine.h as some systems
+ offer multiple interfaces and refclock configuration likes to
+ peek into the configuration defines for tty model restrictions.
+ Thus all tty definitions should be in the files in the machines directory.
+
+ HAVE_TERMIOS - Use POSIX termios.h
+ HAVE_SYSV_TTYS - Use SYSV termio.h
+ HAVE_BSD_TTYS - Use BSD stty.h
+
+THIS MAKES PORTS TO NEW SYSTEMS EASY - You only have to wory about
+ kernel mucking.
+
+ NTP_POSIX_SOURCE - Use POSIX functions over bsd functions and att functions.
+ This is NOT the same as _POSIX_SOURCE.
+ It is much weaker!
+
+
+STEP SLEW OR TWO STEP - The Default is to step.
+
+ SLEWALWAYS - setttimeofday can not be used to set the time of day at
+ all.
+ STEP_SLEW - setttimeofday can not set the seconds part of time
+ time use setttimeofday to set the seconds part of the
+ time and the slew the seconds.
+ FORCE_NTPDATE_STEP - even if SLEWALWAYS is defined, force a step of
+ of the systemtime (via settimeofday()). Only takes
+ affect if STEP_SLEW isn't defined.
+
+WHICH TIMEOFDAY()
+
+ SYSV_TIMEOFDAY - [sg]ettimeofday(struct timeval *) as opposed to BSD
+ [sg]ettimeofday(struct timeval *, struct timezone *)
+
+INFO ON NEW KERNEL PLL SYS CALLS
+
+ NTP_SYSCALLS_STD - use the "normal" ones
+ NTP_SYSCALL_GET - SYS_ntp_gettime id
+ NTP_SYSCALL_ADJ - SYS_ntp_adjtime id
+ NTP_SYSCALLS_LIBC - ntp_adjtime() and ntp_gettime() are in libc.
+
+HOW TO GET IP INTERFACE INFORMATION
+
+ Some UNIX V.4 machines implement a sockets library on top of
+ streams. For these systems, you must use send the SIOCGIFCONF down
+ the stream in an I_STR ioctl. This ususally also implies
+ USE_STREAMS_DEVICE FOR IF_CONFIG. Dell UNIX is a notable exception.
+
+ STREAMS_TLI - use ioctl(I_STR) to implement ioctl(SIOCGIFCONF)
+
+WHAT DOES IOCTL(SIOCGIFCONF) RETURN IN THE BUFFER
+
+ UNIX V.4 machines implement a sockets library on top of streams.
+ When requesting the IP interface configuration with an ioctl(2) calll,
+ an array of ifreq structures are placed in the provided buffer. Some
+ implementations also place the length of the buffer information in
+ the first integer position of the buffer.
+
+ SIZE_RETURNED_IN_BUFFER - size integer is in the buffer
+
+WILL IOCTL(SIOCGIFCONF) WORK ON A SOCKET
+
+ Some UNIX V.4 machines do not appear to support ioctl() requests for the
+ IP interface configuration on a socket. They appear to require the use
+ of the streams device instead.
+
+ USE_STREAMS_DEVICE_FOR_IF_CONFIG - use the /dev/ip device for configuration
+
+MISC
+
+ USE_PROTOTYPES - Prototype functions
+ DOSYNCTODR - Resync TODR clock every hour.
+ RETSIGTYPE - Define signal function type.
+ NO_SIGNED_CHAR_DECL - No "signed char" see include/ntp.h
+ LOCK_PROCESS - Have plock.
+ UDP_WILDCARD_DELIVERY
+ - these systems deliver broadcast packets to the wildcard
+ port instead to a port bound to the interface bound
+ to the correct broadcast address - are these
+ implementations broken or did the spec change ?
+
+DEFINITIONS FOR SYSTEM && PROCESSOR
+ STR_SYSTEM - value of system variable
+ STR_PROCESSOR - value of processor variable
+
+You could just put the defines on the DEFS line in machines/<os> file.
+I don't since there are lots of different types of compilers that a system might
+have, some that can do proto typing and others that cannot on the same system.
+I get a chance to twiddle some of the configuration parameters at compile
+time based on compiler/machine combinations by using this include file.
+See convex, aix and sun configurations see how complex it get.
+
+Note that it _is_ considered reasonable to add some system-specific defines
+to the machine/<os> file if it would be too inconvenient to puzzle them out
+in this file.
+
+*/
+
+
+/*
+ * RS6000 running AIX.
+ */
+#if defined(SYS_AIX)
+#define HAVE_SIGNALED_IO
+#ifndef _BSD
+#define NTP_STDC
+#define NTP_POSIX_SOURCE
+/*
+ * Keep USE_PROTOTYPES and _NO_PROTO in step.
+ */
+#if defined(_NO_PROTO) && defined(USE_PROTOTYPES)
+#undef USE_PROTOTYPES
+#endif
+#if !defined(_NO_PROTO) && !defined(USE_PROTOTYPES)
+#define USE_PROTOTYPES
+#endif
+#endif /*_BSD */
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/AIX"
+#endif
+#endif /* RS6000 */
+
+/*
+ * SunOS 4.X.X
+ * Note: posix version has NTP_POSIX_SOURCE and HAVE_SIGNALED_IO
+ */
+#if defined(SYS_SUNOS4)
+#define NTP_NEED_BOPS
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_LIBKVM
+#define HAVE_MALLOC_H
+#define HAVE_BSD_NICE
+#define RETSIGTYPE void
+#define NTP_SYSCALL_GET 132
+#define NTP_SYSCALL_ADJ 147
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SunOS 4.x"
+#endif
+#endif
+
+/*
+ * Sinix-M
+ */
+#if defined(SYS_SINIXM)
+#undef HAVE_SIGNALED_IO
+#undef USE_TTY_SIGPOLL
+#undef USE_UDP_SIGPOLL
+#define STREAMS_TLI
+#define NO_SIGNED_CHAR_DECL
+#define STEP_SLEW /* TWO step */
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SINIX-M"
+#endif
+#endif
+
+/*
+ * SunOS 5.1 or SunOS 5.2 or Solaris 2.1 or Solaris 2.2
+ */
+#if defined(SYS_SOLARIS)
+#define HAVE_SIGNALED_IO
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define NO_SIGNED_CHAR_DECL
+#define STEP_SLEW /* TWO step */
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#define UDP_WILDCARD_DELIVERY
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Solaris 2.x"
+#endif
+#endif
+
+/*
+ * Convex
+ */
+#if defined(SYS_CONVEXOS10) || defined(SYS_CONVEXOS9)
+#define HAVE_SIGNALED_IO
+#define HAVE_N_UN
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#if defined(convex)
+#define RETSIGTYPE int
+#define NO_SIGNED_CHAR_DECL
+#else
+#if defined(__stdc__) && !defined(USE_PROTOTYPES)
+#define USE_PROTOTYPES
+#endif
+#if !defined(__stdc__) && defined(USE_PROTOTYPES)
+#undef USE_PROTOTYPES
+#endif
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/ConvexOS"
+#endif
+#endif
+
+/*
+ * IRIX 4.X and IRIX 5.x
+ */
+#if defined(SYS_IRIX4)||defined(SYS_IRIX5)
+#define HAVE_SIGNALED_IO
+#define USE_TTY_SIGPOLL
+#define ADJTIME_IS_ACCURATE
+#define LOCK_PROCESS
+#define USE_PROTOTYPES
+#define HAVE_ATT_SETPGRP
+#define HAVE_BSD_NICE
+#define NTP_POSIX_SOURCE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/IRIX"
+#endif
+#endif
+
+/*
+ * Ultrix
+ * Note: posix version has NTP_POSIX_SOURCE and HAVE_SIGNALED_IO
+ */
+#if defined(SYS_ULTRIX)
+#define S_CHAR_DEFINED
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#define RETSIGTYPE void
+#define NTP_SYSCALLS_STD
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Ultrix"
+#endif
+#endif
+
+/*
+ * AUX
+ */
+#if defined(SYS_AUX2) || defined(SYS_AUX3)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_ATT_NICE
+#define LOCK_PROCESS
+#define NTP_POSIX_SOURCE
+/*
+ * This requires that _POSIX_SOURCE be forced on the
+ * compiler command flag. We can't do it here since this
+ * file is included _after_ the system header files and we
+ * need to let _them_ know we're POSIX. We do this in
+ * compilers/aux3.gcc...
+ */
+#define SLEWALWAYS
+#define FORCE_NTPDATE_STEP
+#define RETSIGTYPE void
+#define HAVE_ATT_SETPGRP
+#define LOG_NTP LOG_LOCAL1
+#define HAVE_SIGNALED_IO
+#define NTP_NEED_BOPS
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/AUX"
+#endif
+#endif
+
+/*
+ * Next
+ */
+#if defined(SYS_NEXT)
+#define RETSIGTYPE void
+#define DOSYNCTODR
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#define HAVE_N_UN
+#undef NTP_POSIX_SOURCE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Next"
+#endif
+#endif
+
+/*
+ * HPUX
+ */
+#if defined(SYS_HPUX)
+#define NTP_POSIX_SOURCE
+#define HAVE_SIGNALED_IO
+#define getdtablesize() sysconf(_SC_OPEN_MAX)
+#define setlinebuf(f) setvbuf(f, NULL, _IOLBF, 0)
+#define NO_SIGNED_CHAR_DECL
+#define LOCK_PROCESS
+#define RETSIGTYPE void
+#if (SYS_HPUX < 9)
+#define HAVE_NO_NICE /* HPUX uses rtprio instead */
+#else
+#define HAVE_BSD_NICE /* new at 9.X */
+#endif
+#if (SYS_HPUX < 10)
+#define NOKMEM
+#else
+#define HAVE_READKMEM
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/HPUX"
+#endif
+#endif
+
+/*
+ * bsdi
+ */
+#if defined(SYS_BSDI)
+#define HAVE_SIGNALED_IO
+#define HAVE_LIBKVM
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/BSDI"
+#endif
+#endif
+
+/*
+ * 4.4 bsd
+ */
+#if defined(SYS_44BSD)
+#define HAVE_SIGNALED_IO
+#define HAVE_LIBKVM
+#define HAVE_SYSCTL
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#define USE_PROTOTYPES
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/4.4BSD"
+#endif
+#define MCAST
+#ifdef SYS_FREEBSD
+#ifdef HAVE_SIGNALED_IO
+#undef HAVE_SIGNALED_IO
+#endif
+#define HAVE_TERMIOS
+#define HAVE_UNAME
+#define HAVE_SYS_TIMEX_H
+#define HAVE_GETBOOTFILE
+#define NTP_SYSCALLS_LIBC
+#define KERNEL_PLL
+#endif
+#endif
+
+/*
+ * Linux
+ */
+#if defined(SYS_LINUX)
+#undef HAVE_SIGNALED_IO
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define ADJTIME_IS_ACCURATE
+#define HAVE_SYS_TIMEX_H
+/* hope there will be a standard interface
+ * along with a standard name one day ! */
+#define ntp_adjtime __adjtimex
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Linux"
+#endif
+#endif
+
+/*
+ * 386BSD and any variants 8-) - should really have only ONE define
+ * for this bunch.
+ */
+#if defined(SYS_386BSD) || defined(SYS_NETBSD)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/\052BSD"
+#endif
+#ifdef SYS_FREEBSD
+#define HAVE_TERMIOS
+#define HAVE_UNAME
+#define HAVE_SYS_TIMEX_H
+#define NTP_SYSCALLS_LIBC
+#define KERNEL_PLL
+#endif
+#endif
+
+/*
+ * DEC AXP OSF/1
+ */
+#if defined(SYS_DECOSF1)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define NTP_SYSCALLS_STD
+#define HAVE_BSD_NICE
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/DECOSF1"
+#endif
+#endif
+
+/*
+ * Intel x86 OSF/1
+ */
+#if defined(SYS_IX86OSF1)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define NTP_SYSCALLS_STD
+#define HAVE_BSD_NICE
+#define HAVE_MODEM_CONTROL
+#define SYS_DECOSF1
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/IX86OSF1"
+#endif
+#endif
+
+/*
+ * ISI
+ */
+#if defined(SYS_BSD)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_BSD_NICE
+#define HAVE_BSD_TTYS
+#define HAVE_READKMEM
+#define HAVE_SIGNALED_IO
+#define NEED_VSPRINTF
+#undef NTP_POSIX_SOURCE
+#endif
+
+/*
+ * I386
+ * XXX - what OS?
+ */
+#if defined(SYS_I386)
+#define HAVE_READKMEM
+#define S_CHAR_DEFINED
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/I386"
+#endif
+#endif
+
+/*
+ * Mips
+ */
+#if defined(SYS_MIPS)
+#define NOKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Mips"
+#endif
+#endif
+
+/*
+ * SEQUENT
+ */
+#if defined(SYS_SEQUENT)
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sequent Dynix 3"
+#endif
+#endif
+
+/*
+ * PTX
+ */
+#if defined(SYS_PTX)
+#define NO_SIGNED_CHAR_DECL
+#define STREAMS_TLI
+#define HAVE_ATT_SETPGRP
+#define HAVE_SIGNALED_IO
+#define USE_UDP_SIGPOLL
+#define USE_TTY_SIGPOLL
+#undef ADJTIME_IS_ACCURATE /* not checked yet */
+#define LOCK_PROCESS
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#define STEP_SLEW /* TWO step */
+#define SYSV_GETTIMEOFDAY
+#define HAVE_READKMEM
+#define UDP_WILDCARD_DELIVERY
+#define NTP_POSIX_SOURCE
+#define memmove(x, y, z) memcpy(x, y, z)
+struct timezone { int __0; }; /* unused placebo */
+/*
+ * no comment !@!
+ */
+typedef unsigned int u_int;
+#ifndef _NETINET_IN_SYSTM_INCLUDED /* i am about to comment... */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned long u_long;
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sequent PTX"
+#endif
+#endif
+
+
+/*
+ * Sony NEWS
+ */
+#if defined(SYS_SONY)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sony"
+#endif
+#endif
+
+/*
+ * VAX
+ * XXX - VMS?
+ */
+#if defined(SYS_VAX)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/VAX"
+#endif
+#endif
+
+/*
+ * UNIX V.4 on and NCR 3000
+ */
+#if defined(SYS_SVR4)
+#define HAVE_ATT_SETPGRP
+#define USE_PROTOTYPES
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_NICE
+#define HAVE_READKMEM
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define STREAM
+#define STEP_SLEW /* TWO step */
+#define LOCK_PROCESS
+#define SYSV_TIMEOFDAY
+#define SIZE_RETURNED_IN_BUFFER
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SysVR4"
+#endif
+#endif
+
+/*
+ * (Univel/Novell) Unixware1 SVR4 on intel x86 processor
+ */
+#if defined(SYS_UNIXWARE1)
+/* #define _POSIX_SOURCE */
+#undef HAVE_ATT_SETPGRP
+#define USE_PROTOTYPES
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_NICE
+#define HAVE_READKMEM
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define UDP_WILDCARD_DELIVERY
+#undef HAVE_SIGNALED_IO
+#define STREAM
+#define STREAMS
+#ifndef STREAMS_TLI
+/*#define STREAMS_TLI*/
+#endif
+/* #define USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+#undef STEP_SLEW /* TWO step */
+#define LOCK_PROCESS
+#define NO_SIGNED_CHAR_DECL
+#undef SYSV_TIMEOFDAY
+#define SIZE_RETURNED_IN_BUFFER
+#define RETSIGTYPE void
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Unixware1"
+#endif
+#endif
+
+/*
+ * DomainOS
+ */
+#if defined(SYS_DOMAINOS)
+#define HAVE_BSD_NICE
+#define NOKMEM
+#define HAVE_SIGNALED_IO
+#define NTP_SYSCALLS_STD
+#define USE_PROTOTYPES
+#define UDP_WILDCARD_DELIVERY
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/DOMAINOS"
+#endif
+#endif
+
+#ifdef STREAM /* STREAM implies TERMIOS */
+#ifndef HAVE_TERMIOS
+#define HAVE_TERMIOS
+#endif
+#endif
+
+#ifndef RETSIGTYPE
+#if defined(NTP_POSIX_SOURCE)
+#define RETSIGTYPE void
+#else
+#define RETSIGTYPE int
+#endif
+#endif
+
+#ifdef NTP_SYSCALLS_STD
+#ifndef NTP_SYSCALL_GET
+#define NTP_SYSCALL_GET 235
+#endif
+#ifndef NTP_SYSCALL_ADJ
+#define NTP_SYSCALL_ADJ 236
+#endif
+#endif /* NTP_SYSCALLS_STD */
+
+#if !defined(HAVE_ATT_NICE) \
+ && !defined(HAVE_BSD_NICE) \
+ && !defined(HAVE_NO_NICE)
+ ERROR You_must_define_one_of_the_HAVE_xx_NICE_defines
+#endif
+
+/*
+ * use only one tty model - no use in initialising
+ * a tty in three ways
+ * HAVE_TERMIOS is preferred over HAVE_SYSV_TTYS over HAVE_BSD_TTYS
+ */
+#ifdef HAVE_TERMIOS
+#undef HAVE_BSD_TTYS
+#undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+#undef HAVE_BSD_TTYS
+#endif
+
+#if !defined(HAVE_SYSV_TTYS) \
+ && !defined(HAVE_BSD_TTYS) \
+ && !defined(HAVE_TERMIOS)
+ ERROR no_tty_type_defined
+#endif
+
+
+#if !defined(XNTP_BIG_ENDIAN) && !defined(XNTP_LITTLE_ENDIAN)
+
+# if defined(XNTP_AUTO_ENDIAN)
+# include <netinet/in.h>
+
+# if BYTE_ORDER == BIG_ENDIAN
+# define XNTP_BIG_ENDIAN
+# endif
+# if BYTE_ORDER == LITTLE_ENDIAN
+# define XNTP_LITTLE_ENDIAN
+# endif
+
+# else /* AUTO */
+
+# ifdef WORDS_BIGENDIAN
+# define XNTP_BIG_ENDIAN 1
+# else
+# define XNTP_LITTLE_ENDIAN 1
+# endif
+
+# endif /* AUTO */
+
+#endif /* !BIG && !LITTLE */
+
+/*
+ * Byte order woes. The DES code is sensitive to byte order. This
+ * used to be resolved by calling ntohl() and htonl() to swap things
+ * around, but this turned out to be quite costly on Vaxes where those
+ * things are actual functions. The code now straightens out byte
+ * order troubles on its own, with no performance penalty for little
+ * end first machines, but at great expense to cleanliness.
+ */
+#if !defined(XNTP_BIG_ENDIAN) && !defined(XNTP_LITTLE_ENDIAN)
+ /*
+ * Pick one or the other.
+ */
+ BYTE_ORDER_NOT_DEFINED_FOR_AUTHENTICATION
+#endif
+
+#if defined(XNTP_BIG_ENDIAN) && defined(XNTP_LITTLE_ENDIAN)
+ /*
+ * Pick one or the other.
+ */
+ BYTE_ORDER_NOT_DEFINED_FOR_AUTHENTICATION
+#endif
+
+
+#endif /* __ntp_machine */
diff --git a/usr.sbin/xntpd/include/ntp_malloc.h b/usr.sbin/xntpd/include/ntp_malloc.h
new file mode 100644
index 0000000..0079cb7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_malloc.h
@@ -0,0 +1,15 @@
+/*
+ * Define malloc and friends.
+ */
+#ifndef _ntp_malloc_h
+
+#define _ntp_malloc_h
+#ifdef NTP_POSIX_SOURCE
+#include <stdlib.h>
+#else /* NTP_POSIX_SOURCE */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#endif /* NTP_POSIX_SOURCE */
+
+#endif /* _ntp_malloc_h */
diff --git a/usr.sbin/xntpd/include/ntp_refclock.h b/usr.sbin/xntpd/include/ntp_refclock.h
new file mode 100644
index 0000000..fcc1200
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_refclock.h
@@ -0,0 +1,224 @@
+/*
+ * ntp_refclock.h - definitions for reference clock support
+ */
+
+#include "ntp_types.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(CLK)
+#include <sys/clkdefs.h>
+#endif /* CLK */
+#endif /* STREAM */
+
+#if !defined(SYSV_TTYS) && !defined(STREAM) & !defined(BSD_TTYS)
+#define BSD_TTYS
+#endif /* SYSV_TTYS STREAM BSD_TTYS */
+
+/*
+ * Macros to determine the clock type and unit numbers from a
+ * 127.127.t.u address
+ */
+#define REFCLOCKTYPE(srcadr) ((SRCADR(srcadr) >> 8) & 0xff)
+#define REFCLOCKUNIT(srcadr) (SRCADR(srcadr) & 0xff)
+
+/*
+ * List of reference clock names and descriptions. These must agree with
+ * lib/clocktypes.c and xntpd/refclock_conf.c.
+ */
+struct clktype {
+ int code; /* driver "major" number */
+ char *clocktype; /* long description */
+ char *abbrev; /* short description */
+};
+
+/*
+ * Configuration flag values
+ */
+#define CLK_HAVETIME1 0x1
+#define CLK_HAVETIME2 0x2
+#define CLK_HAVEVAL1 0x4
+#define CLK_HAVEVAL2 0x8
+
+#define CLK_FLAG1 0x1
+#define CLK_FLAG2 0x2
+#define CLK_FLAG3 0x4
+#define CLK_FLAG4 0x8
+
+#define CLK_HAVEFLAG1 0x10
+#define CLK_HAVEFLAG2 0x20
+#define CLK_HAVEFLAG3 0x40
+#define CLK_HAVEFLAG4 0x80
+
+/*
+ * Structure for returning clock status
+ */
+struct refclockstat {
+ u_char type; /* clock type */
+ u_char flags; /* clock flags */
+ u_char haveflags; /* bit array of valid flags */
+ u_char lencode; /* length of last timecode */
+ char *lastcode; /* last timecode received */
+ U_LONG polls; /* transmit polls */
+ U_LONG noresponse; /* no response to poll */
+ U_LONG badformat; /* bad format timecode received */
+ U_LONG baddata; /* invalid data timecode received */
+ U_LONG timereset; /* driver resets */
+ char *clockdesc; /* ASCII description */
+ l_fp fudgetime1; /* configure fudge time1 */
+ l_fp fudgetime2; /* configure fudge time2 */
+ LONG fudgeval1; /* configure fudge value1 */
+ LONG fudgeval2; /* configure fudge value2 */
+ u_char currentstatus; /* clock status */
+ u_char lastevent; /* last exception event */
+ u_char unused; /* spare */
+ struct ctl_var *kv_list; /* additional variables */
+};
+
+/*
+ * Reference clock I/O structure. Used to provide an interface between
+ * the reference clock drivers and the I/O module.
+ */
+struct refclockio {
+ struct refclockio *next; /* link to next structure */
+ void (*clock_recv)();/* completion routine */
+ caddr_t srcclock; /* pointer to clock structure */
+ int datalen; /* lenth of data */
+ int fd; /* file descriptor */
+ u_long recvcount; /* count of receive completions */
+};
+
+/*
+ * Structure for returning debugging info
+ */
+#define NCLKBUGVALUES 16
+#define NCLKBUGTIMES 32
+
+struct refclockbug {
+ u_char nvalues; /* values following */
+ u_char ntimes; /* times following */
+ u_short svalues; /* values format sign array */
+ U_LONG stimes; /* times format sign array */
+ U_LONG values[NCLKBUGVALUES]; /* real values */
+ l_fp times[NCLKBUGTIMES]; /* real times */
+};
+
+/*
+ * Structure interface between the reference clock support
+ * ntp_refclock.c and the driver utility routines
+ */
+#define MAXSTAGE 64 /* max stages in shift register */
+#define BMAX 128 /* max timecode length */
+#define GMT 0 /* I hope nobody sees this */
+#define MAXDIAL 20 /* max length of modem dial strings */
+
+/*
+ * Line discipline flags. These require line discipline or streams
+ * modules to be installed/loaded in the kernel. If specified, but not
+ * installed, the code runs as if unspecified.
+ */
+#define LDISC_STD 0x0 /* standard */
+#define LDISC_CLK 0x1 /* tty_clk \n intercept */
+#define LDISC_CLKPPS 0x2 /* tty_clk \377 intercept */
+#define LDISC_ACTS 0x4 /* tty_clk #* intercept */
+#define LDISC_CHU 0x8 /* tty_chu */
+#define LDISC_PPS 0x10 /* ppsclock */
+
+struct refclockproc {
+ struct refclockio io; /* I/O handler structure */
+ caddr_t unitptr; /* pointer to unit structure */
+ u_long lasttime; /* last clock update time */
+ u_char leap; /* leap/synchronization code */
+ u_char currentstatus; /* clock status */
+ u_char lastevent; /* last exception event */
+ u_char type; /* clock type */
+ char *clockdesc; /* clock description */
+ char lastcode[BMAX]; /* last timecode received */
+ u_char lencode; /* length of last timecode */
+
+ u_int year; /* year of eternity */
+ u_int day; /* day of year */
+ u_int hour; /* hour of day */
+ u_int minute; /* minute of hour */
+ u_int second; /* second of minute */
+ u_int msec; /* millisecond of second */
+ u_long usec; /* microsecond of second (alt) */
+ u_int nstages; /* median filter stages */
+ u_long yearstart; /* beginning of year */
+ u_long coderecv; /* sample counter */
+ l_fp lastref; /* last reference timestamp */
+ l_fp lastrec; /* last local timestamp */
+ l_fp offset; /* median offset */
+ u_fp dispersion; /* sample dispersion */
+ l_fp filter[MAXSTAGE]; /* median filter */
+
+ /*
+ * Configuration data
+ */
+ l_fp fudgetime1; /* fudge time1 */
+ l_fp fudgetime2; /* fudge time2 */
+ u_long refid; /* reference identifier */
+ u_long sloppyclockflag; /* fudge flags */
+
+ /*
+ * Status tallies
+ */
+ u_long timestarted; /* time we started this */
+ u_long polls; /* polls sent */
+ u_long noreply; /* no replies to polls */
+ u_long badformat; /* bad format reply */
+ u_long baddata; /* bad data reply */
+};
+
+/*
+ * Structure interface between the reference clock support
+ * ntp_refclock.c and particular clock drivers. This must agree with the
+ * structure defined in the driver.
+ */
+#define noentry 0 /* flag for null routine */
+#define NOFLAGS 0 /* flag for null flags */
+
+struct refclock {
+ int (*clock_start) P((int, struct peer *));
+ void (*clock_shutdown) P((int, struct peer *));
+ void (*clock_poll) P((int, struct peer *));
+ void (*clock_control) P((int, struct refclockstat *,
+ struct refclockstat *));
+ void (*clock_init) P((void));
+ void (*clock_buginfo) P((int, struct refclockbug *));
+ u_long clock_flags;
+};
+
+/*
+ * Function prototypes
+ */
+extern int io_addclock_simple P((struct refclockio *));
+extern int io_addclock P((struct refclockio *));
+extern void io_closeclock P((struct refclockio *));
+
+#ifdef REFCLOCK
+extern void refclock_buginfo P((struct sockaddr_in *,
+ struct refclockbug *));
+extern void refclock_control P((struct sockaddr_in *,
+ struct refclockstat *,
+ struct refclockstat *));
+extern int refclock_open P((char *, int, int));
+extern void refclock_transmit P((struct peer *));
+extern int refclock_ioctl P((int, int));
+extern int refclock_process P((struct refclockproc *, int, int));
+extern void refclock_report P((struct peer *, u_char));
+extern int refclock_gtlin P((struct recvbuf *, char *, int,
+ l_fp *));
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/include/ntp_request.h b/usr.sbin/xntpd/include/ntp_request.h
new file mode 100644
index 0000000..ea2d769
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_request.h
@@ -0,0 +1,808 @@
+/*
+ * ntp_request.h - definitions for the xntpd remote query facility
+ */
+
+#include "ntp_types.h"
+
+/*
+ * A mode 7 packet is used exchanging data between an NTP server
+ * and a client for purposes other than time synchronization, e.g.
+ * monitoring, statistics gathering and configuration. A mode 7
+ * packet has the following format:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|M| VN | Mode|A| Sequence | Implementation| Req Code |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Err | Number of data items | MBZ | Size of data item |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Data (Minimum 0 octets, maximum 500 octets) |
+ * | |
+ * [...]
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encryption Keyid (when A bit set) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Message Authentication Code (when A bit set) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * where the fields are (note that the client sends requests, the server
+ * responses):
+ *
+ * Response Bit: This packet is a response (if clear, packet is a request).
+ *
+ * More Bit: Set for all packets but the last in a response which
+ * requires more than one packet.
+ *
+ * Version Number: 2 for current version
+ *
+ * Mode: Always 7
+ *
+ * Authenticated bit: If set, this packet is authenticated.
+ *
+ * Sequence number: For a multipacket response, contains the sequence
+ * number of this packet. 0 is the first in the sequence,
+ * 127 (or less) is the last. The More Bit must be set in
+ * all packets but the last.
+ *
+ * Implementation number: The number of the implementation this request code
+ * is defined by. An implementation number of zero is used
+ * for requst codes/data formats which all implementations
+ * agree on. Implementation number 255 is reserved (for
+ * extensions, in case we run out).
+ *
+ * Request code: An implementation-specific code which specifies the
+ * operation to be (which has been) performed and/or the
+ * format and semantics of the data included in the packet.
+ *
+ * Err: Must be 0 for a request. For a response, holds an error
+ * code relating to the request. If nonzero, the operation
+ * requested wasn't performed.
+ *
+ * 0 - no error
+ * 1 - incompatable implementation number
+ * 2 - unimplemented request code
+ * 3 - format error (wrong data items, data size, packet size etc.)
+ * 4 - no data available (e.g. request for details on unknown peer)
+ * 5-6 I don't know
+ * 7 - authentication failure (i.e. permission denied)
+ *
+ * Number of data items: number of data items in packet. 0 to 500
+ *
+ * MBZ: A reserved data field, must be zero in requests and responses.
+ *
+ * Size of data item: size of each data item in packet. 0 to 500
+ *
+ * Data: Variable sized area containing request/response data. For
+ * requests and responses the size in octets must be greater
+ * than or equal to the product of the number of data items
+ * and the size of a data item. For requests the data area
+ * must be exactly 40 octets in length. For responses the
+ * data area may be any length between 0 and 500 octets
+ * inclusive.
+ *
+ * Message Authentication Code: Same as NTP spec, in definition and function.
+ * May optionally be included in requests which require
+ * authentication, is never included in responses.
+ *
+ * The version number, mode and keyid have the same function and are
+ * in the same location as a standard NTP packet. The request packet
+ * is the same size as a standard NTP packet to ease receive buffer
+ * management, and to allow the same encryption procedure to be used
+ * both on mode 7 and standard NTP packets. The mac is included when
+ * it is required that a request be authenticated, the keyid should be
+ * zero in requests in which the mac is not included.
+ *
+ * The data format depends on the implementation number/request code pair
+ * and whether the packet is a request or a response. The only requirement
+ * is that data items start in the octet immediately following the size
+ * word and that data items be concatenated without padding between (i.e.
+ * if the data area is larger than data_items*size, all padding is at
+ * the end). Padding is ignored, other than for encryption purposes.
+ * Implementations using encryption might want to include a time stamp
+ * or other data in the request packet padding. The key used for requests
+ * is implementation defined, but key 15 is suggested as a default.
+ */
+
+/*
+ * A request packet. These are almost a fixed length.
+ */
+struct req_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[32]; /* data area */
+ l_fp tstamp; /* time stamp, for authentication */
+ U_LONG keyid; /* encryption key */
+ char mac[MAX_MAC_LEN-sizeof(U_LONG)]; /* (optional) 8 byte auth code */
+};
+
+/*
+ * Input packet lengths. One with the mac, one without.
+ */
+#define REQ_LEN_MAC (sizeof(struct req_pkt))
+#define REQ_LEN_NOMAC (sizeof(struct req_pkt) - MAX_MAC_LEN)
+
+/*
+ * A response packet. The length here is variable, this is a
+ * maximally sized one. Note that this implementation doesn't
+ * authenticate responses.
+ */
+#define RESP_HEADER_SIZE (8)
+#define RESP_DATA_SIZE (500)
+
+struct resp_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[RESP_DATA_SIZE]; /* data area */
+};
+
+
+/*
+ * Information error codes
+ */
+#define INFO_OKAY 0
+#define INFO_ERR_IMPL 1 /* incompatable implementation */
+#define INFO_ERR_REQ 2 /* unknown request code */
+#define INFO_ERR_FMT 3 /* format error */
+#define INFO_ERR_NODATA 4 /* no data for this request */
+#define INFO_ERR_AUTH 7 /* authentication failure */
+
+/*
+ * Maximum sequence number.
+ */
+#define MAXSEQ 127
+
+
+/*
+ * Bit setting macros for multifield items.
+ */
+#define RESP_BIT 0x80
+#define MORE_BIT 0x40
+
+#define ISRESPONSE(rm_vn_mode) (((rm_vn_mode)&RESP_BIT)!=0)
+#define ISMORE(rm_vn_mode) (((rm_vn_mode)&MORE_BIT)!=0)
+#define INFO_VERSION(rm_vn_mode) ((u_char)(((rm_vn_mode)>>3)&0x7))
+#define INFO_MODE(rm_vn_mode) ((rm_vn_mode)&0x7)
+
+#define RM_VN_MODE(resp, more) ((u_char)(((resp)?RESP_BIT:0)\
+ |((more)?MORE_BIT:0)\
+ |((NTP_VERSION)<<3)\
+ |(MODE_PRIVATE)))
+
+#define INFO_IS_AUTH(auth_seq) (((auth_seq) & 0x80) != 0)
+#define INFO_SEQ(auth_seq) ((auth_seq)&0x7f)
+#define AUTH_SEQ(auth, seq) ((u_char)((((auth)!=0)?0x80:0)|((seq)&0x7f)))
+
+#define INFO_ERR(err_nitems) ((u_short)((ntohs(err_nitems)>>12)&0xf))
+#define INFO_NITEMS(err_nitems) ((u_short)(ntohs(err_nitems)&0xfff))
+#define ERR_NITEMS(err, nitems) (htons((((u_short)(err)<<12)&0xf000)\
+ |((u_short)(nitems)&0xfff)))
+
+#define INFO_MBZ(mbz_itemsize) ((ntohs(mbz_itemsize)>>12)&0xf)
+#define INFO_ITEMSIZE(mbz_itemsize) (ntohs(mbz_itemsize)&0xfff)
+#define MBZ_ITEMSIZE(itemsize) (htons((u_short)(itemsize)))
+
+
+/*
+ * Implementation numbers. One for universal use and one for xntpd.
+ */
+#define IMPL_UNIV 0
+#define IMPL_XNTPD 2
+
+/*
+ * Some limits related to authentication. Frames which are
+ * authenticated must include a time stamp which differs from
+ * the receive time stamp by no more than 10 seconds.
+ */
+#define INFO_TS_MAXSKEW_UI 10
+
+/*
+ * Universal request codes go here. There aren't any.
+ */
+
+/*
+ * XNTPD request codes go here.
+ */
+#define REQ_PEER_LIST 0 /* return list of peers */
+#define REQ_PEER_LIST_SUM 1 /* return summary info for all peers */
+#define REQ_PEER_INFO 2 /* get standard information on peer */
+#define REQ_PEER_STATS 3 /* get statistics for peer */
+#define REQ_SYS_INFO 4 /* get system information */
+#define REQ_SYS_STATS 5 /* get system stats */
+#define REQ_IO_STATS 6 /* get I/O stats */
+#define REQ_MEM_STATS 7 /* stats related to peer list maint */
+#define REQ_LOOP_INFO 8 /* info from the loop filter */
+#define REQ_TIMER_STATS 9 /* get timer stats */
+#define REQ_CONFIG 10 /* configure a new peer */
+#define REQ_UNCONFIG 11 /* unconfigure an existing peer */
+#define REQ_SET_SYS_FLAG 12 /* set system flags */
+#define REQ_CLR_SYS_FLAG 13 /* clear system flags */
+#define REQ_MONITOR 14 /* monitor clients */
+#define REQ_NOMONITOR 15 /* stop monitoring clients */
+#define REQ_GET_RESTRICT 16 /* return restrict list */
+#define REQ_RESADDFLAGS 17 /* add flags to restrict list */
+#define REQ_RESSUBFLAGS 18 /* remove flags from restrict list */
+#define REQ_UNRESTRICT 19 /* remove entry from restrict list */
+#define REQ_MON_GETLIST 20 /* return data collected by monitor */
+#define REQ_RESET_STATS 21 /* reset stat counters */
+#define REQ_RESET_PEER 22 /* reset peer stat counters */
+#define REQ_REREAD_KEYS 23 /* reread the encryption key file */
+#define REQ_DO_DIRTY_HACK 24 /* historical interest */
+#define REQ_DONT_DIRTY_HACK 25 /* Ibid. */
+#define REQ_TRUSTKEY 26 /* add a trusted key */
+#define REQ_UNTRUSTKEY 27 /* remove a trusted key */
+#define REQ_AUTHINFO 28 /* return authentication info */
+#define REQ_TRAPS 29 /* return currently set traps */
+#define REQ_ADD_TRAP 30 /* add a trap */
+#define REQ_CLR_TRAP 31 /* clear a trap */
+#define REQ_REQUEST_KEY 32 /* define a new request keyid */
+#define REQ_CONTROL_KEY 33 /* define a new control keyid */
+#define REQ_GET_CTLSTATS 34 /* get stats from the control module */
+#define REQ_GET_LEAPINFO 35 /* get leap information */
+#define REQ_GET_CLOCKINFO 36 /* get clock information */
+#define REQ_SET_CLKFUDGE 37 /* set clock fudge factors */
+#define REQ_GET_KERNEL 38 /* get kernel pll/pps information */
+#define REQ_GET_CLKBUGINFO 39 /* get clock debugging info */
+#define REQ_SET_PRECISION 41 /* set clock precision */
+#define REQ_MON_GETLIST_1 42 /* return data collected by monitor v1*/
+
+/*
+ * Flags in the peer information returns
+ */
+#define INFO_FLAG_CONFIG 0x1
+#define INFO_FLAG_SYSPEER 0x2
+#define INFO_FLAG_UNUSED 0x4
+#define INFO_FLAG_REFCLOCK 0x8
+#define INFO_FLAG_PREFER 0x10
+#define INFO_FLAG_AUTHENABLE 0x20
+#define INFO_FLAG_SEL_CANDIDATE 0x40
+#define INFO_FLAG_SHORTLIST 0x80
+
+/*
+ * Flags in the system information returns
+ */
+#define INFO_FLAG_BCLIENT 0x1
+#define INFO_FLAG_AUTHENTICATE 0x2
+#define INFO_FLAG_PLL 0x4
+#define INFO_FLAG_PPS 0x8 /* unused */
+#define INFO_FLAG_PLL_SYNC 0x10
+#define INFO_FLAG_PPS_SYNC 0x20
+#define INFO_FLAG_MONITOR 0x40
+#define INFO_FLAG_FILEGEN 0x80
+
+/*
+ * Peer list structure. Used to return raw lists of peers. It goes
+ * without saying that everything returned is in network byte order.
+ */
+struct info_peer_list {
+ U_LONG address; /* address of peer */
+ u_short port; /* port number of peer */
+ u_char hmode; /* mode for this peer */
+ u_char flags; /* flags (from above) */
+};
+
+
+/*
+ * Peer summary structure. Sort of the info that ntpdc returns by default.
+ */
+struct info_peer_summary {
+ U_LONG dstadr; /* local address (zero for undetermined) */
+ U_LONG srcadr; /* source address */
+ u_short srcport; /* source port */
+ u_char stratum; /* stratum of peer */
+ s_char hpoll; /* host polling interval */
+ s_char ppoll; /* peer polling interval */
+ u_char reach; /* reachability register */
+ u_char flags; /* flags, from above */
+ u_char hmode; /* peer mode */
+ s_fp delay; /* peer.estdelay */
+ l_fp offset; /* peer.estoffset */
+ u_fp dispersion; /* peer.estdisp */
+};
+
+
+/*
+ * Peer information structure.
+ */
+struct info_peer {
+ U_LONG dstadr; /* local address */
+ U_LONG srcadr; /* remote address */
+ u_short srcport; /* remote port */
+ u_char flags; /* peer flags */
+ u_char leap; /* peer.leap */
+ u_char hmode; /* peer.hmode */
+ u_char pmode; /* peer.pmode */
+ u_char stratum; /* peer.stratum */
+ u_char ppoll; /* peer.ppoll */
+ u_char hpoll; /* peer.hpoll */
+ s_char precision; /* peer.precision */
+ u_char version; /* peer.version */
+ u_char valid; /* peer.valid */
+ u_char reach; /* peer.reach */
+ u_char unreach; /* peer.unreach */
+ u_char flash; /* peer.flash */
+ u_char ttl; /* peer.ttl */
+ u_char unused8; /* (obsolete) */
+ u_char unused9;
+ u_short associd; /* association ID */
+ U_LONG keyid; /* auth key in use */
+ U_LONG pkeyid; /* peer.pkeyid */
+ U_LONG refid; /* peer.refid */
+ U_LONG timer; /* peer.timer */
+ s_fp rootdelay; /* peer.distance */
+ u_fp rootdispersion; /* peer.dispersion */
+ l_fp reftime; /* peer.reftime */
+ l_fp org; /* peer.org */
+ l_fp rec; /* peer.rec */
+ l_fp xmt; /* peer.xmt */
+ s_fp filtdelay[NTP_SHIFT]; /* delay shift register */
+ l_fp filtoffset[NTP_SHIFT]; /* offset shift register */
+ u_char order[NTP_SHIFT]; /* order of peers from last filter */
+ s_fp delay; /* peer.estdelay */
+ u_fp dispersion; /* peer.estdisp */
+ l_fp offset; /* peer.estoffset */
+ u_fp selectdisp; /* peer select dispersion */
+ LONG unused1; /* (obsolete) */
+ LONG unused2;
+ LONG unused3;
+ LONG unused4;
+ LONG unused5;
+ LONG unused6;
+ LONG unused7;
+ s_fp estbdelay; /* broadcast offset */
+};
+
+
+/*
+ * Peer statistics structure
+ */
+struct info_peer_stats {
+ U_LONG dstadr; /* local address */
+ U_LONG srcadr; /* remote address */
+ u_short srcport; /* remote port */
+ u_short flags; /* peer flags */
+ U_LONG timereset; /* time counters were reset */
+ U_LONG timereceived; /* time since a packet received */
+ U_LONG timetosend; /* time until a packet sent */
+ U_LONG timereachable; /* time peer has been reachable */
+ U_LONG sent; /* number sent */
+ U_LONG unused1; /* (unused) */
+ U_LONG processed; /* number processed */
+ U_LONG unused2; /* (unused) */
+ U_LONG badauth; /* bad authentication */
+ U_LONG bogusorg; /* bogus origin */
+ U_LONG oldpkt; /* duplicate */
+ U_LONG unused3; /* (unused) */
+ U_LONG unused4; /* (unused) */
+ U_LONG seldisp; /* bad dispersion */
+ U_LONG selbroken; /* bad reference time */
+ U_LONG unused5; /* (unused) */
+ u_char candidate; /* select order */
+ u_char unused6; /* (unused) */
+ u_char unused7; /* (unused) */
+ u_char unused8; /* (unused) */
+};
+
+
+/*
+ * Loop filter variables
+ */
+struct info_loop {
+ l_fp last_offset;
+ l_fp drift_comp;
+ U_LONG compliance;
+ U_LONG watchdog_timer;
+};
+
+
+/*
+ * System info. Mostly the sys.* variables, plus a few unique to
+ * the implementation.
+ */
+struct info_sys {
+ U_LONG peer; /* system peer address */
+ u_char peer_mode; /* mode we are syncing to peer in */
+ u_char leap; /* system leap bits */
+ u_char stratum; /* our stratum */
+ s_char precision; /* local clock precision */
+ s_fp rootdelay; /* distance from sync source */
+ u_fp rootdispersion; /* dispersion from sync source */
+ U_LONG refid; /* reference ID of sync source */
+ l_fp reftime; /* system reference time */
+ U_LONG poll; /* system poll interval */
+ u_char flags; /* system flags */
+ u_char unused1; /* unused */
+ u_char unused2; /* unused */
+ u_char unused3; /* unused */
+ s_fp bdelay; /* default broadcast offset */
+ s_fp frequency; /* frequency residual (scaled ppm) */
+ l_fp authdelay; /* default authentication delay */
+ u_fp stability; /* clock stability (scaled ppm) */
+};
+
+
+/*
+ * System stats. These are collected in the protocol module
+ */
+struct info_sys_stats {
+ U_LONG timeup; /* time we have been up and running */
+ U_LONG timereset; /* time since these were last cleared */
+ U_LONG badstratum; /* packets claiming an invalid stratum */
+ U_LONG oldversionpkt; /* old version packets received */
+ U_LONG newversionpkt; /* new version packets received */
+ U_LONG unknownversion; /* don't know version packets */
+ U_LONG badlength; /* packets with bad length */
+ U_LONG processed; /* packets processed */
+ U_LONG badauth; /* packets dropped because of authorization */
+ U_LONG wanderhold; /* (obsolete) */
+ U_LONG limitrejected; /* rejected because of client limitation */
+};
+
+
+/*
+ * System stats - old version
+ */
+struct old_info_sys_stats {
+ U_LONG timeup; /* time we have been up and running */
+ U_LONG timereset; /* time since these were last cleared */
+ U_LONG badstratum; /* packets claiming an invalid stratum */
+ U_LONG oldversionpkt; /* old version packets received */
+ U_LONG newversionpkt; /* new version packets received */
+ U_LONG unknownversion; /* don't know version packets */
+ U_LONG badlength; /* packets with bad length */
+ U_LONG processed; /* packets processed */
+ U_LONG badauth; /* packets dropped because of authorization */
+ U_LONG wanderhold;
+};
+
+
+/*
+ * Peer memory statistics. Collected in the peer module.
+ */
+struct info_mem_stats {
+ U_LONG timereset; /* time since reset */
+ u_short totalpeermem;
+ u_short freepeermem;
+ U_LONG findpeer_calls;
+ U_LONG allocations;
+ U_LONG demobilizations;
+ u_char hashcount[HASH_SIZE];
+};
+
+
+/*
+ * I/O statistics. Collected in the I/O module
+ */
+struct info_io_stats {
+ U_LONG timereset; /* time since reset */
+ u_short totalrecvbufs; /* total receive bufs */
+ u_short freerecvbufs; /* free buffers */
+ u_short fullrecvbufs; /* full buffers */
+ u_short lowwater; /* number of times we've added buffers */
+ U_LONG dropped; /* dropped packets */
+ U_LONG ignored; /* ignored packets */
+ U_LONG received; /* received packets */
+ U_LONG sent; /* packets sent */
+ U_LONG notsent; /* packets not sent */
+ U_LONG interrupts; /* interrupts we've handled */
+ U_LONG int_received; /* received by interrupt handler */
+};
+
+
+/*
+ * Timer stats. Guess where from.
+ */
+struct info_timer_stats {
+ U_LONG timereset; /* time since reset */
+ U_LONG alarms; /* alarms we've handled */
+ U_LONG overflows; /* timer overflows */
+ U_LONG xmtcalls; /* calls to xmit */
+};
+
+
+/*
+ * Structure for passing peer configuration information
+ */
+struct conf_peer {
+ U_LONG peeraddr; /* address to poll */
+ u_char hmode; /* mode, either broadcast, active or client */
+ u_char version; /* version number to poll with */
+ u_char minpoll; /* min host poll interval */
+ u_char maxpoll; /* max host poll interval */
+ u_char flags; /* flags for this request */
+ u_char ttl; /* time to live (multicast) */
+ u_short unused; /* unused */
+ U_LONG keyid; /* key to use for this association */
+};
+
+#define CONF_FLAG_AUTHENABLE 0x1
+#define CONF_FLAG_PREFER 0x2
+
+/*
+ * Structure for passing peer deletion information. Currently
+ * we only pass the address and delete all configured peers with
+ * this addess.
+ */
+struct conf_unpeer {
+ U_LONG peeraddr; /* address of peer */
+};
+
+/*
+ * Structure for carrying system flags.
+ */
+struct conf_sys_flags {
+ U_LONG flags;
+};
+
+/*
+ * System flags we can set/clear
+ */
+#define SYS_FLAG_BCLIENT 0x1
+#define SYS_FLAG_AUTHENTICATE 0x2
+#define SYS_FLAG_PLL 0x4
+#define SYS_FLAG_PPS 0x8
+#define SYS_FLAG_MONITOR 0x10
+#define SYS_FLAG_FILEGEN 0x20
+
+/*
+ * Structure used for returning restrict entries
+ */
+struct info_restrict {
+ U_LONG addr; /* match address */
+ U_LONG mask; /* match mask */
+ U_LONG count; /* number of packets matched */
+ u_short flags; /* restrict flags */
+ u_short mflags; /* match flags */
+};
+
+
+/*
+ * Structure used for specifying restrict entries
+ */
+struct conf_restrict {
+ U_LONG addr; /* match address */
+ U_LONG mask; /* match mask */
+ u_short flags; /* restrict flags */
+ u_short mflags; /* match flags */
+};
+
+
+/*
+ * Structure used for returning monitor data
+ */
+struct info_monitor_1 {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG lastdrop; /* last time we rejected a packet due to client limitation policy */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ U_LONG daddr; /* destination host address */
+ U_LONG flags; /* flags about destination */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+
+/*
+ * Structure used for returning monitor data
+ */
+struct info_monitor {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG lastdrop; /* last time we rejected a packet due to client limitation policy */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+/*
+ * Structure used for returning monitor data (old format
+ */
+struct old_info_monitor {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+/*
+ * Structure used for passing indication of flags to clear
+ */
+struct reset_flags {
+ U_LONG flags;
+};
+
+#define RESET_FLAG_ALLPEERS 0x01
+#define RESET_FLAG_IO 0x02
+#define RESET_FLAG_SYS 0x04
+#define RESET_FLAG_MEM 0x08
+#define RESET_FLAG_TIMER 0x10
+#define RESET_FLAG_AUTH 0x20
+#define RESET_FLAG_CTL 0x40
+
+#define RESET_ALLFLAGS \
+ (RESET_FLAG_ALLPEERS|RESET_FLAG_IO|RESET_FLAG_SYS \
+ |RESET_FLAG_MEM|RESET_FLAG_TIMER|RESET_FLAG_AUTH|RESET_FLAG_CTL)
+
+/*
+ * Structure used to return information concerning the authentication
+ * module.
+ */
+struct info_auth {
+ U_LONG timereset; /* time counters were reset */
+ U_LONG numkeys; /* number of keys we know */
+ U_LONG numfreekeys; /* number of free keys */
+ U_LONG keylookups; /* calls to authhavekey() */
+ U_LONG keynotfound; /* requested key unknown */
+ U_LONG encryptions; /* number of encryptions */
+ U_LONG decryptions; /* number of decryptions */
+ U_LONG unused; /* (unused) */
+ U_LONG keyuncached; /* calls to encrypt/decrypt with uncached key */
+};
+
+
+/*
+ * Structure used to pass trap information to the client
+ */
+struct info_trap {
+ U_LONG local_address; /* local interface address */
+ U_LONG trap_address; /* remote client's address */
+ u_short trap_port; /* remote port number */
+ u_short sequence; /* sequence number */
+ U_LONG settime; /* time trap last set */
+ U_LONG origtime; /* time trap originally set */
+ U_LONG resets; /* number of resets on this trap */
+ U_LONG flags; /* trap flags, as defined in ntp_control.h */
+};
+
+/*
+ * Structure used to pass add/clear trap information to the client
+ */
+struct conf_trap {
+ U_LONG local_address; /* local interface address */
+ U_LONG trap_address; /* remote client's address */
+ u_short trap_port; /* remote client's port */
+ u_short unused;
+};
+
+
+/*
+ * Structure used to return statistics from the control module
+ */
+struct info_control {
+ U_LONG ctltimereset;
+ U_LONG numctlreq; /* number of requests we've received */
+ U_LONG numctlbadpkts; /* number of bad control packets */
+ U_LONG numctlresponses; /* # resp packets sent */
+ U_LONG numctlfrags; /* # of fragments sent */
+ U_LONG numctlerrors; /* number of error responses sent */
+ U_LONG numctltooshort; /* number of too short input packets */
+ U_LONG numctlinputresp; /* number of responses on input */
+ U_LONG numctlinputfrag; /* number of fragments on input */
+ U_LONG numctlinputerr; /* # input pkts with err bit set */
+ U_LONG numctlbadoffset; /* # input pkts with nonzero offset */
+ U_LONG numctlbadversion; /* # input pkts with unknown version */
+ U_LONG numctldatatooshort; /* data too short for count */
+ U_LONG numctlbadop; /* bad op code found in packet */
+ U_LONG numasyncmsgs; /* # async messages we've sent */
+};
+
+
+/*
+ * Structure used to return leap information.
+ */
+struct info_leap {
+ u_char sys_leap; /* current sys_leap */
+ u_char leap_indicator; /* current leap indicator */
+ u_char leap_warning; /* current leap warning */
+ u_char leap_bits; /* leap flags */
+ U_LONG leap_timer; /* seconds to next interrupt */
+ U_LONG leap_processcalls; /* calls to the leap process */
+ U_LONG leap_notclose; /* found leap was not close */
+ U_LONG leap_monthofleap; /* in month of leap */
+ U_LONG leap_dayofleap; /* in day of leap */
+ U_LONG leap_hoursfromleap; /* leap within two hours */
+ U_LONG leap_happened; /* leap second happened */
+};
+
+#define INFO_LEAP_MASK 0x3 /* flag for leap_bits */
+#define INFO_LEAP_SEENSTRATUM1 0x4 /* server has seen stratum 1 */
+#define INFO_LEAP_OVERRIDE 0x8 /* server will override the leap information */
+
+/*
+ * Structure used to return clock information
+ */
+struct info_clock {
+ U_LONG clockadr;
+ u_char type;
+ u_char flags;
+ u_char lastevent;
+ u_char currentstatus;
+ U_LONG polls;
+ U_LONG noresponse;
+ U_LONG badformat;
+ U_LONG baddata;
+ U_LONG timestarted;
+ l_fp fudgetime1;
+ l_fp fudgetime2;
+ LONG fudgeval1;
+ LONG fudgeval2;
+};
+
+
+/*
+ * Structure used for setting clock fudge factors
+ */
+struct conf_fudge {
+ U_LONG clockadr;
+ U_LONG which;
+ l_fp fudgetime;
+ LONG fudgeval_flags;
+};
+
+#define FUDGE_TIME1 1
+#define FUDGE_TIME2 2
+#define FUDGE_VAL1 3
+#define FUDGE_VAL2 4
+#define FUDGE_FLAGS 5
+
+
+/*
+ * Structure used for returning clock debugging info
+ */
+#define NUMCBUGVALUES 16
+#define NUMCBUGTIMES 32
+
+struct info_clkbug {
+ U_LONG clockadr;
+ u_char nvalues;
+ u_char ntimes;
+ u_short svalues;
+ U_LONG stimes;
+ U_LONG values[NUMCBUGVALUES];
+ l_fp times[NUMCBUGTIMES];
+};
+
+/*
+ * Structure used for returning kernel pll/PPS information
+ */
+struct info_kernel {
+ LONG offset;
+ LONG freq;
+ LONG maxerror;
+ LONG esterror;
+ u_short status;
+ u_short shift;
+ LONG constant;
+ LONG precision;
+ LONG tolerance;
+
+/*
+ * Variables used only if PPS signal discipline is implemented
+ */
+ LONG ppsfreq;
+ LONG jitter;
+ LONG stabil;
+ LONG jitcnt;
+ LONG calcnt;
+ LONG errcnt;
+ LONG stbcnt;
+};
diff --git a/usr.sbin/xntpd/include/ntp_select.h b/usr.sbin/xntpd/include/ntp_select.h
new file mode 100644
index 0000000..14e498d
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_select.h
@@ -0,0 +1,20 @@
+/*
+ * Not all machines define FD_SET in sys/types.h
+ */
+#ifndef _ntp_select_h
+#define _ntp_select_h
+
+#if (defined(RS6000)||defined(SYS_PTX))&&!defined(_BSD)
+#include <sys/select.h>
+#endif
+
+#ifndef FD_SET
+#define NFDBITS 32
+#define FD_SETSIZE 32
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p)))
+#endif
+
+#endif /* _ntp_select_h */
diff --git a/usr.sbin/xntpd/include/ntp_stdlib.h b/usr.sbin/xntpd/include/ntp_stdlib.h
new file mode 100644
index 0000000..646ce60
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_stdlib.h
@@ -0,0 +1,93 @@
+/*
+ * ntp_stdlib.h - Prototypes for XNTP lib.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "l_stdlib.h"
+
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif
+#endif
+#endif
+
+#if defined(__STDC__)
+extern void msyslog P((int, char *, ...));
+#else
+extern void msyslog P(());
+#endif
+
+extern void auth_des P((u_long *, u_char *));
+extern void auth_delkeys P((void));
+extern int auth_havekey P((u_long));
+extern int auth_parity P((u_long *));
+extern void auth_setkey P((u_long, u_long *));
+extern void auth_subkeys P((u_long *, u_char *, u_char *));
+extern int authistrusted P((u_long));
+extern int authusekey P((u_long, int, const char *));
+
+extern void auth_delkeys P((void));
+
+extern void auth1crypt P((u_long, U_LONG *, int));
+extern int auth2crypt P((u_long, U_LONG *, int));
+extern int authdecrypt P((u_long, U_LONG *, int));
+extern int authencrypt P((u_long, U_LONG *, int));
+extern int authhavekey P((u_long));
+extern int authreadkeys P((const char *));
+extern void authtrust P((u_long, int));
+extern void calleapwhen P((u_long, u_long *, u_long *));
+extern u_long calyearstart P((u_long));
+extern const char *clockname P((int));
+extern int clocktime P((int, int, int, int, int, u_long, u_long *, U_LONG *));
+extern char * emalloc P((u_int));
+extern int ntp_getopt P((int, char **, char *));
+extern void init_auth P((void));
+extern void init_lib P((void));
+extern void init_random P((void));
+
+#ifdef DES
+extern void DESauth1crypt P((u_long, U_LONG *, int));
+extern int DESauth2crypt P((u_long, U_LONG *, int));
+extern int DESauthdecrypt P((u_long, const U_LONG *, int));
+extern int DESauthencrypt P((u_long, U_LONG *, int));
+extern void DESauth_setkey P((u_long, const U_LONG *));
+extern void DESauth_subkeys P((const U_LONG *, u_char *, u_char *));
+extern void DESauth_des P((U_LONG *, u_char *));
+extern int DESauth_parity P((U_LONG *));
+#endif /* DES */
+
+#ifdef MD5
+extern void MD5auth1crypt P((u_long, U_LONG *, int));
+extern int MD5auth2crypt P((u_long, U_LONG *, int));
+extern int MD5authdecrypt P((u_long, const U_LONG *, int));
+extern int MD5authencrypt P((u_long, U_LONG *, int));
+extern void MD5auth_setkey P((u_long, const U_LONG *));
+#endif /* MD5 */
+
+extern int atoint P((const char *, long *));
+extern int atouint P((const char *, u_long *));
+extern int hextoint P((const char *, u_long *));
+extern char * humandate P((u_long));
+extern char * inttoa P((long));
+extern char * mfptoa P((u_long, u_long, int));
+extern char * mfptoms P((u_long, u_long, int));
+extern char * modetoa P((int));
+extern u_long netof P((u_long));
+extern char * numtoa P((u_long));
+extern char * numtohost P((u_long));
+extern int octtoint P((const char *, u_long *));
+extern u_long ranp2 P((int));
+extern char * refnumtoa P((u_long));
+extern int tsftomsu P((u_long, int));
+extern char * uinttoa P((u_long));
+
+extern int decodenetnum P((const char *, u_long *));
+
+extern RETSIGTYPE signal_no_reset P((int, RETSIGTYPE (*func)()));
diff --git a/usr.sbin/xntpd/include/ntp_string.h b/usr.sbin/xntpd/include/ntp_string.h
new file mode 100644
index 0000000..90a29da
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_string.h
@@ -0,0 +1,35 @@
+/*
+ * Define string ops: strchr strrchr memcmp memmove memset
+ */
+
+#ifndef _ntp_string_h
+#define _ntp_string_h
+
+#if defined(NTP_POSIX_SOURCE)
+
+# if defined(HAVE_MEMORY_H)
+# include <memory.h>
+# endif
+
+# include <string.h>
+
+#else
+
+# include <strings.h>
+# define strchr(s,c) index(s,c)
+# define strrchr(s,c) rindex(s,c)
+# ifndef NTP_NEED_BOPS
+# define NTP_NEED_BOPS
+# endif
+#endif /* NTP_POSIX_SOURCE */
+
+#ifdef NTP_NEED_BOPS
+
+# define memcmp(a,b,c) bcmp(a,b,c)
+# define memmove(t,f,c) bcopy(f,t,c)
+# define memset(a,x,c) if (x == 0x00) bzero(a,c); else ntp_memset((char*)a,x,c)
+void ntp_memset P((char *, int, int));
+
+#endif /* NTP_NEED_BOPS */
+
+#endif /* _ntp_string_h */
diff --git a/usr.sbin/xntpd/include/ntp_syslog.h b/usr.sbin/xntpd/include/ntp_syslog.h
new file mode 100644
index 0000000..38b847b
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_syslog.h
@@ -0,0 +1,15 @@
+/*
+ * A hack for platforms which require specially built syslog facilities
+ */
+#ifdef GIZMO
+#include "gizmo_syslog.h"
+#else /* !GIZMO */
+#include <syslog.h>
+#ifdef SYSLOG_FILE
+#include <stdio.h>
+#endif
+#endif /* GIZMO */
+#ifdef SYSLOG_FILE
+extern FILE *syslog_file;
+#define syslog msyslog
+#endif
diff --git a/usr.sbin/xntpd/include/ntp_timex.h b/usr.sbin/xntpd/include/ntp_timex.h
new file mode 100644
index 0000000..cb8396a
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_timex.h
@@ -0,0 +1,273 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * 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 appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * This header file defines the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. These are implemented using
+ * private syscalls and data structures and require specific kernel
+ * support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ */
+#ifndef MSDOS /* Microsoft specific */
+#include <sys/syscall.h>
+#endif /* MSDOS */
+
+/*
+ * The following defines establish the engineering parameters of the
+ * phase-lock loop (PLL) model used in the kernel implementation. These
+ * parameters have been carefully chosen by analysis for good stability
+ * and wide dynamic range.
+ *
+ * The hz variable is defined in the kernel build environment. It
+ * establishes the timer interrupt frequency, 100 Hz for the SunOS
+ * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1
+ * kernel. SHIFT_HZ expresses the same value as the nearest power of two
+ * in order to avoid hardware multiply operations.
+ *
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_HZ 7 /* log2(hz) */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The following defines establish the scaling of the various variables
+ * used by the PLL. They are chosen to allow the greatest precision
+ * possible without overflow of a 32-bit word.
+ *
+ * SHIFT_SCALE defines the scaling (shift) of the time_phase variable,
+ * which serves as a an extension to the low-order bits of the system
+ * clock variable time.tv_usec.
+ *
+ * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable,
+ * which represents the current time offset with respect to standard
+ * time.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 23 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+/*
+ * The following defines establish the performance envelope of the PLL.
+ * They insure it operates within predefined limits, in order to satisfy
+ * correctness assertions. An excursion which exceeds these bounds is
+ * clamped to the bound and operation proceeds accordingly. In practice,
+ * this can occur only if something has failed or is operating out of
+ * tolerance, but otherwise the PLL continues to operate in a stable
+ * mode.
+ *
+ * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as
+ * defined in the NTP specification. CLOCK.MAX establishes the maximum
+ * time offset allowed before the system time is reset, rather than
+ * incrementally adjusted. Here, the maximum offset is clamped to
+ * MAXPHASE only in order to prevent overflow errors due to defective
+ * protocol implementations.
+ *
+ * MAXFREQ is the maximum frequency tolerance of the CPU clock
+ * oscillator plus the maximum slew rate allowed by the protocol. It
+ * should be set to at least the frequency tolerance of the oscillator
+ * plus 100 ppm for vernier frequency adjustments. If the kernel
+ * PPS discipline code is configured (PPS_SYNC), the oscillator time and
+ * frequency are disciplined to an external source, presumably with
+ * negligible time and frequency error relative to UTC, and MAXFREQ can
+ * be reduced.
+ *
+ * MAXTIME is the maximum jitter tolerance of the PPS signal if the
+ * kernel PPS discipline code is configured (PPS_SYNC).
+ *
+ * MINSEC and MAXSEC define the lower and upper bounds on the interval
+ * between protocol updates.
+ */
+#define MAXPHASE 128000L /* max phase error (us) */
+#ifdef PPS_SYNC
+#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (100 ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#else
+#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (200 ppm) */
+#endif /* PPS_SYNC */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+#endif /* PPS_SYNC */
+
+/*
+ * The following defines and structures define the user interface for
+ * the ntp_gettime() and ntp_adjtime() system calls.
+ *
+ * Control mode codes (timex.modes)
+ */
+#define MOD_OFFSET 0x0001 /* set time offset */
+#define MOD_FREQUENCY 0x0002 /* set frequency offset */
+#define MOD_MAXERROR 0x0004 /* set maximum time error */
+#define MOD_ESTERROR 0x0008 /* set estimated time error */
+#define MOD_STATUS 0x0010 /* set clock status bits */
+#define MOD_TIMECONST 0x0020 /* set pll time constant */
+#define MOD_CLKB 0x4000 /* set clock B */
+#define MOD_CLKA 0x8000 /* set clock A */
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* no leap second warning */
+#define TIME_INS 1 /* insert leap second warning */
+#define TIME_DEL 2 /* delete leap second warning */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occured */
+#define TIME_ERROR 5 /* clock not synchronized */
+
+/*
+ * NTP user interface (ntp_gettime()) - used to read kernel clock values
+ *
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ LONG maxerror; /* maximum error (us) (ro) */
+ LONG esterror; /* estimated error (us) (ro) */
+};
+
+/*
+ * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock
+ * oscillator
+ */
+struct timex {
+ unsigned int modes; /* clock mode bits (wo) */
+ LONG offset; /* time offset (us) (rw) */
+ LONG freq; /* frequency offset (scaled ppm) (rw) */
+ LONG maxerror; /* maximum error (us) (rw) */
+ LONG esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ LONG constant; /* pll time constant (rw) */
+ LONG precision; /* clock precision (us) (ro) */
+ LONG tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ LONG ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ LONG jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ LONG stabil; /* pps stability (scaled ppm) (ro) */
+ LONG jitcnt; /* jitter limit exceeded (ro) */
+ LONG calcnt; /* calibration intervals (ro) */
+ LONG errcnt; /* calibration errors (ro) */
+ LONG stbcnt; /* stability limit exceeded (ro) */
+
+};
diff --git a/usr.sbin/xntpd/include/ntp_types.h b/usr.sbin/xntpd/include/ntp_types.h
new file mode 100644
index 0000000..bbcea1b
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_types.h
@@ -0,0 +1,60 @@
+/*
+ * ntp_types.h - defines how LONG and U_LONG are treated. For 64 bit systems
+ * like the DEC Alpha, they has to be defined as int and u_int. for 32 bit
+ * systems, define them as long and u_long
+ */
+#include "ntp_machine.h"
+
+#ifndef _NTP_TYPES_
+#define _NTP_TYPES_
+
+/*
+ * This is another naming conflict.
+ * On NetBSD for MAC the macro "mac" is defined as 1
+ * this is fun for a as a paket structure contains an
+ * optional "mac" member - severe confusion results 8-)
+ * As we hopefully do not have to rely on that macro we
+ * just undefine that.
+ */
+#ifdef mac
+#undef mac
+#endif
+
+/*
+ * Set up for prototyping
+ */
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else /* __STDC__ USE_PROTOTYPES */
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif /* const */
+#endif /* __STDC__ USE_PROTOTYPES */
+#endif /* P */
+
+/*
+ * DEC Alpha systems need LONG and U_LONG defined as int and u_int
+ */
+#ifdef __alpha
+#ifndef LONG
+#define LONG int
+#endif /* LONG */
+#ifndef U_LONG
+#define U_LONG u_int
+#endif /* U_LONG */
+/*
+ * All other systems fall into this part
+ */
+#else /* __alpha */
+#ifndef LONG
+#define LONG long
+#endif /* LONG */
+#ifndef U_LONG
+#define U_LONG u_long
+#endif /* U_LONG */
+#endif /* __ alplha */
+
+#endif /* _NTP_TYPES_ */
+
diff --git a/usr.sbin/xntpd/include/ntp_unixtime.h b/usr.sbin/xntpd/include/ntp_unixtime.h
new file mode 100644
index 0000000..8007a06
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_unixtime.h
@@ -0,0 +1,119 @@
+/*
+ * ntp_unixtime.h - contains constants and macros for converting between
+ * NTP time stamps (l_fp) and Unix times (struct timeval)
+ */
+
+#include "ntp_types.h"
+#include <sys/time.h>
+
+/* gettimeofday() takes two args in BSD and only one in SYSV */
+#ifdef SYSV_TIMEOFDAY
+# define GETTIMEOFDAY(a, b) (gettimeofday(a))
+# define SETTIMEOFDAY(a, b) (settimeofday(a))
+#else /* ! SYSV_TIMEOFDAY */
+# define GETTIMEOFDAY(a, b) (gettimeofday(a, b))
+# define SETTIMEOFDAY(a, b) (settimeofday(a, b))
+#endif /* SYSV_TIMEOFDAY */
+
+/*
+ * Time of day conversion constant. Ntp's time scale starts in 1900,
+ * Unix in 1970.
+ */
+#define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */
+
+/*
+ * These constants are used to round the time stamps computed from
+ * a struct timeval to the microsecond (more or less). This keeps
+ * things neat.
+ */
+#define TS_MASK 0xfffff000 /* mask to usec, for time stamps */
+#define TS_ROUNDBIT 0x00000800 /* round at this bit */
+
+
+/*
+ * Convert usec to a time stamp fraction. If you use this the program
+ * must include the following declarations:
+ */
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+#define TVUTOTSF(tvu, tsf) \
+ (tsf) = ustotslo[(tvu) & 0xff] \
+ + ustotsmid[((tvu) >> 8) & 0xff] \
+ + ustotshi[((tvu) >> 16) & 0xf]
+
+/*
+ * Convert a struct timeval to a time stamp.
+ */
+#define TVTOTS(tv, ts) \
+ do { \
+ (ts)->l_ui = (u_long)(tv)->tv_sec; \
+ TVUTOTSF((tv)->tv_usec, (ts)->l_uf); \
+ } while(0)
+
+#define sTVTOTS(tv, ts) \
+ do { \
+ int isneg = 0; \
+ long usec; \
+ (ts)->l_ui = (tv)->tv_sec; \
+ usec = (tv)->tv_usec; \
+ if (((tv)->tv_sec < 0) || ((tv)->tv_usec < 0)) { \
+ usec = -usec; \
+ (ts)->l_ui = -(ts)->l_ui; \
+ isneg = 1; \
+ } \
+ TVUTOTSF(usec, (ts)->l_uf); \
+ if (isneg) { \
+ L_NEG((ts)); \
+ } \
+ } while(0)
+
+/*
+ * TV_SHIFT is used to turn the table result into a usec value. To round,
+ * add in TV_ROUNDBIT before shifting
+ */
+#define TV_SHIFT 3
+#define TV_ROUNDBIT 0x4
+
+
+/*
+ * Convert a time stamp fraction to microseconds. The time stamp
+ * fraction is assumed to be unsigned. To use this in a program, declare:
+ */
+extern long tstouslo[];
+extern long tstousmid[];
+extern long tstoushi[];
+
+#define TSFTOTVU(tsf, tvu) \
+ (tvu) = (tstoushi[((tsf) >> 24) & 0xff] \
+ + tstousmid[((tsf) >> 16) & 0xff] \
+ + tstouslo[((tsf) >> 9) & 0x7f] \
+ + TV_ROUNDBIT) >> TV_SHIFT
+/*
+ * Convert a time stamp to a struct timeval. The time stamp
+ * has to be positive.
+ */
+#define TSTOTV(ts, tv) \
+ do { \
+ (tv)->tv_sec = (ts)->l_ui; \
+ TSFTOTVU((ts)->l_uf, (tv)->tv_usec); \
+ if ((tv)->tv_usec == 1000000) { \
+ (tv)->tv_sec++; \
+ (tv)->tv_usec = 0; \
+ } \
+ } while (0)
+
+/*
+ * Convert milliseconds to a time stamp fraction. This shouldn't be
+ * here, but it is convenient since the guys who use the definition will
+ * often be including this file anyway.
+ */
+extern u_long msutotsflo[];
+extern u_long msutotsfhi[];
+
+#define MSUTOTSF(msu, tsf) \
+ (tsf) = msutotsfhi[((msu) >> 5) & 0x1f] + msutotsflo[(msu) & 0x1f]
+
+extern char * tvtoa P((const struct timeval *));
+extern char * utvtoa P((const struct timeval *));
diff --git a/usr.sbin/xntpd/include/ntpd.h b/usr.sbin/xntpd/include/ntpd.h
new file mode 100644
index 0000000..53e3a91
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntpd.h
@@ -0,0 +1,175 @@
+/*
+ * ntpd.h - Prototypes for xntpd.
+ */
+
+#include "ntp_syslog.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_malloc.h"
+
+/* ntp_config.c */
+extern void getstartup P((int, char **));
+extern void getconfig P((int, char **));
+
+/* ntp_config.c */
+extern void ctl_clr_stats P((void));
+extern int ctlclrtrap P((struct sockaddr_in *, struct interface *, int));
+extern u_short ctlpeerstatus P((struct peer *));
+extern int ctlsettrap P((struct sockaddr_in *, struct interface *, int, int));
+extern u_short ctlsysstatus P((void));
+extern void init_control P((void));
+extern void process_control P((struct recvbuf *, int));
+extern void report_event P((int, struct peer *));
+
+/* ntp_control.c */
+/*
+ * Structure for translation tables between internal system
+ * variable indices and text format.
+ */
+struct ctl_var {
+ u_short code;
+ u_short flags;
+ char *text;
+};
+/*
+ * Flag values
+ */
+#define CAN_READ 0x01
+#define CAN_WRITE 0x02
+
+#define DEF 0x20
+#define PADDING 0x40
+#define EOV 0x80
+
+#define RO (CAN_READ)
+#define WO (CAN_WRITE)
+#define RW (CAN_READ|CAN_WRITE)
+
+extern char * add_var P((struct ctl_var **, unsigned long, int));
+extern void free_varlist P((struct ctl_var *));
+extern void set_var P((struct ctl_var **, char *, unsigned long, int));
+extern void set_sys_var P((char *, unsigned long, int));
+
+/* ntp_intres.c */
+extern void ntp_intres P((void));
+
+/* ntp_io.c */
+extern struct interface *findbcastinter P((struct sockaddr_in *));
+extern struct interface *findinterface P((struct sockaddr_in *));
+extern void freerecvbuf P((struct recvbuf *));
+extern struct recvbuf *getrecvbufs P((void));
+extern void init_io P((void));
+extern void input_handler P((l_fp *));
+extern void io_clr_stats P((void));
+extern void io_setbclient P((void));
+extern void io_unsetbclient P((void));
+extern void io_multicast_add P((u_long));
+extern void io_multicast_del P((u_long));
+
+extern void sendpkt P((struct sockaddr_in *, struct interface *, int, struct pkt *, int));
+#ifdef HAVE_SIGNALED_IO
+extern void wait_for_signal P((void));
+extern void unblock_io_and_alarm P((void));
+extern void block_io_and_alarm P((void));
+#endif
+
+/* ntp_leap.c */
+extern void init_leap P((void));
+extern void leap_process P((void));
+extern int leap_setleap P((int, int));
+/*
+ * there seems to be a bug in the IRIX 4 compiler which prevents
+ * u_char from beeing used in prototyped functions.
+ * This is also true AIX compiler.
+ * So give up and define it to be int. WLJ
+ */
+extern int leap_actual P((int));
+
+/* ntp_loopfilter.c */
+extern void init_loopfilter P((void));
+extern int local_clock P((l_fp *, struct peer *));
+extern void adj_frequency P((s_fp));
+extern void adj_host_clock P((void));
+extern void loop_config P((int, l_fp *, int));
+#if defined(PPS) || defined(PPSPPS) || defined(PPSCD)
+extern int pps_sample P((l_fp *));
+#endif /* PPS || PPSDEV || PPSCD */
+
+/* ntp_monitor.c */
+extern void init_mon P((void));
+extern void mon_start P((int));
+extern void mon_stop P((int));
+extern void monitor P((struct recvbuf *));
+
+/* ntp_peer.c */
+extern void init_peer P((void));
+extern struct peer *findexistingpeer P((struct sockaddr_in *, struct peer *));
+extern struct peer *findpeer P((struct sockaddr_in *, struct interface *, int));
+extern struct peer *findpeerbyassoc P((int));
+extern struct peer *newpeer P((struct sockaddr_in *, struct interface *, int, int, int, int, int, u_long));
+extern void peer_all_reset P((void));
+extern void peer_clr_stats P((void));
+extern struct peer *peer_config P((struct sockaddr_in *, struct interface *, int, int, int, int, int, int, u_long));
+extern void peer_reset P((struct peer *));
+extern int peer_unconfig P((struct sockaddr_in *, struct interface *));
+extern void unpeer P((struct peer *));
+
+/* ntp_proto.c */
+extern void transmit P((struct peer *));
+extern void receive P((struct recvbuf *));
+extern void peer_clear P((struct peer *));
+extern int process_packet P((struct peer *, struct pkt *, l_fp *, int, int));
+extern void clock_update P((struct peer *));
+
+/*
+ * there seems to be a bug in the IRIX 4 compiler which prevents
+ * u_char from beeing used in prototyped functions.
+ * This is also true AIX compiler.
+ * So give up and define it to be int. WLJ
+ */
+extern void poll_update P((struct peer *, unsigned int, int));
+
+extern void clear P((struct peer *));
+extern void clock_filter P((struct peer *, l_fp *, s_fp, u_fp));
+extern void clock_select P((void));
+extern void clock_combine P((struct peer **, int));
+extern void fast_xmit P((struct recvbuf *, int, int));
+extern void init_proto P((void));
+extern void proto_config P((int, u_long));
+extern void proto_clr_stats P((void));
+
+#ifdef REFCLOCK
+/* ntp_refclock.c */
+extern int refclock_newpeer P((struct peer *));
+extern void refclock_unpeer P((struct peer *));
+extern void refclock_receive P((struct peer *, l_fp *, s_fp, u_fp, l_fp *, l_fp *, int));
+extern void refclock_leap P((void));
+extern void init_refclock P((void));
+#endif /* REFCLOCK */
+
+/* ntp_request.c */
+extern void init_request P((void));
+extern void process_private P((struct recvbuf *, int));
+
+/* ntp_restrict.c */
+extern void init_restrict P((void));
+extern int restrictions P((struct sockaddr_in *));
+extern void restrict P((int, struct sockaddr_in *, struct sockaddr_in *, int, int));
+
+/* ntp_timer.c */
+extern void init_timer P((void));
+extern void timer P((void));
+extern void timer_clr_stats P((void));
+
+/* ntp_unixclock.c */
+extern void init_systime P((void));
+
+/* ntp_util.c */
+extern void init_util P((void));
+extern void hourly_stats P((void));
+extern void stats_config P((int, char *));
+extern void record_peer_stats P((struct sockaddr_in *, int, l_fp *, s_fp, u_fp));
+extern void record_loop_stats P((l_fp *, s_fp, int));
+extern void record_clock_stats P((struct sockaddr_in *, char *));
+extern void getauthkeys P((char *));
+extern void rereadkeys P((void));
diff --git a/usr.sbin/xntpd/include/parse.h b/usr.sbin/xntpd/include/parse.h
new file mode 100644
index 0000000..774cf5e
--- /dev/null
+++ b/usr.sbin/xntpd/include/parse.h
@@ -0,0 +1,459 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp
+ *
+ * parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+#if !(defined(lint) || defined(__GNUC__))
+ static char parsehrcsid[]="parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp";
+#endif
+
+#include "ntp_types.h"
+
+#include "parse_conf.h"
+
+/*
+ * we use the following datastructures in two modes
+ * either in the NTP itself where we use NTP time stamps at some places
+ * or in the kernel, where only struct timeval will be used.
+ */
+#undef PARSEKERNEL
+#if defined(KERNEL) || defined(_KERNEL)
+#ifndef PARSESTREAM
+#define PARSESTREAM
+#endif
+#endif
+#if defined(PARSESTREAM) && defined(STREAM)
+#define PARSEKERNEL
+#endif
+#ifdef PARSEKERNEL
+#ifndef _KERNEL
+extern caddr_t kmem_alloc P((unsigned int));
+extern caddr_t kmem_free P((caddr_t, unsigned int));
+extern int splx();
+extern int splhigh();
+#define MALLOC(_X_) (char *)kmem_alloc(_X_)
+#define FREE(_X_, _Y_) kmem_free((caddr_t)_X_, _Y_)
+#else
+#include <sys/kmem.h>
+#define MALLOC(_X_) (char *)kmem_alloc(_X_, KM_SLEEP)
+#define FREE(_X_, _Y_) kmem_free((caddr_t)_X_, _Y_)
+#endif
+#else
+#define MALLOC(_X_) malloc(_X_)
+#define FREE(_X_, _Y_) free(_X_)
+#endif
+
+#if defined(PARSESTREAM) && defined(STREAM)
+#include "sys/stream.h"
+#include "sys/stropts.h"
+#ifndef _KERNEL
+extern int printf();
+#endif
+#else /* STREAM */
+#include <stdio.h>
+#include "ntp_syslog.h"
+#ifdef DEBUG
+extern int debug;
+#define DD_PARSE 5
+#define DD_RAWDCF 4
+#define parseprintf(LEVEL, ARGS) if (debug > LEVEL) printf ARGS
+#else /* DEBUG */
+#define parseprintf(LEVEL, ARGS)
+#endif /* DEBUG */
+#endif /* PARSESTREAM */
+
+#if defined(timercmp) && defined(__GNUC__)
+#undef timercmp
+#define timercmp(tvp, uvp, cmp) \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec || \
+ ((tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec))
+#endif
+
+#ifndef TIMES10
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1))
+#endif
+
+/*
+ * state flags
+ */
+#define PARSEB_POWERUP 0x00000001 /* no synchronisation */
+#define PARSEB_NOSYNC 0x00000002 /* timecode currently not confirmed */
+
+/*
+ * time zone information
+ */
+#define PARSEB_ANNOUNCE 0x00000010 /* switch time zone warning (DST switch) */
+#define PARSEB_DST 0x00000020 /* DST in effect */
+#define PARSEB_UTC 0x00000040 /* UTC time */
+
+/*
+ * leap information
+ */
+#define PARSEB_LEAPDEL 0x00000100 /* LEAP deletion warning */
+#define PARSEB_LEAPADD 0x00000200 /* LEAP addition warning */
+#define PARSEB_LEAPS 0x00000300 /* LEAP warnings */
+#define PARSEB_LEAPSECOND 0x00000400 /* actual leap second */
+/*
+ * optional status information
+ */
+#define PARSEB_ALTERNATE 0x00001000 /* alternate antenna used */
+#define PARSEB_POSITION 0x00002000 /* position available */
+
+/*
+ * feature information
+ */
+#define PARSEB_S_LEAP 0x00010000 /* supports LEAP */
+#define PARSEB_S_ANTENNA 0x00020000 /* supports antenna information */
+#define PARSEB_S_PPS 0x00040000 /* supports PPS time stamping */
+#define PARSEB_S_POSITION 0x00080000 /* supports position information (GPS) */
+
+/*
+ * time stamp availality
+ */
+#define PARSEB_TIMECODE 0x10000000 /* valid time code sample */
+#define PARSEB_PPS 0x20000000 /* valid PPS sample */
+
+#define PARSE_TCINFO (PARSEB_ANNOUNCE|PARSEB_POWERUP|PARSEB_NOSYNC|PARSEB_DST|\
+ PARSEB_UTC|PARSEB_LEAPS|PARSEB_ALTERNATE|PARSEB_S_LEAP|\
+ PARSEB_S_LOCATION|PARSEB_TIMECODE)
+
+#define PARSE_POWERUP(x) ((x) & PARSEB_POWERUP)
+#define PARSE_NOSYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == PARSEB_NOSYNC)
+#define PARSE_SYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == 0)
+#define PARSE_ANNOUNCE(x) ((x) & PARSEB_ANNOUNCE)
+#define PARSE_DST(x) ((x) & PARSEB_DST)
+#define PARSE_UTC(x) ((x) & PARSEB_UTC)
+#define PARSE_LEAPADD(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPADD))
+#define PARSE_LEAPDEL(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPDEL))
+#define PARSE_ALTERNATE(x) ((x) & PARSEB_ALTERNATE)
+#define PARSE_LEAPSECOND(x) (PARSE_SYNC(x) && ((x) & PARSEB_LEAP_SECOND))
+
+#define PARSE_S_LEAP(x) ((x) & PARSEB_S_LEAP)
+#define PARSE_S_ANTENNA(x) ((x) & PARSEB_S_ANTENNA)
+#define PARSE_S_PPS(x) ((x) & PARSEB_S_PPS)
+#define PARSE_S_POSITION(x) ((x) & PARSEB_S_POSITION)
+
+#define PARSE_TIMECODE(x) ((x) & PARSEB_TIMECODE)
+#define PARSE_PPS(x) ((x) & PARSEB_PPS)
+#define PARSE_POSITION(x) ((x) & PARSEB_POSITION)
+
+/*
+ * operation flags - some are also fudge flags
+ */
+#define PARSE_STAT_FLAGS 0x03 /* interpreted by io module */
+#define PARSE_STAT_FILTER 0x01 /* filter incoming data */
+#define PARSE_STAT_AVG 0x02 /* 1:median average / 0: median point */
+#define PARSE_LEAP_DELETE 0x04 /* delete leap */
+#define PARSE_STATISTICS 0x08 /* enable statistics */
+#define PARSE_FIXED_FMT 0x10 /* fixed format */
+#define PARSE_PPSCLOCK 0x20 /* try to get PPS time stamp via ppsclock ioctl */
+
+typedef union timestamp
+{
+ struct timeval tv; /* timeval - usually kernel view */
+ l_fp fp; /* fixed point - xntp view */
+} timestamp_t;
+
+/*
+ * standard time stamp structure
+ */
+struct parsetime
+{
+ u_long parse_status; /* data status - CVT_OK, CVT_NONE, CVT_FAIL ... */
+ timestamp_t parse_time; /* PARSE timestamp */
+ timestamp_t parse_stime; /* telegram sample timestamp */
+ timestamp_t parse_ptime; /* PPS time stamp */
+ long parse_usecerror; /* sampled/filtered usec error */
+ long parse_usecdisp; /* sampled usecdispersion */
+ u_long parse_state; /* current receiver state */
+ unsigned short parse_format; /* format code */
+};
+
+typedef struct parsetime parsetime_t;
+
+/*---------- STREAMS interface ----------*/
+
+#ifdef STREAM
+/*
+ * ioctls
+ */
+#define PARSEIOC_ENABLE (('D'<<8) + 'E')
+#define PARSEIOC_DISABLE (('D'<<8) + 'D')
+#define PARSEIOC_SETSTAT (('D'<<8) + 'S')
+#define PARSEIOC_GETSTAT (('D'<<8) + 'G')
+#define PARSEIOC_SETFMT (('D'<<8) + 'f')
+#define PARSEIOC_GETFMT (('D'<<8) + 'F')
+#define PARSEIOC_SETCS (('D'<<8) + 'C')
+#define PARSEIOC_TIMECODE (('D'<<8) + 'T')
+
+#endif
+
+/*------ IO handling flags (sorry) ------*/
+
+#define PARSE_IO_CSIZE 0x00000003
+#define PARSE_IO_CS5 0x00000000
+#define PARSE_IO_CS6 0x00000001
+#define PARSE_IO_CS7 0x00000002
+#define PARSE_IO_CS8 0x00000003
+
+/*
+ * sizes
+ */
+#define PARSE_TCMAX 128
+
+/*
+ * ioctl structure
+ */
+union parsectl
+{
+ struct parsestatus
+ {
+ u_long flags; /* new/old flags */
+ } parsestatus;
+
+ struct parsegettc
+ {
+ u_long parse_state; /* last state */
+ u_long parse_badformat; /* number of bad packets since last query */
+ unsigned short parse_format;/* last decoded format */
+ unsigned short parse_count; /* count of valid time code bytes */
+ char parse_buffer[PARSE_TCMAX+1]; /* timecode buffer */
+ } parsegettc;
+
+ struct parseformat
+ {
+ unsigned short parse_format;/* number of examined format */
+ unsigned short parse_count; /* count of valid string bytes */
+ char parse_buffer[PARSE_TCMAX+1]; /* format code string */
+ } parseformat;
+
+ struct parsesetcs
+ {
+ u_long parse_cs; /* character size (needed for stripping) */
+ } parsesetcs;
+};
+
+typedef union parsectl parsectl_t;
+
+/*------ for conversion routines --------*/
+
+#define PARSE_DELTA 16
+
+struct parse /* parse module local data */
+{
+ int parse_flags; /* operation and current status flags */
+
+ int parse_ioflags; /* io handling flags (5-8 Bit control currently) */
+ int parse_syncflags; /* possible sync events (START/END/character) */
+ /*
+ * RS232 input parser information
+ */
+ unsigned char parse_startsym[32]; /* possible start packet values */
+ unsigned char parse_endsym[32]; /* possible end packet values */
+ unsigned char parse_syncsym[32]; /* sync characters */
+ struct timeval parse_timeout; /* max gap between characters (us) */
+
+ /*
+ * PPS 'input' buffer
+ */
+ struct timeval parse_lastone; /* time stamp of last PPS 1 transition */
+ struct timeval parse_lastzero; /* time stamp of last PPS 0 transition */
+
+ /*
+ * character input buffer
+ */
+ timestamp_t parse_lastchar; /* time stamp of last received character */
+
+ /*
+ * private data - fixed format only
+ */
+ unsigned short parse_plen; /* length of private data */
+ void *parse_pdata; /* private data pointer */
+
+ /*
+ * time code input buffer (from RS232 or PPS)
+ */
+ unsigned short parse_index; /* current buffer index */
+ char *parse_data; /* data buffer */
+ unsigned short parse_dsize; /* size of data buffer */
+ unsigned short parse_lformat; /* last format used */
+ u_long parse_lstate; /* last state code */
+ char *parse_ldata; /* last data buffer */
+ unsigned short parse_ldsize; /* last data buffer length */
+ u_long parse_badformat; /* number of unparsable pakets */
+
+ /*
+ * time stamp filtering
+ */
+ long parse_delta[PARSE_DELTA]; /* delta buffer */
+ int parse_dindex;
+
+ parsetime_t parse_dtime; /* external data prototype */
+};
+
+typedef struct parse parse_t;
+
+struct clocktime /* clock time broken up from time code */
+{
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in seconds */
+ time_t utctime; /* the actual time - alternative to date/time */
+ long flags; /* current clock status */
+};
+
+typedef struct clocktime clocktime_t;
+
+/*
+ * clock formats specify routines to be called to
+ * convert the buffer into a struct clock.
+ * functions are called
+ * fn(buffer, data, clock) -> CVT_NONE, CVT_FAIL, CVT_OK
+ *
+ * the private data pointer can be used to
+ * distingush between different formats of a common
+ * base type
+ */
+#define F_START 0x00000001 /* start packet delimiter */
+#define F_END 0x00000002 /* end packet delimiter */
+#define SYNC_TIMEOUT 0x00000004 /* packet restart after timeout */
+#define SYNC_START 0x00000008 /* packet start is sync event */
+#define SYNC_END 0x00000010 /* packet end is sync event */
+#define SYNC_CHAR 0x00000020 /* special character is sync event */
+#define SYNC_ONE 0x00000040 /* PPS synchronize on 'ONE' transition */
+#define SYNC_ZERO 0x00000080 /* PPS synchronize on 'ZERO' transition */
+#define SYNC_SYNTHESIZE 0x00000100 /* generate intermediate time stamps */
+#define CVT_FIXEDONLY 0x00010000 /* convert only in fixed configuration */
+
+/*
+ * parser related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_SKIP 0x00000008 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+#define CVT_BADDATE 0x00000020 /* date field incorrect */
+#define CVT_BADTIME 0x00000040 /* time field incorrect */
+
+struct clockformat
+{
+ u_long (*input)(); /* special input protocol - implies fixed format */
+ u_long (*convert)(); /* conversion routine */
+ void (*syncevt)(); /* routine for handling RS232 sync events (time stamps) */
+ u_long (*syncpps)(); /* PPS input routine */
+ u_long (*synth)(); /* time code synthesizer */
+ void *data; /* local parameters */
+ char *name; /* clock format name */
+ unsigned short length; /* maximum length of data packet */
+ u_long flags; /* valid start symbols etc. */
+ unsigned short plen; /* length of private data - implies fixed format */
+ struct timeval timeout; /* buffer restart after timeout (us) */
+ unsigned char startsym; /* start symbol */
+ unsigned char endsym; /* end symbol */
+ unsigned char syncsym; /* sync symbol */
+};
+
+typedef struct clockformat clockformat_t;
+
+/*
+ * parse interface
+ */
+extern int parse_ioinit P((parse_t *));
+extern void parse_ioend P((parse_t *));
+extern int parse_ioread P((parse_t *, unsigned char, timestamp_t *));
+extern int parse_iopps P((parse_t *, int, timestamp_t *));
+extern void parse_iodone P((parse_t *));
+
+extern int parse_getstat P((parsectl_t *, parse_t *));
+extern int parse_setstat P((parsectl_t *, parse_t *));
+extern int parse_timecode P((parsectl_t *, parse_t *));
+extern int parse_getfmt P((parsectl_t *, parse_t *));
+extern int parse_setfmt P((parsectl_t *, parse_t *));
+extern int parse_setcs P((parsectl_t *, parse_t *));
+
+extern int Strok P((char *, char *));
+extern int Stoi P((char *, long *, int));
+
+extern time_t parse_to_unixtime P((clocktime_t *, u_long *));
+extern u_long updatetimeinfo P((parse_t *, time_t, u_long, u_long));
+extern void syn_simple P((parse_t *, timestamp_t *, struct format *, u_long));
+extern u_long pps_simple P((parse_t *, int, timestamp_t *));
+#endif
+
+/*
+ * History:
+ *
+ * parse.h,v
+ * Revision 3.21 1994/05/30 20:58:34 kardel
+ * fix prototypes
+ *
+ * Revision 3.20 1994/05/30 10:19:44 kardel
+ * LONG cleanup
+ *
+ * Revision 3.19 1994/05/15 11:30:33 kardel
+ * documented flag4 as statistics enable flag
+ *
+ * Revision 3.18 1994/05/12 12:40:34 kardel
+ * shut up gcc about broken Sun/BSD code
+ *
+ * Revision 3.17 1994/03/03 09:27:20 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.13 1994/01/25 19:04:21 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.12 1994/01/23 17:23:05 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.11 1993/11/11 11:20:18 kardel
+ * declaration fixes
+ *
+ * Revision 3.10 1993/11/01 19:59:48 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.9 1993/10/06 00:14:57 kardel
+ * include fixes
+ *
+ * Revision 3.8 1993/10/05 23:15:41 kardel
+ * more STREAM protection
+ *
+ * Revision 3.7 1993/10/05 22:56:10 kardel
+ * STREAM must be defined for PARSESTREAMS
+ *
+ * Revision 3.6 1993/10/03 19:10:28 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/26 23:41:13 kardel
+ * new parse driver logic
+ *
+ * Revision 3.4 1993/09/01 21:46:31 kardel
+ * conditional cleanup
+ *
+ * Revision 3.3 1993/08/27 00:29:29 kardel
+ * compilation cleanup
+ *
+ * Revision 3.2 1993/07/09 11:37:05 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 09:59:12 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/include/parse_conf.h b/usr.sbin/xntpd/include/parse_conf.h
new file mode 100644
index 0000000..3c512aa
--- /dev/null
+++ b/usr.sbin/xntpd/include/parse_conf.h
@@ -0,0 +1,54 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp
+ *
+ * parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __PARSE_CONF_H__
+#define __PARSE_CONF_H__
+#if !(defined(lint) || defined(__GNUC__))
+ static char dcfhrcsid[]="parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp FAU";
+#endif
+
+/*
+ * field location structure (Meinberg clocks/simple format)
+ */
+#define O_DAY 0
+#define O_MONTH 1
+#define O_YEAR 2
+#define O_HOUR 3
+#define O_MIN 4
+#define O_SEC 5
+#define O_WDAY 6
+#define O_FLAGS 7
+#define O_ZONE 8
+#define O_UTCHOFFSET 9
+#define O_UTCMOFFSET 10
+#define O_UTCSOFFSET 11
+#define O_COUNT (O_UTCSOFFSET+1)
+
+#define MBG_EXTENDED 0x00000001
+
+/*
+ * see below for field offsets
+ */
+
+struct format
+{
+ struct foff
+ {
+ char offset; /* offset into buffer */
+ char length; /* length of field */
+ } field_offsets[O_COUNT];
+ char *fixed_string; /* string with must be chars (blanks = wildcards) */
+ u_long flags;
+};
+#endif
diff --git a/usr.sbin/xntpd/include/sys/bsd_audioirig.h b/usr.sbin/xntpd/include/sys/bsd_audioirig.h
new file mode 100644
index 0000000..6a23260
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/bsd_audioirig.h
@@ -0,0 +1,101 @@
+/*
+ * $Header: bsd_audioirig.h,v 1.0 93/08/02 12:42:00
+ */
+
+#ifndef _BSD_AUDIOIRIG_H_
+#define _BSD_AUDIOIRIG_H_
+
+#include <sys/time.h>
+
+/********************************************************************/
+/* user interface */
+
+/*
+ * irig ioctls
+ */
+#if defined(__STDC__) || !(defined(ibm032) && !defined(__GNUC))
+#define AUDIO_IRIG_OPEN _IO('A', 50)
+#define AUDIO_IRIG_CLOSE _IO('A', 51)
+#define AUDIO_IRIG_SETFORMAT _IOWR('A', 52, int)
+#else
+#define AUDIO_IRIG_OPEN _IO(A, 50)
+#define AUDIO_IRIG_CLOSE _IO(A, 51)
+#define AUDIO_IRIG_SETFORMAT _IOWR(A, 52, int)
+#endif
+
+/*
+ * irig error codes
+ */
+#define AUDIO_IRIG_BADSIGNAL 0x01
+#define AUDIO_IRIG_BADDATA 0x02
+#define AUDIO_IRIG_BADSYNC 0x04
+#define AUDIO_IRIG_BADCLOCK 0x08
+#define AUDIO_IRIG_OLDDATA 0x10
+
+/********************************************************************/
+
+/*
+ * auib definitions
+ */
+#define AUIB_SIZE (0x0040)
+#define AUIB_INC (0x0008)
+#define AUIB_MOD(k) ((k) & 0x0038)
+#define AUIB_INIT(ib) ((ib)->ib_head = (ib)->ib_tail = (ib)->ib_lock = \
+ (ib)->phase = (ib)->shi = (ib)->slo = (ib)->high = \
+ (ib)->level0 = (ib)->level1 = \
+ (ib)->shift[0] = (ib)->shift[1] = (ib)->shift[2] = \
+ (ib)->shift[3] = (ib)->sdata[0] = (ib)->sdata[1] = \
+ (ib)->sdata[2] = (ib)->sdata[3] = (ib)->err = 0)
+#define AUIB_EMPTY(ib) ((ib)->ib_head == (ib)->ib_tail)
+#define AUIB_LEN(ib) (AUIB_MOD((ib)->ib_tail - (ib)->ib_head))
+#define AUIB_LEFT(ib) (AUIB_MOD((ib)->ib_head - (ib)->ib_tail - 1))
+#define IRIGDELAY 3
+#define IRIGLEVEL 1355
+
+#ifndef LOCORE
+/*
+ * irig_time holds IRIG data for one second
+ */
+struct irig_time {
+ struct timeval stamp; /* timestamp */
+ u_char bits[13]; /* 100 irig data bits */
+ u_char status; /* status byte */
+ char time[14]; /* time string */
+};
+
+/*
+ * auib's are used for IRIG data communication between the trap
+ * handler and the software interrupt.
+ */
+struct auib {
+ /* driver variables */
+ u_short active; /* 0=inactive, else=active */
+ u_short format; /* time output format */
+ struct irig_time timestr; /* time structure */
+ char buffer[14]; /* output formation buffer */
+
+ /* hardware interrupt variables */
+ struct timeval tv1,tv2,tv3; /* time stamps (median filter) */
+ int level0,level1; /* lo/hi input levels */
+ int level; /* decision level */
+ int high; /* recent largest sample */
+ int sl0,sl1; /* recent sample levels */
+ int lasts; /* last sample value */
+ u_short scount; /* sample count */
+ u_long eacc; /* 10-bit element accumulator */
+ u_long ebit; /* current bit in element */
+ u_char r_level,mmr1; /* recording level 0-255 */
+ int shi,slo,phase; /* AGC variables */
+ u_long err; /* error status bits */
+ int ecount; /* count of elements this second */
+ long shift[4]; /* shift register of pos ident */
+ long sdata[4]; /* shift register of symbols */
+
+ int ib_head; /* queue head */
+ int ib_tail; /* queue tail */
+ u_short ib_lock; /* queue head lock */
+ u_long ib_data[AUIB_SIZE]; /* data buffer */
+};
+#endif
+
+#endif /* _BSD_AUDIOIRIG_H_ */
diff --git a/usr.sbin/xntpd/include/sys/chudefs.h b/usr.sbin/xntpd/include/sys/chudefs.h
new file mode 100644
index 0000000..f5549f5
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/chudefs.h
@@ -0,0 +1,22 @@
+/*
+ * Definitions for the CHU line discipline v2.0
+ */
+
+/*
+ * The CHU time code consists of 10 BCD digits and is repeated
+ * twice for a total of 10 characters. A time is taken after
+ * the arrival of each character. The following structure is
+ * used to return this stuff.
+ */
+#define NCHUCHARS (10)
+
+struct chucode {
+ u_char codechars[NCHUCHARS]; /* code characters */
+ u_char ncodechars; /* number of code characters */
+ u_char chutype; /* packet type */
+ struct timeval codetimes[NCHUCHARS]; /* arrival times */
+};
+
+#define CHU_TIME 0 /* second half is equal to first half */
+#define CHU_YEAR 1 /* second half is one's complement */
+
diff --git a/usr.sbin/xntpd/include/sys/clkdefs.h b/usr.sbin/xntpd/include/sys/clkdefs.h
new file mode 100644
index 0000000..afbc77a
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/clkdefs.h
@@ -0,0 +1,38 @@
+/*
+ * Defines for the "clk" timestamping STREAMS module
+ */
+
+#if defined(sun)
+#include <sys/ioccom.h>
+#else
+#include <sys/ioctl.h>
+#endif
+
+/*
+ * First, we need to define the maximum size of the set of
+ * characters to timestamp. 32 is MORE than enough.
+ */
+
+#define CLK_MAXSTRSIZE 32
+struct clk_tstamp_charset { /* XXX to use _IOW not _IOWN */
+ char val[CLK_MAXSTRSIZE];
+};
+
+/*
+ * ioctl(fd, CLK_SETSTR, (char*)c );
+ *
+ * will tell the driver that any char in the null-terminated
+ * string c should be timestamped. It is possible, though
+ * unlikely that this ioctl number could collide with an
+ * existing one on your system. If so, change the 'K'
+ * to some other letter. However, once you've compiled
+ * the kernel with this include file, you should NOT
+ * change this file.
+ */
+
+#if defined(__STDC__) /* XXX avoid __STDC__=0 on SOLARIS */
+#define CLK_SETSTR _IOW('K', 01, struct clk_tstamp_charset)
+#else
+#define CLK_SETSTR _IOW(K, 01, struct clk_tstamp_charset)
+#endif
+
diff --git a/usr.sbin/xntpd/include/sys/parsestreams.h b/usr.sbin/xntpd/include/sys/parsestreams.h
new file mode 100644
index 0000000..ac66f42
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/parsestreams.h
@@ -0,0 +1,66 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/sys/parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp
+ *
+ * parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#if !(defined(lint) || defined(__GNUC__))
+ static char parse77hrcsid[]="parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp";
+#endif
+
+#undef PARSEKERNEL
+#if defined(KERNEL) || defined(_KERNEL)
+#ifndef PARSESTREAM
+#define PARSESTREAM
+#endif
+#endif
+#if defined(PARSESTREAM) && defined(STREAM)
+#define PARSEKERNEL
+#include <sys/ppsclock.h>
+
+struct parsestream /* parse module local data */
+{
+ queue_t *parse_queue; /* read stream for this channel */
+ queue_t *parse_dqueue; /* driver queue entry (PPS support) */
+ unsigned long parse_status; /* operation flags */
+ void *parse_data; /* local data space (PPS support) */
+ parse_t parse_io; /* io structure */
+ struct ppsclockev parse_ppsclockev; /* copy of last pps event */
+};
+
+typedef struct parsestream parsestream_t;
+
+#define PARSE_ENABLE 0x0001
+
+/*--------------- debugging support ---------------------------------*/
+
+#ifdef DEBUG_PARSE
+
+extern int parsedebug;
+
+#define DD_OPEN 0x00000001
+#define DD_CLOSE 0x00000002
+#define DD_RPUT 0x00000004
+#define DD_WPUT 0x00000008
+#define DD_RSVC 0x00000010
+#define DD_PARSE 0x00000020
+#define DD_INSTALL 0x00000040
+#define DD_ISR 0x00000080
+#define DD_RAWDCF 0x00000100
+
+#define parseprintf(X, Y) if ((X) & parsedebug) printf Y
+
+#else
+
+#define parseprintf(X, Y)
+
+#endif
+#endif
diff --git a/usr.sbin/xntpd/include/sys/ppsclock.h b/usr.sbin/xntpd/include/sys/ppsclock.h
new file mode 100644
index 0000000..edf28aa
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/ppsclock.h
@@ -0,0 +1,58 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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 PPSCLOCKSTR "ppsclock"
+
+struct ppsclockev {
+ struct timeval tv;
+ u_int serial;
+};
+
+#if defined(__STDC__) || defined(SYS_HPUX)
+#ifdef _IOR
+#define CIOGETEV _IOR('C', 0, struct ppsclockev) /* get last pps event */
+#else /* XXX SOLARIS is different */
+#define CIO ('C'<<8)
+#define CIOGETEV (CIO|0) /* get last pps event */
+#endif /* _IOR */
+#else /* __STDC__ */
+#ifdef _IOR
+#define CIOGETEV _IOR(C, 0, struct ppsclockev) /* get last pps event */
+#else /* XXX SOLARIS is different */
+#define CIO ('C'<<8)
+#define CIOGETEV (CIO|0) /* get last pps event */
+#endif /* _IOR */
+#endif /* __STDC__ */
diff --git a/usr.sbin/xntpd/include/sys/timex.h b/usr.sbin/xntpd/include/sys/timex.h
new file mode 100644
index 0000000..bc2d634
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/timex.h
@@ -0,0 +1,290 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * 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 appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * This header file defines the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. These are implemented using
+ * private syscalls and data structures and require specific kernel
+ * support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ */
+#ifndef _SYS_TIMEX_H_
+#define _SYS_TIMEX_H_ 1
+
+#ifndef MSDOS /* Microsoft specific */
+#include <sys/syscall.h>
+#endif /* MSDOS */
+
+/*
+ * The following defines establish the engineering parameters of the
+ * phase-lock loop (PLL) model used in the kernel implementation. These
+ * parameters have been carefully chosen by analysis for good stability
+ * and wide dynamic range.
+ *
+ * The hz variable is defined in the kernel build environment. It
+ * establishes the timer interrupt frequency, 100 Hz for the SunOS
+ * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1
+ * kernel. SHIFT_HZ expresses the same value as the nearest power of two
+ * in order to avoid hardware multiply operations.
+ *
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_HZ 7 /* log2(hz) */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The following defines establish the scaling of the various variables
+ * used by the PLL. They are chosen to allow the greatest precision
+ * possible without overflow of a 32-bit word.
+ *
+ * SHIFT_SCALE defines the scaling (shift) of the time_phase variable,
+ * which serves as a an extension to the low-order bits of the system
+ * clock variable time.tv_usec.
+ *
+ * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable,
+ * which represents the current time offset with respect to standard
+ * time.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 23 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+/*
+ * The following defines establish the performance envelope of the PLL.
+ * They insure it operates within predefined limits, in order to satisfy
+ * correctness assertions. An excursion which exceeds these bounds is
+ * clamped to the bound and operation proceeds accordingly. In practice,
+ * this can occur only if something has failed or is operating out of
+ * tolerance, but otherwise the PLL continues to operate in a stable
+ * mode.
+ *
+ * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as
+ * defined in the NTP specification. CLOCK.MAX establishes the maximum
+ * time offset allowed before the system time is reset, rather than
+ * incrementally adjusted. Here, the maximum offset is clamped to
+ * MAXPHASE only in order to prevent overflow errors due to defective
+ * protocol implementations.
+ *
+ * MAXFREQ is the maximum frequency tolerance of the CPU clock
+ * oscillator plus the maximum slew rate allowed by the protocol. It
+ * should be set to at least the frequency tolerance of the oscillator
+ * plus 100 ppm for vernier frequency adjustments. If the kernel
+ * PPS discipline code is configured (PPS_SYNC), the oscillator time and
+ * frequency are disciplined to an external source, presumably with
+ * negligible time and frequency error relative to UTC, and MAXFREQ can
+ * be reduced.
+ *
+ * MAXTIME is the maximum jitter tolerance of the PPS signal if the
+ * kernel PPS discipline code is configured (PPS_SYNC).
+ *
+ * MINSEC and MAXSEC define the lower and upper bounds on the interval
+ * between protocol updates.
+ */
+#define MAXPHASE 128000L /* max phase error (us) */
+#ifdef PPS_SYNC
+#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (100 ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#else
+#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (200 ppm) */
+#endif /* PPS_SYNC */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+#endif /* PPS_SYNC */
+
+/*
+ * The following defines and structures define the user interface for
+ * the ntp_gettime() and ntp_adjtime() system calls.
+ *
+ * Control mode codes (timex.modes)
+ */
+#define MOD_OFFSET 0x0001 /* set time offset */
+#define MOD_FREQUENCY 0x0002 /* set frequency offset */
+#define MOD_MAXERROR 0x0004 /* set maximum time error */
+#define MOD_ESTERROR 0x0008 /* set estimated time error */
+#define MOD_STATUS 0x0010 /* set clock status bits */
+#define MOD_TIMECONST 0x0020 /* set pll time constant */
+#define MOD_CLKB 0x4000 /* set clock B */
+#define MOD_CLKA 0x8000 /* set clock A */
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* no leap second warning */
+#define TIME_INS 1 /* insert leap second warning */
+#define TIME_DEL 2 /* delete leap second warning */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occured */
+#define TIME_ERROR 5 /* clock not synchronized */
+
+/*
+ * NTP user interface (ntp_gettime()) - used to read kernel clock values
+ *
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ long maxerror; /* maximum error (us) (ro) */
+ long esterror; /* estimated error (us) (ro) */
+};
+
+/*
+ * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock
+ * oscillator
+ */
+struct timex {
+ unsigned int modes; /* clock mode bits (wo) */
+ long offset; /* time offset (us) (rw) */
+ long freq; /* frequency offset (scaled ppm) (rw) */
+ long maxerror; /* maximum error (us) (rw) */
+ long esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ long constant; /* pll time constant (rw) */
+ long precision; /* clock precision (us) (ro) */
+ long tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+
+};
+#ifdef __FreeBSD__
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int ntp_gettime __P((struct ntptimeval *));
+extern int ntp_adjtime __P((struct timex *));
+__END_DECLS
+
+#endif /* not KERNEL */
+
+#endif /* __FreeBSD__ */
+#endif /* _SYS_TIMEX_H_ */
diff --git a/usr.sbin/xntpd/include/sys/tpro.h b/usr.sbin/xntpd/include/sys/tpro.h
new file mode 100644
index 0000000..f276f81
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/tpro.h
@@ -0,0 +1,34 @@
+/*
+ * Structure for the KSI/Odetics TPRO-S data returned in reponse to a
+ * read() call. Note that these are driver-specific and not dependent on
+ * 32/64-bit architecture.
+ */
+struct tproval {
+ u_short day100; /* days * 100 */
+ u_short day10; /* days * 10 */
+ u_short day1; /* days * 1 */
+ u_short hour10; /* hours * 10 */
+ u_short hour1; /* hours * 1 */
+ u_short min10; /* minutes * 10 */
+ u_short min1; /* minutes * 1 */
+ u_short sec10; /* seconds * 10 */
+ u_short sec1; /* seconds * 1*/
+ u_short ms100; /* milliseconds * 100 */
+ u_short ms10; /* milliseconds * 10 */
+ u_short ms1; /* milliseconds * 1 */
+ u_short usec100; /* microseconds * 100 */
+ u_short usec10; /* microseconds * 10 */
+ u_short usec1; /* microseconds * 1 */
+ long tv_sec; /* seconds */
+ long tv_usec; /* microseconds */
+ u_short status; /* status register */
+};
+
+/*
+ * Status register bits
+ */
+#define TIMEAVAIL 0x0001 /* time available */
+#define NOSIGNAL 0x0002 /* insufficient IRIG-B signal */
+#define NOSYNC 0x0004 /* local oscillator not synchronized */
+
+/* end of tpro.h */
diff --git a/usr.sbin/xntpd/kernel/README.kern b/usr.sbin/xntpd/kernel/README.kern
new file mode 100644
index 0000000..64ba9c5
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/README.kern
@@ -0,0 +1,596 @@
+Precision Time and Frequency Synchronization Using Modified Kernels
+
+1. Introduction
+
+This memo describes replacements for certain SunOS and Ultrix kernel
+routines that manage the system clock and timer functions. They provide
+improved accuracy and stability through the use of a disciplined clock
+interface for use with the Network Time Protocol (NTP) or similar time-
+synchronization protocol. In addition, for certain models of the
+DECstation 5000 product line, the new routines provide improved
+precision to +-1 microsecond (us) (SunOS 4.1.1 already does provide
+precision to +-1 us). The current public NTP distribution cooperates
+with these kernel routines to provide synchronization in principle to
+within a microsecond, but in practice this is limited by the short-term
+stability of the oscillator that drives the timer interrupt.
+
+This memo describes the principles behind the design and operation of
+the software. There are two versions of the software, one that operates
+with the SunOS 4.1.1 kernel and the other that operates with the Ultrix
+4.2a kernel (and probably the 4.3 kernel, although this has not been
+tested). A detailed description of the variables and algorithms is given
+in the hope that similar improvements can be incorporated in Unix
+kernels for other machines. The software itself is not included in this
+memo, since it involves licensed code. Detailed instructions on where to
+obtain it for either SunOS or Ultrix will be given separately.
+
+The principle function added to the SunOS and Ultrix kernels is to
+change the way the system clock is controlled, in order to provide
+precision time and frequency adjustments. Another function utilizes an
+undocumented counter in the DECstation hardware to provide precise time
+to the microsecond. This function can be used only with the DECstation
+5000/240 and possibly others that use the same input/output chipset.
+
+2. Design Principles
+
+In order to understand how these routines work, it is useful to consider
+how most Unix systems maintain the system clock. In the original design
+a hardware timer interrupts the kernel at some fixed rate, such as 100
+Hz in the SunOS kernel and 256 Hz in the Ultrix kernel. Since 256 does
+not evenly divide the second in microseconds, the kernel inserts 64 us
+once each second so that the system clock stays in step with real time.
+The time returned by the gettimeofday() routine is thus characterized by
+255 advances of 3906 us plus one of 3970 us.
+
+Also in the original design it is possible to slew the system clock to a
+new offset using the adjtime() system call. To do this the clock
+frequency is changed by adding or subtracting a fixed amount (tickadj)
+at each timer interrupt (tick) for a calculated number of ticks. Since
+this calculation involves dividing the requested offset by tickadj, it
+is possible to slew to a new offset with a precision only of tickadj,
+which is usually in the neighborhood of 5 us, but sometimes much higher.
+
+In order to maintain the system clock within specified bounds with this
+scheme, it is necessary to call adjtime() on a regular basis. For
+instance, let the bound be set at 100 us, which is a reasonable value
+for NTP-synchronized hosts on a local network, and let the onboard
+oscillator tolerance be 100 ppm, which is a reasonably conservative
+assumption. This requires that adjtime() be called at intervals not
+exceeding 1 second (s), which is in fact what the unmodified NTP
+software daemon does.
+
+In the modified kernel routines this scheme is replaced by another that
+extends the low-order bits of the system clock to provide very precise
+clock adjustments. At each timer interrupt a precisely calibrated time
+adjustment is added to the composite time value and overflows handled as
+required. The quantity to add is computed from the adjtime() call and,
+in addition a frequency adjustment, which is automatically calculated
+from previous time adjustments. This implementation operates as an
+adaptive-parameter, first-order, type-II, phase-lock loop (PLL), which
+in principle provides precision control of the system clock phase to
+within +-1 us and frequency to within +-5 nanoseconds (ns) per day.
+
+This PLL model is identical to the one implemented in NTP, except that
+in NTP the software daemon has to simulate the PLL using only the
+original adjtime() system call. The daemon is considerably complicated
+by the need to parcel time adjustments at frequent intervals in order to
+maintain the accuracy to specified bounds. The kernel routines do this
+directly, allowing vast gobs of ugly daemon code to be avoided at the
+expense of only a small amount of new code in the kernel. In fact, the
+amount of code added to the kernel for the new scheme is about the
+amount removed for the old scheme. The new adjtime() routine needs to be
+called only as each new time update is determined, which in NTP occurs
+at intervals of from 64 s to 1024 s. In addition, doing the frequency
+correction in the kernel means that the system time runs true even if
+the daemon were to cease operation or the network paths to the primary
+reference source fail.
+
+Note that the degree to which the adjtime() adjustment can be made is
+limited to a specific maximum value, presently +-128 milliseconds (ms),
+in order to achieve microsecond resolution. It is the intent in the
+design that settimeofday() be used for changes in system time greater
+than +-128 ms. It has been the Internet experience that the need to
+change the system time in increments greater than +-128 milliseconds is
+extremely rare and is usually associated with a hardware or software
+malfunction. Nevertheless, the limit applies to each adjtime() call and
+it is possible, but not recommended, that this routine is called at
+intervals smaller than 64 seconds, which is the NTP lower limit.
+
+For the most accurate and stable operation, adjtime() should be called
+at specified intervals; however, the PLL is quite forgiving and neither
+moderate loss of updates nor variations in the length of the interval is
+serious. The current engineering parameters have been optimized for
+intervals not greater than about 64 s. For larger intervals the PLL time
+constant can be adjusted to optimize the dynamic response up to
+intervals of 1024 s. Normally, this is automatically done by NTP. In any
+case, if updates are suspended, the PLL coasts at the frequency last
+determinated, which usually results in errors increasing only to a few
+tens of milliseconds over a day.
+
+The new code needs to know the initial frequency offset and time
+constant for the PLL, and the daemon needs to know the current frequency
+offset computed by the kernel for monitoring purposes. This is provided
+by a small change in the second argument of the kernel adjtime() calling
+sequence, which is documented later in this memo. Ordinarily, only the
+daemon will call the adjtime() routine, so the modified calling sequence
+is easily accommodated. Other than this change, the operation of
+adjtime() is transparent to the original.
+
+In the DECstation 5000/240 and possibly other models there happens to be
+an undocumented hardware register that counts system bus cycles at a
+rate of 25 MHz. The new kernel routines test for the CPU type and, in
+the case of the '240, use this register to interpolate system time
+between hardware timer interrupts. This results in a precision of +-1 us
+for all time values obtained via the gettimeofday() system call. This
+routine calls the kernel routine microtime(), which returns the actual
+interpolated value, but does not change the kernel time variable.
+Therefore, other kernel routines that access the kernel time variable
+directly and do not call either gettimeofday() or microtime() will
+continue their present behavior.
+
+The new kernel routines include provisions for error statistics (maximum
+error and estimated error), leap seconds and system clock status. These
+are intended to support applications that need such things; however,
+there are no applications other than the time-synchronization daemon
+itself that presently use them. At issue is the manner in which these
+data can be provided to application clients, such as new system calls
+and data interfaces. While a proposed interface is described later in
+this memo, it has not yet been implemented. This is an area for further
+study.
+
+While any time-synchronization daemon can in principle be modified to
+use the new code, the most likely will be users of the xntp3
+distribution of NTP. The code in the xntp3 distribution determines
+whether the new kernel code is in use and automatically reconfigures as
+required. When the new code is in use, the daemon reads the frequency
+offset from a file and provides it and the initial time constant via
+adjtime(). In subsequent calls to adjtime(), only the time adjustment
+and time constant are affected. The daemon reads the frequency from the
+kernel (returned as the second argument of adjtime()) at intervals of
+one hour and writes it to the file.
+
+3. Technical Description
+
+Following is a technical description of how the new scheme works in
+terms of the variables and algorithms involved. These components are
+discussed as a distinct entity and do not involve coding details
+specific to the Ultrix kernel. The algorithms involve only minor changes
+to the system clock and interval timer routines, but do not in
+themselves provide a conduit for application programs to learn the
+system clock status or statistics of the time-synchronization process.
+In a later section a number of new system calls are proposed to do this,
+along with an interface specification.
+
+The new scheme works like the companion simulator called kern.c and
+included in this directory. This stand-alone simulator includes code
+fragments identical to those in the modified kernel routines and
+operates in the same way. The system clock is implemented in the kernel
+using a set of variables and algorithms defined below and in the
+simulator. The algorithms are driven by explicit calls from the
+synchronization protocol as each time update is computed. The clock is
+read and set using the gettimeofday() and settimeofday() system calls,
+which operate in the same way as the originals, but return a status word
+describing the state of the system clock.
+
+Once the system clock has been set, the adjtime() system call is used to
+provide periodic updates including the time offset and possibly
+frequency offset and time constant. With NTP this occurs at intervals of
+from 64 s to 1024 s, deending on the time constant value. The kernel
+implements an adaptive-parameter, first-order, type-II, phase-lock loop
+(PLL) in order to integrate this offset into the phase and frequency of
+the system clock. The kernel keeps track of the time of the last update
+and adjusts the maximum error to grow by an amount equal to the
+oscillator frequency tolerance times the elapsed time since the last
+update.
+
+Occasionally, it is necessary to adjust the PLL parameters in response
+to environmental conditions, such as leap-second warning and oscillator
+stability observations. While the interface to do this has not yet been
+implemented, proposals to to that are included in a later section. A
+system call (setloop()) is used on such occasions to communicate these
+data. In addition, a system call (getloop())) is used to extract these
+data from the kernel for monitoring purposes.
+
+All programs utilize the system clock status variable time_status, which
+records whether the clock is synchronized, waiting for a leap second,
+etc. The value of this variable is returned by each system call. It can
+be set explicitly by the setloop() system call and implicitly by the
+settimeofday() system call and in the timer-interrupt routine. Values
+presently defined in the header file timex.h are as follows:
+
+int time_status = TIME_BAD; /* clock synchronization status */
+
+#define TIME_UNS 0 /* unspecified or unknown */
+#define TIME_OK 1 /* operation succeeded */
+#define TIME_INS 1 /* insert leap second at end of current day */
+#define TIME_DEL 2 /* delete leap second at end of current day */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_BAD 4 /* system clock is not synchronized */
+#define TIME_ADR -1 /* operation failed: invalid address */
+#define TIME_VAL -2 /* operation failed: invalid argument */
+#define TIME_PRV -3 /* operation failed: priviledged operation */
+
+In case of a negative result code, the operation has failed; however,
+some variables may have been modified before the error was detected.
+Note that the new system calls never return a value of zero, so it is
+possible to determine whether the old routines or the new ones are in
+use. The syntax of the modified adjtime() is as follows:
+
+/*
+ * adjtime - adjuts system time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, fiddle)
+
+struct timeval *tp; /* system time adjustment*/
+struct timeval *fiddle; /* sneak path */
+
+On entry the "timeval" sneak path is coded:
+
+struct timeval {
+ long tv_sec = time_constant; /* time constant */
+ long tv_usec = time_freq; /* new frequency offset */
+}
+
+However, the sneak is ignored if fiddle is the null pointer and the new
+frequency offset is ignored if zero.
+
+The value returned on exit is the system clock status defined above. The
+"timeval" sneak path is modified as follows:
+
+struct timeval {
+ long tv_sec = time_precision; /* system clock precision */
+ long tv_usec = time_freq; /* current frequency offset */
+}
+
+3.1. Kernel Variables
+
+The following variables are used by the new code:
+
+long time_offset = 0; /* time adjustment (us) */
+
+This variable is used by the PLL to adjust the system time in small
+increments. It is scaled by (1 << SHIFT_UPDATE) in binary microseconds.
+The maximum value that can be represented is about +-130 ms and the
+minimum value or precision is about one nanosecond.
+
+long time_constant = SHIFT_TAU; /* pll time constant */
+
+This variable determines the bandwidth or "stiffness" of the PLL. It is
+used as a shift, with the effective value in positive powers of two. The
+optimum value for this variable is equal to 1/64 times the update
+interval. The default value SHIFT_TAU (0) corresponds to a PLL time
+constant of about one hour or an update interval of about one minute,
+which is appropriate for typical uncompensated quartz oscillators used
+in most computing equipment. Values larger than four are not useful,
+unless the local clock timebase is derived from a precision oscillator.
+
+long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
+
+This variable represents the maximum frequency error or tolerance of the
+particular platform and is a property of the architecture. It is
+expressed as a positive number greater than zero in parts-per-million
+(ppm). The default MAXFREQ (100) is appropriate for conventional
+workstations.
+
+long time_precision = 1000000 / HZ; /* clock precision (us) */
+
+This variable represents the maximum error in reading the system clock.
+It is expressed as a positive number greater than zero in microseconds
+and is usually based on the number of microseconds between timer
+interrupts, in the case of the Ultrix kernel, 3906. However, in cases
+where the time can be interpolated between timer interrupts with
+microsecond resolution, the precision is specified as 1. This variable
+is computed by the kernel for use by the time-synchronization daemon,
+but is otherwise not used by the kernel.
+
+struct timeval time_maxerror; /* maximum error */
+
+This variable represents the maximum error, expressed as a Unix timeval,
+of the system clock. For NTP, it is computed as the synchronization
+distance, which is equal to one-half the root delay plus the root
+dispersion. It is increased by a small amount (time_tolerance) each
+second to reflect the clock frequency tolerance. This variable is
+computed by the time-synchronization daemon and the kernel for use by
+the application program, but is otherwise not used by the kernel.
+
+struct timeval time_esterror; /* estimated error */
+
+This variable represents the best estimate of the actual error,
+expressed as a Unix timeval, of the system clock based on its past
+behavior, together with observations of multiple clocks within the peer
+group. This variable is computed by the time-synchronization daemon for
+use by the application program, but is otherwise not used by the kernel.
+
+The PLL itself is controlled by the following variables:
+
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */long
+time_adj = 0; /* tick adjust (scaled 1 / HZ) */
+
+These variables control the phase increment and the frequency increment
+of the system clock at each tick of the clock. The time_phase variable
+is scaled by (1 << SHIFT_SCALE) in binary microseconds, giving a minimum
+value (time resolution) of 9.3e-10 us. The time_freq variable is scaled
+by (1 << SHIFT_KF) in parts-per-million (ppm), giving it a maximum value
+of about +-130 ppm and a minimum value (frequency resolution) of 6e-8
+ppm. The time_adj variable is the actual phase increment in scaled
+microseconds to add to time_phase once each tick. It is computed from
+time_phase and time_freq once per second.
+
+long time_reftime = 0; /* time at last adjustment (s) */
+
+This variable is the second's portion of the system time on the last
+call to adjtime(). It is used to adjust the time_freq variable as the
+time since the last update increases.
+
+The HZ define establishes the timer interrupt frequency, 256 Hz for the
+Ultrix kernel and 100 Hz for the SunOS kernel. The SHIFT_HZ define
+expresses the same value as the nearest power of two in order to avoid
+hardware multiply operations. These are the only parameters that need to
+be changed for different timer interrupt rates.
+
+#define HZ 256 /* timer interrupt frequency (Hz) */
+#define SHIFT_HZ 8 /* log2(HZ) */
+
+The following defines establish the engineering parameters of the PLL
+model. They are chosen for an initial convergence time of about an hour,
+an overshoot of about seven percent and a final convergence time of
+several hours, depending on initial frequency error.
+
+#define SHIFT_KG 10 /* shift for phase increment */
+#define SHIFT_KF 24 /* shift for frequency increment */
+#define SHIFT_TAU 0 /* default time constant (shift) */
+
+The SHIFT_SCALE define establishes the decimal point on the time_phase
+variable which serves as a an extension to the low-order bits of the
+system clock variable. The SHIFT_UPDATE define establishes the decimal
+point of the phase portion of the adjtime() update. The FINEUSEC define
+represents 1 us in scaled units.
+
+#define SHIFT_SCALE 28 /* shift for scale factor */
+#define SHIFT_UPDATE 14 /* shift for offset scale factor */
+#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in scaled units */
+
+The FINETUNE define represents the residual, in ppm, to be added to the
+system clock variable in addition to the integral 1-us value given by
+tick. This allows a systematic frequency offset in cases where the timer
+interrupt frequency does not exactly divide the second in microseconds.
+
+#define FINETUNE (1000000 - (1000000 / HZ) * HZ) /* frequency adjustment
+ * for non-isochronous HZ (ppm) */
+
+The following four defines establish the performance envelope of the
+PLL, one to bound the maximum phase error, another to bound the maximum
+frequency error and the last two to bound the minimum and maximum time
+between updates. The intent of these bounds is to force the PLL to
+operate within predefined limits in order to conform to the correctness
+models assumed by time-synchronization protocols like NTP and DTSS. An
+excursion which exceeds these bounds is clamped to the bound and
+operation proceeds accordingly. In practice, this can occur only if
+something has failed or is operating out of tolerance, but otherwise the
+PLL continues to operate in a stable mode. Note that the MAXPHASE define
+conforms to the maximum offset allowed in NTP before the system time is
+reset, rather than incrementally adjusted.
+
+#define MAXPHASE 128000 /* max phase error (us) */
+#define MINSEC 64 /* min interval between updates (s) */
+#define MAXFREQ 100 /* max frequency error (ppm) */
+#define MAXSEC 1024 /* max interval between updates (s) */
+
+3.2. Code Segments
+
+The code segments illustrated in the simulator should make clear the
+operations at various points in the code. These segments are not derived
+from any licensed code. The hardupdate() fragment is called by adjtime()
+to update the system clock phase and frequency. This is an
+implementation of an adaptive-parameter, first-order, type-II phase-lock
+loop. Note that the time constant is in units of powers of two, so that
+multiplies can be done by simple shifts. The phase variable is computed
+as the offset multiplied by the time constant. Then, the time since the
+last update is computed and clamped to a maximum (for robustness) and to
+zero if initializing. The offset is multiplied (sorry about the ugly
+multiply) by the result and by the square of the time constant and then
+added to the frequency variable. Finally, the frequency variable is
+clamped not to exceed the tolerance. Note that all shifts are assumed to
+be positive and that a shift of a signed quantity to the right requires
+a litle dance.
+
+With the defines given, the maximum time offset is determined by the
+size in bits of the long type (32) less the SHIFT_UPDATE (14) scale
+factor or 18 bits (signed). The scale factor is chosen so that there is
+no loss of significance in later steps, which may involve a right shift
+up to 14 bits. This results in a maximum offset of about +-130 ms. Since
+the time_constant must be greater than or equal to zero, the maximum
+frequency offset is determined by the SHIFT_KF (24) scale factor, or
+about +-130 ppm. In the addition step the value of offset * mtemp is
+represented in 18 + 10 = 28 bits, which will not overflow a long add.
+There could be a loss of precision due to the right shift of up to eight
+bits, since time_constant is bounded at four. This results in a net
+worst-case frequency error of about 2^-16 us or well down into the
+oscillator phase noise. While the time_offset value is assumed checked
+before entry, the time_phase variable is an accumulator, so is clamped
+to the tolerance on every call. This helps to damp transients before the
+oscillator frequency has been determined, as well as to satisfy the
+correctness assertions if the time-synchronization protocol comes
+unstuck.
+
+The hardclock() fragment is inserted in the hardware timer interrupt
+routine at the point the system clock is to be incremented. The phase
+adjustment (time_adj) is added to the clock phase (time_phase) and
+tested for overflow of the microsecond. If an overflow occurs, the
+microsecond (tick) in incremented or decremented.
+
+The second_overflow() fragment is inserted at the point where the
+microseconds field of the system time variable is being checked for
+overflow. On rollover of the second the maximum error is increased by
+the tolerance. The time offset is divided by the phase weight (SHIFT_KG)
+and time constant. The time offset is then reduced by the result and the
+result is scaled and becomes the value of the phase adjustment. The
+phase adjustment is then corrected for the calculated frequency offset
+and a fixed offset FINETUNE which is a property of the architecture. On
+rollover of the day the leap-warning indicator is checked and the
+apparent time adjusted +-1 s accordingly. The gettimeofday() routine
+insures that the reported time is always monotonically increasing.
+
+The simulator can be used to check the loop operation over the design
+range of +-128 ms in time error and +-100 ppm in frequency error. This
+confirms that no overflows occur and that the loop initially converges
+in about 50-60 minutes for timer interrupt rates from 50 Hz to 1024 Hz.
+The loop has a normal overshoot of about seven percent and a final
+convergence time of several hours, depending on the initional frequency
+error.
+
+3.3. Leap Seconds
+
+The leap-warning condition is determined by the synchronization protocol
+(if remotely synchronized), by the timecode receiver (if available), or
+by the operator (if awake). The time_status value must be set on the day
+the leap event is to occur (30 June or 31 December) and is automatically
+reset after the event. If the value is TIME_DEL, the kernel adds one
+second to the system time immediately following second 23:59:58 and
+resets time_status to TIME_OK. If the value is TIME_INS, the kernel
+subtracts one second from the system time immediately following second
+23:59:59 and resets time_status to TIME_OOP, in effect causing system
+time to repeat second 59. Immediately following the repeated second, the
+kernel resets time_status to TIME_OK.
+
+Depending upon the system call implementation, the reported time during
+a leap second may repeat (with a return code set to advertise that fact)
+or be monotonically adjusted until system time "catches up" to reported
+time. With the latter scheme the reported time will be correct before
+and after the leap second, but freeze or slowly advance during the leap
+second itself. However, Most programs will probably use the ctime()
+library routine to convert from timeval (seconds, microseconds) format
+to tm format (seconds, minutes,...). If this routine is modified to
+inspect the return code of the gettimeofday() routine, it could simply
+report the leap second as second 60.
+
+To determine local midnight without fuss, the kernel simply finds the
+residue of the time.tv_sec value mod 86,400, but this requires a messy
+divide. Probably a better way to do this is to initialize an auxiliary
+counter in the settimeofday() routine using an ugly divide and increment
+the counter at the same time the time.tv_sec is incremented in the timer
+interrupt routine. For future embellishment.
+
+4. Proposed Application Program Interface
+
+Most programs read the system clock using the gettimeofday() system
+call, which returns the system time and time-zone data. In the modified
+5000/240 kernel, the gettimeofday() routine calls the microtime()
+routine, which interpolates between hardware timer interrupts to a
+precision of +-1 microsecond. However, the synchronization protocol
+provides additional information that will be of interest in many
+applications. For some applications it is necessary to know the maximum
+error of the reported time due to all causes, including those due to the
+system clock reading error, oscillator frequency error and accumulated
+errors due to intervening time servers on the path to a primary
+reference source. However, for those protocols that adjust the system
+clock frequency as well as the time offset, the errors expected in
+actual use will almost always be much less than the maximum error.
+Therefore, it is useful to report the estimated error, as well as the
+maximum error.
+
+It does not seem useful to provide additional details private to the
+kernel and synchronization protocol, such as stratum, reference
+identifier, reference timestamp and so forth. It would in principle be
+possible for the application to independently evaluate the quality of
+time and project into the future how long this time might be "valid."
+However, to do that properly would duplicate the functionality of the
+synchronization protocol and require knowledge of many mundane details
+of the platform architecture, such as the tick value, reachability
+status and related variables. Therefore, the application interface does
+not reveal anything except the time, timezone and error data.
+
+With respect to NTP, the data maintained by the protocol include the
+roundtrip delay and total dispersion to the source of synchronization.
+In terms of the above, the maximum error is computed as half the delay
+plus the dispersion, while the estimated error is equal to the
+dispersion. These are reported in timeval structures. A new system call
+is proposed that includes all the data in the gettimeofday() plus the
+two new timeval structures.
+
+The proposed interface involves modifications to the gettimeofday(),
+settimeofday() and adjtime() system calls, as well as new system calls
+to get and set various system parameters. In order to minimize
+confusion, by convention the new system calls are named with an "x"
+following the "time"; e.g., adjtime() becomes adjtimex(). The operation
+of the modified gettimexofday(), settimexofday() and adjtimex() system
+calls is identical to that of their prototypes, except for the error
+quantities and certain other side effects, as documented below. By
+convention, a NULL pointer can be used in place of any argument, in
+which case the argument is ignored.
+
+The synchronization protocol daemon needs to set and adjust the system
+clock and certain other kernel variables. It needs to read these
+variables for monitoring purposes as well. The present list of these
+include a subset of the variables defined previously:
+
+long time_precision
+long time_timeconstant
+long time_tolerance
+long time_freq
+long time_status
+
+/*
+ * gettimexofday, settimexofday - get/set date and time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, tzp, tmaxp, testp)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+struct timeval *tmaxp; /* maximum error */
+struct timeval *testp; /* estimated error */
+
+The settimeofday() syntax is identical. Note that a call to
+settimexofday() automatically results in the system being declared
+unsynchronized (TIME_BAD return code), since the synchronization
+condition can only be achieved by the synchronization daemon using an
+internal or external primary reference source and the adjtimex() system
+call.
+
+/*
+ * adjtimex - adjust system time
+ */
+#include <sys/timex.h>
+
+int adjtimex(tp, tzp, freq, tc)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+long freq; /* frequency adjustment */
+long tc; /* time constant */
+
+/*
+ * getloop, setloop - get/set kernel time variables
+ */
+#include <sys/timex.h>
+
+int getloop(code, argp)
+
+int code; /* operation code */
+long *argp; /* argument pointer */
+
+The paticular kernal variables affected by these routines are selected
+by the operation code. Values presently defined in the header file
+timex.h are as follows:
+
+#define TIME_PREC 1 /* precision (log2(sec)) */
+#define TIME_TCON 2 /* time constant (log2(sec) */
+#define TIME_FREQ 3 /* frequency tolerance */
+#define TIME_FREQ 4 /* frequency offset (scaled) */
+#define TIME_STAT 5 /* status (see return codes) */
+
+The getloop() syntax is identical.
+
+Comments welcome, but very little support is available:
+
+David L. Mills
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+mills@udel.edu
diff --git a/usr.sbin/xntpd/kernel/chuinit.c b/usr.sbin/xntpd/kernel/chuinit.c
new file mode 100644
index 0000000..80b36a9
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/chuinit.c
@@ -0,0 +1,76 @@
+/*
+** dynamically loadable chu driver
+**
+** $Header: /home/ncvs/src/usr.sbin/xntpd/kernel/chuinit.c,v 1.1.1.1 1994/09/29 23:02:42 wollman Exp $
+**
+** william robertson <rob@agate.berkeley.edu>
+*/
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/syslog.h>
+
+#include <sun/openprom.h>
+#include <sun/vddrv.h>
+
+extern int findmod(); /* os/str_io.c */
+
+extern struct streamtab chuinfo;
+
+struct vdldrv vd = {
+ VDMAGIC_USER,
+ "chu"
+ };
+
+
+int
+xxxinit(function_code, vdp, vdi, vds)
+unsigned int function_code;
+struct vddrv *vdp;
+addr_t vdi;
+struct vdstat *vds;
+{
+ register int i = 0;
+ register int j;
+
+ switch (function_code) {
+ case VDLOAD:
+
+ if (findmod("chu") >= 0) {
+ log(LOG_ERR, "chu stream module already loaded\n");
+ return (EADDRINUSE);
+ }
+
+ i = findmod("\0");
+
+ if (i == -1 || fmodsw[i].f_name[0] != '\0')
+ return(-1);
+
+ for (j = 0; vd.Drv_name[j] != '\0'; j++) /* XXX check bounds */
+ fmodsw[i].f_name[j] = vd.Drv_name[j];
+
+ fmodsw[i].f_name[j] = '\0';
+ fmodsw[i].f_str = &chuinfo;
+
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+
+ return(0);
+
+ case VDUNLOAD:
+ if ((i = findmod(vd.Drv_name)) == -1)
+ return(-1);
+
+ fmodsw[i].f_name[0] = '\0';
+ fmodsw[i].f_str = 0;
+
+ return(0);
+
+ case VDSTAT:
+ return(0);
+
+ default:
+ return(EIO);
+ }
+}
diff --git a/usr.sbin/xntpd/kernel/clkinit.c b/usr.sbin/xntpd/kernel/clkinit.c
new file mode 100644
index 0000000..b8fe456
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/clkinit.c
@@ -0,0 +1,76 @@
+/*
+** dynamically loadable clk driver
+**
+** $Header: /home/ncvs/src/usr.sbin/xntpd/kernel/clkinit.c,v 1.1.1.1 1994/09/29 23:02:47 wollman Exp $
+**
+** william robertson <rob@agate.berkeley.edu>
+*/
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/syslog.h>
+
+#include <sun/openprom.h>
+#include <sun/vddrv.h>
+
+extern int findmod(); /* os/str_io.c */
+
+extern struct streamtab clkinfo;
+
+struct vdldrv vd = {
+ VDMAGIC_USER,
+ "clk"
+ };
+
+
+int
+xxxinit(function_code, vdp, vdi, vds)
+unsigned int function_code;
+struct vddrv *vdp;
+addr_t vdi;
+struct vdstat *vds;
+{
+ register int i = 0;
+ register int j;
+
+ switch (function_code) {
+ case VDLOAD:
+
+ if (findmod("clk") >= 0) {
+ log(LOG_ERR, "clk stream module already loaded\n");
+ return (EADDRINUSE);
+ }
+
+ i = findmod("\0");
+
+ if (i == -1 || fmodsw[i].f_name[0] != '\0')
+ return(-1);
+
+ for (j = 0; vd.Drv_name[j] != '\0'; j++) /* XXX check bounds */
+ fmodsw[i].f_name[j] = vd.Drv_name[j];
+
+ fmodsw[i].f_name[j] = '\0';
+ fmodsw[i].f_str = &clkinfo;
+
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+
+ return(0);
+
+ case VDUNLOAD:
+ if ((i = findmod(vd.Drv_name)) == -1)
+ return(-1);
+
+ fmodsw[i].f_name[0] = '\0';
+ fmodsw[i].f_str = 0;
+
+ return(0);
+
+ case VDSTAT:
+ return(0);
+
+ default:
+ return(EIO);
+ }
+}
diff --git a/usr.sbin/xntpd/lib/Makefile b/usr.sbin/xntpd/lib/Makefile
new file mode 100644
index 0000000..948e66d
--- /dev/null
+++ b/usr.sbin/xntpd/lib/Makefile
@@ -0,0 +1,35 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+SRCS= atoint.c atolfp.c atouint.c auth12crypt.c authdecrypt.c authdes.c \
+ authencrypt.c authkeys.c authparity.c authreadkeys.c authusekey.c \
+ buftvtots.c caljulian.c calleapwhen.c caltontp.c calyearstart.c \
+ clocktime.c dofptoa.c dolfptoa.c emalloc.c fptoa.c fptoms.c \
+ gettstamp.c hextoint.c hextolfp.c humandate.c inttoa.c \
+ lib_strbuf.c mfptoa.c mfptoms.c modetoa.c mstolfp.c \
+ msutotsf.c netof.c numtoa.c refnumtoa.c numtohost.c octtoint.c \
+ prettydate.c ranny.c tsftomsu.c tstotv.c tvtoa.c tvtots.c \
+ uglydate.c uinttoa.c utvtoa.c clocktypes.c \
+ md5.c a_md5encrypt.c a_md5decrypt.c \
+ a_md512crypt.c decodenetnum.c systime.c msyslog.c syssignal.c \
+ findconfig.c getopt.c
+
+NOMAN=
+NOPROFILE=
+LIB= ntp
+CLEANFILES+=authdes.c
+
+.if !defined(NOCRYPT) && exists(${.CURDIR}/../../../secure/usr.sbin/xntpd/lib)
+.PATH: ${.CURDIR}/../../../secure/usr.sbin/xntpd/lib
+.else
+authdes.c: authdes.c.export
+ cp ${.CURDIR}/authdes.c.export authdes.c
+.endif
+
+# don't install this anywhere
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/xntpd/lib/README b/usr.sbin/xntpd/lib/README
new file mode 100644
index 0000000..c2b65d9
--- /dev/null
+++ b/usr.sbin/xntpd/lib/README
@@ -0,0 +1,5 @@
+README file for directory ./lib of the NTP Version 3 distribution
+
+This directory contains the sources for the NTP library used by most
+programs in this distribution. See the README and RELNOTES files in the
+parent directory for directions on how to make this library.
diff --git a/usr.sbin/xntpd/lib/a_md512crypt.c b/usr.sbin/xntpd/lib/a_md512crypt.c
new file mode 100644
index 0000000..515d83b
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md512crypt.c
@@ -0,0 +1,86 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+static MD5_CTX ctx;
+
+/*
+ * Do first stage of a two stage authenticator generation.
+ */
+
+void
+MD5auth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+
+ authencryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length - 8);
+ /* just leave the partially computed value in the static MD5_CTX */
+}
+
+/*
+ * Do second state of a two stage authenticator generation.
+ */
+int
+MD5auth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ /*
+ * Don't bother checking the keys. The first stage would have
+ * handled that. Finish up the generation by also including the
+ * last 8 bytes of the data area.
+ */
+
+ MD5Update(&ctx, (char *)(pkt) + length - 8, 8);
+ MD5Final(&ctx);
+
+ memmove((char *) &pkt[NOCRYPT_LONGS + length/sizeof(U_LONG)],
+ (char *) ctx.digest,
+ BLOCK_OCTETS);
+ return (4 + BLOCK_OCTETS);
+}
diff --git a/usr.sbin/xntpd/lib/a_md5decrypt.c b/usr.sbin/xntpd/lib/a_md5decrypt.c
new file mode 100644
index 0000000..958c21f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md5decrypt.c
@@ -0,0 +1,59 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+int
+MD5authdecrypt(keyno, pkt, length)
+ u_long keyno;
+ const U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ MD5_CTX ctx;
+
+ authdecryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno))
+ return 0;
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length);
+ MD5Final(&ctx);
+
+ return (!memcmp((char *)ctx.digest,
+ (char *)pkt + length + 4,
+ BLOCK_OCTETS));
+}
diff --git a/usr.sbin/xntpd/lib/a_md5encrypt.c b/usr.sbin/xntpd/lib/a_md5encrypt.c
new file mode 100644
index 0000000..6fae6fb
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md5encrypt.c
@@ -0,0 +1,69 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+
+int
+MD5authencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ MD5_CTX ctx;
+ int len; /* in 4 byte quantities */
+
+ authencryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+ len = length / sizeof(U_LONG);
+
+ /*
+ * Generate the authenticator.
+ */
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length);
+ MD5Final(&ctx);
+
+ memmove((char *)&pkt[NOCRYPT_LONGS + len],
+ (char *)ctx.digest,
+ BLOCK_OCTETS);
+ return (4 + BLOCK_OCTETS); /* return size of key and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/adjtimex.c b/usr.sbin/xntpd/lib/adjtimex.c
new file mode 100644
index 0000000..03e9d79
--- /dev/null
+++ b/usr.sbin/xntpd/lib/adjtimex.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+_sccsid:.asciz "11/19/91 ULTRIX @(#)adjtime.c 6.1"
+#endif not lint
+
+#include "SYS.h"
+
+SYSCALL(adjtimex)
+ ret
+
diff --git a/usr.sbin/xntpd/lib/atoint.c b/usr.sbin/xntpd/lib/atoint.c
new file mode 100644
index 0000000..57abd44
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atoint.c
@@ -0,0 +1,48 @@
+/*
+ * atoint - convert an ascii string to a signed long, with error checking
+ */
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+
+int
+atoint(str, ival)
+ const char *str;
+ long *ival;
+{
+ register long u;
+ register const char *cp;
+ register int isneg;
+ register int oflow_digit;
+
+ cp = str;
+
+ if (*cp == '-') {
+ cp++;
+ isneg = 1;
+ oflow_digit = '8';
+ } else {
+ isneg = 0;
+ oflow_digit = '7';
+ }
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp))
+ return 0;
+ if (u > 214748364 || (u == 214748364 && *cp > oflow_digit))
+ return 0; /* overflow */
+ u = (u << 3) + (u << 1);
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+
+ if (isneg)
+ *ival = -u;
+ else
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/atolfp.c b/usr.sbin/xntpd/lib/atolfp.c
new file mode 100644
index 0000000..644a38c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atolfp.c
@@ -0,0 +1,117 @@
+/*
+ * atolfp - convert an ascii string to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+
+/*
+ * Powers of 10
+ */
+static u_long ten_to_the_n[10] = {
+ 0,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+};
+
+
+int
+atolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind;
+ int ndec;
+ int isneg;
+ static char *digits = "0123456789";
+
+ isneg = 0;
+ dec_i = dec_f = 0;
+ ndec = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-|+][digits][.][digits][spaces|\n|\0]
+ */
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '-') {
+ cp++;
+ isneg = 1;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ if (*cp != '.' && !isdigit(*cp))
+ return 0;
+
+ while (*cp != '\0' && (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = (dec_i << 3) + (dec_i << 1); /* multiply by 10 */
+ dec_i += (ind - digits);
+ cp++;
+ }
+
+ if (*cp != '\0' && !isspace(*cp)) {
+ if (*cp++ != '.')
+ return 0;
+
+ while (ndec < 9 && *cp != '\0'
+ && (ind = strchr(digits, *cp)) != NULL) {
+ ndec++;
+ dec_f = (dec_f << 3) + (dec_f << 1); /* *10 */
+ dec_f += (ind - digits);
+ cp++;
+ }
+
+ while (isdigit(*cp))
+ cp++;
+
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+ }
+
+ if (ndec > 0) {
+ register u_long tmp;
+ register u_long bit;
+ register u_long ten_fact;
+
+ ten_fact = ten_to_the_n[ndec];
+
+ tmp = 0;
+ bit = 0x80000000;
+ while (bit != 0) {
+ dec_f <<= 1;
+ if (dec_f >= ten_fact) {
+ tmp |= bit;
+ dec_f -= ten_fact;
+ }
+ bit >>= 1;
+ }
+ if ((dec_f << 1) > ten_fact)
+ tmp++;
+ dec_f = tmp;
+ }
+
+ if (isneg)
+ M_NEG(dec_i, dec_f);
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/atouint.c b/usr.sbin/xntpd/lib/atouint.c
new file mode 100644
index 0000000..d826bb4
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atouint.c
@@ -0,0 +1,33 @@
+/*
+ * atouint - convert an ascii string to an unsigned long, with error checking
+ */
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+
+int
+atouint(str, uval)
+ const char *str;
+ u_long *uval;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp))
+ return 0;
+ if (u > 429496729 || (u == 429496729 && *cp >= '6'))
+ return 0; /* overflow */
+ u = (u << 3) + (u << 1);
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+
+ *uval = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/auth12crypt.c b/usr.sbin/xntpd/lib/auth12crypt.c
new file mode 100644
index 0000000..7d69122
--- /dev/null
+++ b/usr.sbin/xntpd/lib/auth12crypt.c
@@ -0,0 +1,125 @@
+/*
+ * auth12crypt.c - routines to support two stage NTP encryption
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, which
+ * is encrypted in pass 1, followed by:
+ * an 8 byte chunk of data which is encrypted in pass 2
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_ekeys[]; /* cached decryption keys */
+extern u_char DESzeroekeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters, from the database module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+
+/*
+ * auth1crypt - do the first stage of a two stage encryption
+ */
+void
+DESauth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+ register U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ U_LONG work[2];
+
+ authencryptions++;
+
+ if (keyno == 0) {
+ keys = DESzeroekeys;
+ } else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+ keys = DEScache_ekeys;
+ }
+
+ /*
+ * Do the first five encryptions. Stick the intermediate result
+ * in the mac field. The sixth encryption must wait until the
+ * caller freezes a transmit time stamp, and will be done in stage 2.
+ */
+ pd = pkt;
+ work[0] = work[1] = 0;
+
+ for (i = (length/BLOCK_OCTETS - 1); i > 0; i--) {
+ work[0] ^= *pd++;
+ work[1] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ /*
+ * Space to the end of the packet and stick the intermediate
+ * result in the mac field.
+ */
+ pd += BLOCK_LONGS + NOCRYPT_LONGS;
+ *pd++ = work[0];
+ *pd = work[1];
+}
+
+
+/*
+ * auth2crypt - do the second stage of a two stage encryption
+ */
+int
+DESauth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ register U_LONG *pd;
+ register u_char *keys;
+
+ /*
+ * Skip the key check. The call to the first stage should
+ * have got it.
+ */
+ if (keyno == 0)
+ keys = DESzeroekeys;
+ else
+ keys = DEScache_ekeys;
+
+ /*
+ * The mac currently should hold the results of the first `n'
+ * encryptions. We xor in the last block in data section and
+ * do the final encryption in place.
+ *
+ * Get a pointer to the MAC block. XOR in the last two words of
+ * the data area. Call the encryption routine.
+ */
+ pd = pkt + (length/sizeof(U_LONG)) + NOCRYPT_LONGS;
+
+ *pd ^= *(pd - NOCRYPT_LONGS - 2);
+ *(pd + 1) ^= *(pd - NOCRYPT_LONGS - 1);
+ DESauth_des(pd, keys);
+
+ return 4 + 8; /* return size of key number and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/authdecrypt.c b/usr.sbin/xntpd/lib/authdecrypt.c
new file mode 100644
index 0000000..6ad3aeb
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authdecrypt.c
@@ -0,0 +1,82 @@
+/*
+ * authdecrypt - routine to decrypt a packet to see if this guy knows our key.
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of unencrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_dkeys[]; /* cached decryption keys */
+extern u_char DESzerodkeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+
+int
+DESauthdecrypt(keyno, pkt, length)
+ u_long keyno;
+ const U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ register const U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ register int longlen;
+ U_LONG work[2];
+
+ authdecryptions++;
+
+ if (keyno == 0)
+ keys = DESzerodkeys;
+ else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno))
+ return 0;
+ }
+ keys = DEScache_dkeys;
+ }
+
+ /*
+ * Get encryption block data in host byte order and decrypt it.
+ */
+ longlen = length / sizeof(U_LONG);
+ pd = pkt + longlen; /* points at NOCRYPT area */
+ work[0] = *(pd + NOCRYPT_LONGS);
+ work[1] = *(pd + NOCRYPT_LONGS + 1);
+
+ if (longlen & 0x1) {
+ DESauth_des(work, keys);
+ work[0] ^= *(--pd);
+ }
+
+ for (i = longlen/2; i > 0; i--) {
+ DESauth_des(work, keys);
+ work[1] ^= *(--pd);
+ work[0] ^= *(--pd);
+ }
+
+ /*
+ * Success if the encryption data is zero
+ */
+ if ((work[0] == 0) && (work[1] == 0))
+ return 1;
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/authdes.c.export b/usr.sbin/xntpd/lib/authdes.c.export
new file mode 100644
index 0000000..a22fc83
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authdes.c.export
@@ -0,0 +1,41 @@
+/*
+ * authdes.c - dummy encryption routines for destinations outside the USA.
+ *
+ * Sorry, folks; I hate this, too. Send me your e-mail address in an
+ * envelope bearing a US postmark and I'll send you the decryption key
+ * for the des program normally distributed with Unix in the USA. Outside
+ * the USA you are on your own; however, you should be able quickly to
+ * obtain the source from lots of places, homegrown or otherwise.
+ *
+ * to decrypt the des routine, mumble the following:
+ *
+ * des -d -k key authdes.c.des authdes.c
+ *
+ * , where key is as above, and rebuild. To restore the distribution
+ * to its exportable state, copy this file to authdes.c .
+ */
+#include <sys/types.h>
+#include "ntp_stdlib.h"
+
+/*
+ * This routine is normally called to compute the key schedule.
+ */
+void
+DESauth_subkeys(key, encryptkeys, decryptkeys)
+ const U_LONG *key;
+ u_char *encryptkeys;
+ u_char *decryptkeys;
+{
+};
+
+/*
+ * This routine is normally called to encrypt and decrypt the data. This
+ * is done in-place using the Digital Encryption Standard (DES) Cipher-
+ * Block Chaining (CBC) method as described in the NTP specification.
+ */
+void
+DESauth_des(data, subkeys)
+ U_LONG *data;
+ u_char *subkeys;
+{
+};
diff --git a/usr.sbin/xntpd/lib/authencrypt.c b/usr.sbin/xntpd/lib/authencrypt.c
new file mode 100644
index 0000000..f84d10f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authencrypt.c
@@ -0,0 +1,88 @@
+/*
+ * authencrypt - compute and encrypt the mac field in an NTP packet
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_ekeys[]; /* cached decryption keys */
+extern u_char DESzeroekeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters from the database module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+int
+DESauthencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ register U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ register int len;
+ U_LONG work[2];
+
+ authencryptions++;
+
+ if (keyno == 0) {
+ keys = DESzeroekeys;
+ } else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+ keys = DEScache_ekeys;
+ }
+
+ /*
+ * Do the encryption. Work our way forward in the packet, eight
+ * bytes at a time, encrypting as we go. Note that the byte order
+ * issues are handled by the DES routine itself
+ */
+ pd = pkt;
+ work[0] = work[1] = 0;
+ len = length / sizeof(U_LONG);
+
+ for (i = (len/2); i > 0; i--) {
+ work[0] ^= *pd++;
+ work[1] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ if (len & 0x1) {
+ work[0] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ /*
+ * Space past the keyid and stick the result back in the mac field
+ */
+ pd += NOCRYPT_LONGS;
+ *pd++ = work[0];
+ *pd = work[1];
+
+ return 4 + BLOCK_OCTETS; /* return size of key and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/authkeys.c b/usr.sbin/xntpd/lib/authkeys.c
new file mode 100644
index 0000000..9fce020
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authkeys.c
@@ -0,0 +1,601 @@
+/*
+ * authkeys.c - routines to manage the storage of authentication keys
+ */
+#include <stdio.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Structure to store keys in in the hash table.
+ */
+struct savekey {
+ struct savekey *next;
+ union {
+#ifdef DES
+ U_LONG DES_key[2];
+#endif
+#ifdef MD5
+ char MD5_key[32];
+#endif
+ } k;
+ u_long keyid;
+ u_short flags;
+#ifdef MD5
+ int keylen;
+#endif
+};
+
+#define KEY_TRUSTED 0x1 /* this key is trusted */
+#define KEY_KNOWN 0x2 /* this key is known */
+
+#ifdef DES
+#define KEY_DES 0x100 /* this is a DES type key */
+#endif
+
+#ifdef MD5
+#define KEY_MD5 0x200 /* this is a MD5 type key */
+#endif
+
+/*
+ * The hash table. This is indexed by the low order bits of the
+ * keyid. We make this fairly big for potentially busy servers.
+ */
+#define HASHSIZE 64
+#define HASHMASK ((HASHSIZE)-1)
+#define KEYHASH(keyid) ((keyid) & HASHMASK)
+
+struct savekey *key_hash[HASHSIZE];
+
+U_LONG authkeynotfound;
+U_LONG authkeylookups;
+U_LONG authnumkeys;
+U_LONG authuncached;
+U_LONG authkeyuncached;
+U_LONG authnokey; /* calls to encrypt with no key */
+U_LONG authencryptions;
+U_LONG authdecryptions;
+
+/*
+ * Storage for free key structures. We malloc() such things but
+ * never free them.
+ */
+struct savekey *authfreekeys;
+int authnumfreekeys;
+
+#define MEMINC 12 /* number of new free ones to get at once */
+
+
+#ifdef DES
+/*
+ * Size of the key schedule
+ */
+#define KEY_SCHED_SIZE 128 /* number of octets to store key schedule */
+
+/*
+ * The zero key, which we always have. Store the permutted key
+ * zero in here.
+ */
+#define ZEROKEY_L 0x01010101 /* odd parity zero key */
+#define ZEROKEY_R 0x01010101 /* right half of same */
+u_char DESzeroekeys[KEY_SCHED_SIZE];
+u_char DESzerodkeys[KEY_SCHED_SIZE];
+u_char DEScache_ekeys[KEY_SCHED_SIZE];
+u_char DEScache_dkeys[KEY_SCHED_SIZE];
+#endif
+
+/*
+ * The key cache. We cache the last key we looked at here.
+ */
+u_long cache_keyid;
+u_short cache_flags;
+
+#ifdef MD5
+int cache_keylen;
+char *cache_key;
+#endif
+
+/*
+ * init_auth - initialize internal data
+ */
+void
+init_auth()
+{
+ U_LONG zerokey[2];
+
+ /*
+ * Initialize hash table and free list
+ */
+ memset((char *)key_hash, 0, sizeof key_hash);
+ cache_flags = cache_keyid = 0;
+
+ authnumfreekeys = authkeynotfound = authkeylookups = 0;
+ authnumkeys = authuncached = authkeyuncached = authnokey = 0;
+ authencryptions = authdecryptions = 0;
+
+#ifdef DES
+ /*
+ * Initialize the zero key
+ */
+ zerokey[0] = ZEROKEY_L;
+ zerokey[1] = ZEROKEY_R;
+ /* could just zero all */
+ DESauth_subkeys(zerokey, DESzeroekeys, DESzerodkeys);
+#endif
+}
+
+
+/*
+ * auth_findkey - find a key in the hash table
+ */
+struct savekey *
+auth_findkey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ return sk;
+ sk = sk->next;
+ }
+ return 0;
+}
+
+
+/*
+ * auth_havekey - return whether a key is known
+ */
+int
+auth_havekey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ if (keyno == 0 || (keyno == cache_keyid))
+ return 1;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ if (sk->flags & KEY_KNOWN)
+ return 1;
+ else {
+ authkeynotfound++;
+ return 0;
+ }
+ }
+ sk = sk->next;
+ }
+ authkeynotfound++;
+ return 0;
+}
+
+
+/*
+ * authhavekey - return whether a key is known. Permute and cache
+ * the key as a side effect.
+ */
+int
+authhavekey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ authkeylookups++;
+ if (keyno == 0 || keyno == cache_keyid)
+ return 1;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 || !(sk->flags & KEY_KNOWN)) {
+ authkeynotfound++;
+ return 0;
+ }
+
+ cache_keyid = sk->keyid;
+ cache_flags = sk->flags;
+#ifdef MD5
+ if (sk->flags & KEY_MD5) {
+ cache_keylen = sk->keylen;
+ cache_key = (char *) sk->k.MD5_key; /* XXX */
+ return 1;
+ }
+#endif
+
+#ifdef DES
+ if (sk->flags & KEY_DES) {
+ DESauth_subkeys(sk->k.DES_key, DEScache_ekeys, DEScache_dkeys);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ * auth_moremem - get some more free key structures
+ */
+int
+auth_moremem()
+{
+ register struct savekey *sk;
+ register int i;
+
+ sk = (struct savekey *)malloc(MEMINC * sizeof(struct savekey));
+ if (sk == 0)
+ return 0;
+
+ for (i = MEMINC; i > 0; i--) {
+ sk->next = authfreekeys;
+ authfreekeys = sk++;
+ }
+ authnumfreekeys += MEMINC;
+ return authnumfreekeys;
+}
+
+
+/*
+ * authtrust - declare a key to be trusted/untrusted
+ */
+void
+authtrust(keyno, trust)
+ u_long keyno;
+ int trust;
+{
+ register struct savekey *sk;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 && !trust)
+ return;
+
+ if (sk != 0) {
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+
+ if (trust) {
+ sk->flags |= KEY_TRUSTED;
+ return;
+ }
+
+ sk->flags &= ~KEY_TRUSTED;
+ if (!(sk->flags & KEY_KNOWN)) {
+ register struct savekey *skp;
+
+ skp = key_hash[KEYHASH(keyno)];
+ if (skp == sk) {
+ key_hash[KEYHASH(keyno)] = sk->next;
+ } else {
+ while (skp->next != sk)
+ skp = skp->next;
+ skp->next = sk->next;
+ }
+ authnumkeys--;
+
+ sk->next = authfreekeys;
+ authfreekeys = sk;
+ authnumfreekeys++;
+ }
+ return;
+ }
+
+ if (authnumfreekeys == 0)
+ if (auth_moremem() == 0)
+ return;
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ sk->keyid = keyno;
+ sk->flags = KEY_TRUSTED;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+
+
+/*
+ * authistrusted - determine whether a key is trusted
+ */
+int
+authistrusted(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ if (keyno == cache_keyid)
+ return ((cache_flags & KEY_TRUSTED) != 0);
+
+ authkeyuncached++;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 || !(sk->flags & KEY_TRUSTED))
+ return 0;
+ return 1;
+}
+
+
+
+#ifdef DES
+/*
+ * DESauth_setkey - set a key into the key array
+ */
+void
+DESauth_setkey(keyno, key)
+ u_long keyno;
+ const U_LONG *key;
+{
+ register struct savekey *sk;
+
+ /*
+ * See if we already have the key. If so just stick in the
+ * new value.
+ */
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ sk->k.DES_key[0] = key[0];
+ sk->k.DES_key[1] = key[1];
+ sk->flags |= KEY_KNOWN | KEY_DES;
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+ return;
+ }
+ sk = sk->next;
+ }
+
+ /*
+ * Need to allocate new structure. Do it.
+ */
+ if (authnumfreekeys == 0) {
+ if (auth_moremem() == 0)
+ return;
+ }
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ sk->k.DES_key[0] = key[0];
+ sk->k.DES_key[1] = key[1];
+ sk->keyid = keyno;
+ sk->flags = KEY_KNOWN | KEY_DES;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+#endif
+
+#ifdef MD5
+void
+MD5auth_setkey(keyno, key)
+ u_long keyno;
+ const U_LONG *key;
+{
+ register struct savekey *sk;
+
+ /*
+ * See if we already have the key. If so just stick in the
+ * new value.
+ */
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
+ if ((sk->keylen = strlen((char *)key)) >
+ sizeof(sk->k.MD5_key))
+ sk->keylen = sizeof(sk->k.MD5_key);
+
+ sk->flags |= KEY_KNOWN | KEY_MD5;
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+ return;
+ }
+ sk = sk->next;
+ }
+
+ /*
+ * Need to allocate new structure. Do it.
+ */
+ if (authnumfreekeys == 0) {
+ if (auth_moremem() == 0)
+ return;
+ }
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
+ if ((sk->keylen = strlen((char *)key)) > sizeof(sk->k.MD5_key))
+ sk->keylen = sizeof(sk->k.MD5_key);
+
+ sk->keyid = keyno;
+ sk->flags = KEY_KNOWN | KEY_MD5;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+#endif
+
+/*
+ * auth_delkeys - delete all known keys, in preparation for rereading
+ * the keys file (presumably)
+ */
+void
+auth_delkeys()
+{
+ register struct savekey *sk;
+ register struct savekey **skp;
+ register int i;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ skp = &(key_hash[i]);
+ sk = key_hash[i];
+ while (sk != 0) {
+ sk->flags &= ~(KEY_KNOWN
+#ifdef MD5
+ | KEY_MD5
+#endif
+#ifdef DES
+ | KEY_DES
+#endif
+ );
+ if (sk->flags == 0) {
+ *skp = sk->next;
+ authnumkeys--;
+ sk->next = authfreekeys;
+ authfreekeys = sk;
+ authnumfreekeys++;
+ sk = *skp;
+ } else {
+ skp = &(sk->next);
+ sk = sk->next;
+ }
+ }
+ }
+}
+
+
+/*
+ * auth1crypt - support for two stage encryption, part 1.
+ */
+void
+auth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES)) {
+ DESauth1crypt(keyno, pkt, length);
+ return;
+ }
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5) {
+ MD5auth1crypt(keyno, pkt, length);
+ return;
+ }
+#endif
+}
+
+
+/*
+ * auth1crypt - support for two stage encryption, part 1.
+ */
+int
+auth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return DESauth2crypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5auth2crypt(keyno, pkt, length);
+#endif
+
+ return 0;
+}
+
+int
+authencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ int sendlength = 0;
+
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return sendlength = DESauthencrypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5authencrypt(keyno, pkt, length);
+#endif
+ return 0;
+}
+
+
+int
+authdecrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ if (keyno && (keyno != cache_keyid)) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return DESauthdecrypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5authdecrypt(keyno, pkt, length);
+#endif
+
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/authparity.c b/usr.sbin/xntpd/lib/authparity.c
new file mode 100644
index 0000000..a5b59e3
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authparity.c
@@ -0,0 +1,58 @@
+/*
+ * auth_parity - set parity on a key/check for odd parity
+ */
+#include "ntp_stdlib.h"
+
+int
+DESauth_parity(key)
+ U_LONG *key;
+{
+ U_LONG mask;
+ int parity_err;
+ int bitcount;
+ int half;
+ int byte;
+ int i;
+
+ /*
+ * Go through counting bits in each byte. Check to see if
+ * each parity bit was set correctly. If not, note the error
+ * and set it right.
+ */
+ parity_err = 0;
+ for (half = 0; half < 2; half++) { /* two halves of key */
+ mask = 0x80000000;
+ for (byte = 0; byte < 4; byte++) { /* 4 bytes per half */
+ bitcount = 0;
+ for (i = 0; i < 7; i++) { /* 7 data bits / byte */
+ if (key[half] & mask)
+ bitcount++;
+ mask >>= 1;
+ }
+
+ /*
+ * If bitcount is even, parity must be set. If
+ * bitcount is odd, parity must be clear.
+ */
+ if ((bitcount & 0x1) == 0) {
+ if (!(key[half] & mask)) {
+ parity_err++;
+ key[half] |= mask;
+ }
+ } else {
+ if (key[half] & mask) {
+ parity_err++;
+ key[half] &= ~mask;
+ }
+ }
+ mask >>= 1;
+ }
+ }
+
+ /*
+ * Return the result of the parity check.
+ */
+ return (parity_err == 0);
+}
+
+
diff --git a/usr.sbin/xntpd/lib/authreadkeys.c b/usr.sbin/xntpd/lib/authreadkeys.c
new file mode 100644
index 0000000..ee13441
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authreadkeys.c
@@ -0,0 +1,191 @@
+/*
+ * authreadkeys.c - routines to support the reading of the key file
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#ifdef DES
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+#endif
+
+#ifdef MD5
+/*
+ * Arbitrary LONG string of ASCII characters.
+ */
+#define KEY_TYPE_MD5 4
+#endif
+
+/*
+ * nexttok - basic internal tokenizing routine
+ */
+static char *
+nexttok(str)
+ char **str;
+{
+ register char *cp;
+ char *starttok;
+
+ cp = *str;
+
+ /*
+ * Space past white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * Save this and space to end of token
+ */
+ starttok = cp;
+ while (*cp != '\0' && *cp != '\n' && *cp != ' '
+ && *cp != '\t' && *cp != '#')
+ cp++;
+
+ /*
+ * If token length is zero return an error, else set end of
+ * token to zero and return start.
+ */
+ if (starttok == cp)
+ return 0;
+
+ if (*cp == ' ' || *cp == '\t')
+ *cp++ = '\0';
+ else
+ *cp = '\0';
+
+ *str = cp;
+ return starttok;
+}
+
+
+/*
+ * authreadkeys - (re)read keys from a file.
+ */
+int
+authreadkeys(file)
+ const char *file;
+{
+ FILE *fp;
+ char *line;
+ char *token;
+ u_long keyno;
+ int keytype;
+ char buf[512]; /* lots of room for line? */
+extern FILE * fopen P((const char *filename, const char *type));
+extern int fclose P((FILE *stream));
+
+ /*
+ * Open file. Complain and return if it can't be opened.
+ */
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "can't open key file %s: %m", file);
+ return 0;
+ }
+
+ /*
+ * Remove all existing keys
+ */
+ auth_delkeys();
+
+ /*
+ * Now read lines from the file, looking for key entries
+ */
+ while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
+ token = nexttok(&line);
+ if (token == 0)
+ continue;
+
+ /*
+ * First is key number. See if it is okay.
+ */
+ keyno = atoi(token);
+ if (keyno == 0) {
+ syslog(LOG_ERR,
+ "cannot change keyid 0, key entry `%s' ignored",
+ token);
+ continue;
+ }
+
+ /*
+ * Next is keytype. See if that is all right.
+ */
+ token = nexttok(&line);
+ if (token == 0) {
+ syslog(LOG_ERR,
+ "no key type for key number %ld, entry ignored",
+ keyno);
+ continue;
+ }
+ switch (*token) {
+#ifdef DES
+ case 'S':
+ case 's':
+ keytype = KEY_TYPE_STD; break;
+
+ case 'N':
+ case 'n':
+ keytype = KEY_TYPE_NTP; break;
+
+ case 'A':
+ case 'a':
+ keytype = KEY_TYPE_ASCII; break;
+#endif
+#ifdef MD5
+ case 'M':
+ case 'm':
+ keytype = KEY_TYPE_MD5; break;
+#endif
+ default:
+ syslog(LOG_ERR,
+ "invalid key type for key number %ld, entry ignored",
+ keyno);
+ continue;
+ }
+
+ /*
+ * Finally, get key and insert it
+ */
+ token = nexttok(&line);
+ if (token == 0) {
+ syslog(LOG_ERR,
+ "no key for number %ld entry, entry ignored",
+ keyno);
+ } else {
+ switch(keytype) {
+#ifdef DES
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ case KEY_TYPE_ASCII:
+ if (!authusekey(keyno, keytype, token))
+ syslog(LOG_ERR,
+ "format/parity error for DES key %ld, not used",
+ keyno);
+ break;
+#endif
+#ifdef MD5
+ case KEY_TYPE_MD5:
+ if (!authusekey(keyno, keytype, token))
+ syslog(LOG_ERR,
+ "format/parity error for MD5 key %ld, not used",
+ keyno);
+ break;
+#endif
+ }
+ }
+ }
+ (void) fclose(fp);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/authusekey.c b/usr.sbin/xntpd/lib/authusekey.c
new file mode 100644
index 0000000..05fc0d7
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authusekey.c
@@ -0,0 +1,132 @@
+/*
+ * authusekey - decode a key from ascii and use it
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#ifdef DES
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+
+#define STD_PARITY_BITS 0x01010101
+
+#endif
+
+#ifdef MD5
+#define KEY_TYPE_MD5 4
+#endif
+
+int
+authusekey(keyno, keytype, str)
+ u_long keyno;
+ int keytype;
+ const char *str;
+{
+ U_LONG key[2];
+ u_char keybytes[8];
+ const char *cp;
+ char *xdigit;
+ int len;
+ int i;
+ static char *hex = "0123456789abcdef";
+
+ cp = str;
+ len = strlen(cp);
+ if (len == 0)
+ return 0;
+
+ switch(keytype) {
+#ifdef DES
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ if (len != 16) /* Lazy. Should define constant */
+ return 0;
+ /*
+ * Decode hex key.
+ */
+ key[0] = 0;
+ key[1] = 0;
+ for (i = 0; i < 16; i++) {
+ if (!isascii(*cp))
+ return 0;
+ xdigit = strchr(hex, isupper(*cp) ? tolower(*cp) : *cp);
+ cp++;
+ if (xdigit == 0)
+ return 0;
+ key[i>>3] <<= 4;
+ key[i>>3] |= (U_LONG)(xdigit - hex) & 0xf;
+ }
+
+ /*
+ * If this is an NTP format key, put it into NBS format
+ */
+ if (keytype == KEY_TYPE_NTP) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] << 1) & ~STD_PARITY_BITS)
+ | ((key[i] >> 7) & STD_PARITY_BITS);
+ }
+
+ /*
+ * Check the parity, reject the key if the check fails
+ */
+ if (!DESauth_parity(key)) {
+ return 0;
+ }
+
+ /*
+ * We can't find a good reason not to use this key.
+ * So use it.
+ */
+ DESauth_setkey(keyno, key);
+ break;
+
+ case KEY_TYPE_ASCII:
+ /*
+ * Make up key from ascii representation
+ */
+ memset((char *) keybytes, 0, sizeof(keybytes));
+ for (i = 0; i < 8 && i < len; i++)
+ keybytes[i] = *cp++ << 1;
+ key[0] = (U_LONG)keybytes[0] << 24 | (U_LONG)keybytes[1] << 16
+ | (U_LONG)keybytes[2] << 8 | (U_LONG)keybytes[3];
+ key[1] = (U_LONG)keybytes[4] << 24 | (U_LONG)keybytes[5] << 16
+ | (U_LONG)keybytes[6] << 8 | (U_LONG)keybytes[7];
+
+ /*
+ * Set parity on key
+ */
+ (void)DESauth_parity(key);
+
+ /*
+ * Now set key in.
+ */
+ DESauth_setkey(keyno, key);
+ break;
+#endif
+
+#ifdef MD5
+ case KEY_TYPE_MD5:
+ /* XXX FIXME: MD5auth_setkey() casts arg2 back to (char *) */
+ MD5auth_setkey(keyno, (U_LONG *)str);
+ break;
+#endif
+
+ default:
+ /* Oh, well */
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/buftvtots.c b/usr.sbin/xntpd/lib/buftvtots.c
new file mode 100644
index 0000000..7e1ec63
--- /dev/null
+++ b/usr.sbin/xntpd/lib/buftvtots.c
@@ -0,0 +1,61 @@
+/*
+ * buftvtots - pull a Unix-format (struct timeval) time stamp out of
+ * an octet stream and convert it to a l_fp time stamp.
+ * This is useful when using the clock line discipline.
+ */
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+
+int
+buftvtots(bufp, ts)
+ const char *bufp;
+ l_fp *ts;
+{
+ register const u_char *bp;
+ register u_long sec;
+ register u_long usec;
+
+#ifdef XNTP_BIG_ENDIAN
+ bp = (u_char *)bufp;
+
+ sec = (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+
+ usec = (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp & 0xff;
+#else
+ bp = (u_char *)bufp + 7;
+
+ usec = (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+
+ sec = (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp & 0xff;
+#endif
+ if (usec > 999999)
+ return 0;
+
+ ts->l_ui = sec + (u_long)JAN_1970;
+ TVUTOTSF(usec, ts->l_uf);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/caljulian.c b/usr.sbin/xntpd/lib/caljulian.c
new file mode 100644
index 0000000..64efd1e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/caljulian.c
@@ -0,0 +1,105 @@
+/*
+ * caljulian - determine the Julian date from an NTP time.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calmonthtab - month start offsets from the beginning of a cycle.
+ */
+static u_short calmonthtab[12] = {
+ 0, /* March */
+ MAR, /* April */
+ (MAR+APR), /* May */
+ (MAR+APR+MAY), /* June */
+ (MAR+APR+MAY+JUN), /* July */
+ (MAR+APR+MAY+JUN+JUL), /* August */
+ (MAR+APR+MAY+JUN+JUL+AUG), /* September */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP), /* October */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT), /* November */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV), /* December */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC), /* January */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN), /* February */
+};
+
+/*
+ * caldaytab - calendar year start day offsets
+ */
+static u_short caldaytab[YEARSPERCYCLE] = {
+ (DAYSPERYEAR - (JAN + FEB)),
+ ((DAYSPERYEAR * 2) - (JAN + FEB)),
+ ((DAYSPERYEAR * 3) - (JAN + FEB)),
+ ((DAYSPERYEAR * 4) - (JAN + FEB)),
+};
+
+void
+caljulian(ntptime, jt)
+ u_long ntptime;
+ register struct calendar *jt;
+{
+ register int i;
+ register u_long nt;
+ register u_short snt;
+ register int cyear;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ nt = ntptime;
+ if (nt >= MAR1988) {
+ cyear = CYCLE22;
+ nt -= MAR1988;
+ } else {
+ cyear = 0;
+ nt -= MAR1900;
+ }
+ while (nt >= SECSPERCYCLE) {
+ nt -= SECSPERCYCLE;
+ cyear++;
+ }
+
+ /*
+ * Seconds, minutes and hours are too hard to do without
+ * divides, so we don't.
+ */
+ jt->second = nt % SECSPERMIN;
+ nt /= SECSPERMIN; /* nt in minutes */
+ jt->minute = nt % MINSPERHR;
+ snt = nt / MINSPERHR; /* snt in hours */
+ jt->hour = snt % HRSPERDAY;
+ snt /= HRSPERDAY; /* nt in days */
+
+ /*
+ * snt is now the number of days into the cycle, from 0 to 1460.
+ */
+ cyear <<= 2;
+ if (snt < caldaytab[0]) {
+ jt->yearday = snt + JAN + FEBLEAP + 1; /* first year is leap */
+ } else {
+ for (i = 1; i < YEARSPERCYCLE; i++)
+ if (snt < caldaytab[i])
+ break;
+ jt->yearday = snt - caldaytab[i-1] + 1;
+ cyear += i;
+ }
+ jt->year = cyear + 1900;
+
+ /*
+ * One last task, to compute the month and day. Normalize snt to
+ * a day within a cycle year.
+ */
+ while (snt >= DAYSPERYEAR)
+ snt -= DAYSPERYEAR;
+ for (i = 0; i < 11; i++)
+ if (snt < calmonthtab[i+1])
+ break;
+
+ if (i > 9)
+ jt->month = i - 9; /* January or February */
+ else
+ jt->month = i + 3; /* March through December */
+ jt->monthday = snt - calmonthtab[i] + 1;
+}
diff --git a/usr.sbin/xntpd/lib/calleapwhen.c b/usr.sbin/xntpd/lib/calleapwhen.c
new file mode 100644
index 0000000..d68bbe9
--- /dev/null
+++ b/usr.sbin/xntpd/lib/calleapwhen.c
@@ -0,0 +1,61 @@
+/*
+ * calleapwhen - determine the number of seconds to the next possible
+ * leap occurance and the last one.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calleaptab - leaps occur at the end of December and June
+ */
+long calleaptab[10] = {
+ -(JAN+FEBLEAP)*SECSPERDAY, /* leap previous to cycle */
+ (MAR+APR+MAY+JUN)*SECSPERDAY, /* end of June */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY, /* end of Dec */
+ (MAR+APR+MAY+JUN)*SECSPERDAY + SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + SECSPERYEAR,
+ (MAR+APR+MAY+JUN)*SECSPERDAY + 2*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + 2*SECSPERYEAR,
+ (MAR+APR+MAY+JUN)*SECSPERDAY + 3*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + 3*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN+FEBLEAP+MAR+APR+MAY+JUN)
+ *SECSPERDAY + 3*SECSPERYEAR, /* next after current cycle */
+};
+
+void
+calleapwhen(ntpdate, leaplast, leapnext)
+ u_long ntpdate;
+ u_long *leaplast;
+ u_long *leapnext;
+{
+ register u_long dateincycle;
+ register int i;
+
+ /*
+ * Find the offset from the start of the cycle
+ */
+ dateincycle = ntpdate;
+ if (dateincycle >= MAR1988)
+ dateincycle -= MAR1988;
+ else
+ dateincycle -= MAR1900;
+
+ while (dateincycle >= SECSPERCYCLE)
+ dateincycle -= SECSPERCYCLE;
+
+ /*
+ * Find where we are with respect to the leap events.
+ */
+ for (i = 1; i < 9; i++)
+ if (dateincycle < (u_long)calleaptab[i])
+ break;
+
+ /*
+ * i points at the next leap. Compute the last and the next.
+ */
+ *leaplast = (u_long)((long)dateincycle - calleaptab[i-1]);
+ *leapnext = (u_long)(calleaptab[i] - (long)dateincycle);
+}
diff --git a/usr.sbin/xntpd/lib/caltontp.c b/usr.sbin/xntpd/lib/caltontp.c
new file mode 100644
index 0000000..a0c8f61
--- /dev/null
+++ b/usr.sbin/xntpd/lib/caltontp.c
@@ -0,0 +1,90 @@
+/*
+ * caltontp - convert a julian date to an NTP time
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calmonthtab - month start offsets from the beginning of a cycle.
+ */
+static u_short calmonthtab[12] = {
+ 0, /* March */
+ MAR, /* April */
+ (MAR+APR), /* May */
+ (MAR+APR+MAY), /* June */
+ (MAR+APR+MAY+JUN), /* July */
+ (MAR+APR+MAY+JUN+JUL), /* August */
+ (MAR+APR+MAY+JUN+JUL+AUG), /* September */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP), /* October */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT), /* November */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV), /* December */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC), /* January */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN), /* February */
+};
+
+u_long
+caltontp(jt)
+ register const struct calendar *jt;
+{
+ register int cyear;
+ register int resyear;
+ register u_long nt;
+ register int yearday;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ cyear = (int)(jt->year - 1900) >> 2;
+ resyear = (jt->year - 1900) - (cyear << 2);
+ yearday = 0;
+ if (resyear == 0) {
+ if (jt->yearday == 0) {
+ if (jt->month == 1 || jt->month == 2) {
+ cyear--;
+ resyear = 3;
+ }
+ } else {
+ if (jt->yearday <= (u_short)(JAN+FEBLEAP)) {
+ cyear--;
+ resyear = 3;
+ yearday = calmonthtab[10] + jt->yearday;
+ } else {
+ yearday = jt->yearday - (JAN+FEBLEAP);
+ }
+ }
+ } else {
+ if (jt->yearday == 0) {
+ if (jt->month == 1 || jt->month == 2)
+ resyear--;
+ } else {
+ if (jt->yearday <= (u_short)(JAN+FEB)) {
+ resyear--;
+ yearday = calmonthtab[10] + jt->yearday;
+ } else {
+ yearday = jt->yearday - (JAN+FEB);
+ }
+ }
+ }
+
+ if (yearday == 0) {
+ if (jt->month >= 3) {
+ yearday = calmonthtab[jt->month - 3] + jt->monthday;
+ } else {
+ yearday = calmonthtab[jt->month + 9] + jt->monthday;
+ }
+ }
+
+ nt = TIMESDPERC((u_long)cyear);
+ while (resyear-- > 0)
+ nt += DAYSPERYEAR;
+ nt += (u_long) (yearday - 1);
+
+ nt = TIMES24(nt) + (u_long)jt->hour;
+ nt = TIMES60(nt) + (u_long)jt->minute;
+ nt = TIMES60(nt) + (u_long)jt->second;
+
+ return nt + MAR1900;
+}
diff --git a/usr.sbin/xntpd/lib/calyearstart.c b/usr.sbin/xntpd/lib/calyearstart.c
new file mode 100644
index 0000000..e78c058
--- /dev/null
+++ b/usr.sbin/xntpd/lib/calyearstart.c
@@ -0,0 +1,62 @@
+/*
+ * calyearstart - determine the NTP time at midnight of January 1 in
+ * the year of the given date.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calyeartab - year start offsets from the beginning of a cycle
+ */
+u_long calyeartab[YEARSPERCYCLE] = {
+ (SECSPERLEAPYEAR-JANFEBLEAP),
+ (SECSPERLEAPYEAR-JANFEBLEAP) + SECSPERYEAR,
+ (SECSPERLEAPYEAR-JANFEBLEAP) + 2*SECSPERYEAR,
+ (SECSPERLEAPYEAR-JANFEBLEAP) + 3*SECSPERYEAR
+};
+
+u_long
+calyearstart(dateinyear)
+ register u_long dateinyear;
+{
+ register u_long cyclestart;
+ register u_long nextyear, lastyear;
+ register int i;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ if (dateinyear >= MAR1988)
+ cyclestart = MAR1988;
+ else
+ cyclestart = MAR1900;
+ while ((cyclestart + SECSPERCYCLE) <= dateinyear)
+ cyclestart += SECSPERCYCLE;
+
+ /*
+ * If we're in the first year of the cycle, January 1 is
+ * two months back from the cyclestart and the year is
+ * a leap year.
+ */
+ lastyear = cyclestart + calyeartab[0];
+ if (dateinyear < lastyear)
+ return (cyclestart - JANFEBLEAP);
+
+ /*
+ * Look for an intermediate year
+ */
+ for (i = 1; i < YEARSPERCYCLE; i++) {
+ nextyear = cyclestart + calyeartab[i];
+ if (dateinyear < nextyear)
+ return lastyear;
+ lastyear = nextyear;
+ }
+
+ /*
+ * Not found, must be in last two months of cycle
+ */
+ return nextyear;
+}
diff --git a/usr.sbin/xntpd/lib/clocktime.c b/usr.sbin/xntpd/lib/clocktime.c
new file mode 100644
index 0000000..0217f2b
--- /dev/null
+++ b/usr.sbin/xntpd/lib/clocktime.c
@@ -0,0 +1,131 @@
+/*
+ * clocktime - compute the NTP date from a day of year, hour, minute
+ * and second.
+ */
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Two days, in seconds.
+ */
+#define TWODAYS (2*24*60*60)
+
+/*
+ * We demand that the time be within CLOSETIME seconds of the receive
+ * time stamp. This is about 4 hours, which hopefully should be
+ * wide enough to collect most data, while close enough to keep things
+ * from getting confused.
+ */
+#define CLOSETIME (4*60*60)
+
+
+int
+clocktime(yday, hour, minute, second, tzoff, rec_ui, yearstart, ts_ui)
+ int yday;
+ int hour;
+ int minute;
+ int second;
+ int tzoff;
+ u_long rec_ui;
+ u_long *yearstart;
+ U_LONG *ts_ui;
+{
+ register long tmp;
+ register u_long date;
+ register u_long yst;
+
+ /*
+ * Compute the offset into the year in seconds. Note that
+ * this could come out to be a negative number.
+ */
+ tmp = (long)(MULBY24((yday-1)) + hour + tzoff);
+ tmp = MULBY60(tmp) + (long)minute;
+ tmp = MULBY60(tmp) + (long)second;
+
+ /*
+ * Initialize yearstart, if necessary.
+ */
+ yst = *yearstart;
+ if (yst == 0) {
+ yst = calyearstart(rec_ui);
+ *yearstart = yst;
+ }
+
+ /*
+ * Now the fun begins. We demand that the received clock time
+ * be within CLOSETIME of the receive timestamp, but
+ * there is uncertainty about the year the timestamp is in.
+ * Use the current year start for the first check, this should
+ * work most of the time.
+ */
+ date = (u_long)(tmp + (long)yst);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *ts_ui = date;
+ return 1;
+ }
+
+ /*
+ * Trouble. Next check is to see if the year rolled over and, if
+ * so, try again with the new year's start.
+ */
+ yst = calyearstart(rec_ui);
+ if (yst != *yearstart) {
+ date = (u_long)((long)yst + tmp);
+ *ts_ui = date;
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ return 1;
+ }
+ }
+
+ /*
+ * Here we know the year start matches the current system
+ * time. One remaining possibility is that the time code
+ * is in the year previous to that of the system time. This
+ * is only worth checking if the receive timestamp is less
+ * than a couple of days into the new year.
+ */
+ if ((rec_ui - yst) < TWODAYS) {
+ yst = calyearstart(yst - TWODAYS);
+ if (yst != *yearstart) {
+ date = (u_long)(tmp + (long)yst);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ *ts_ui = date;
+ return 1;
+ }
+ }
+ }
+
+ /*
+ * One last possibility is that the time stamp is in the year
+ * following the year the system is in. Try this one before
+ * giving up.
+ */
+ yst = calyearstart(rec_ui + TWODAYS);
+ if (yst != *yearstart) {
+ date = (u_long)((long)yst + tmp);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ *ts_ui = date;
+ return 1;
+ }
+ }
+
+ /*
+ * Give it up.
+ */
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/clocktypes.c b/usr.sbin/xntpd/lib/clocktypes.c
new file mode 100644
index 0000000..4c5f437
--- /dev/null
+++ b/usr.sbin/xntpd/lib/clocktypes.c
@@ -0,0 +1,72 @@
+/*
+ * Data for pretty printing clock types
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "lib_strbuf.h"
+#include "ntp_refclock.h"
+
+struct clktype clktypes[] = {
+ { REFCLK_NONE, "unspecified type (0)",
+ "UNKNOWN" },
+ { REFCLK_LOCALCLOCK, "Undisciplined local clock (1)",
+ "LOCAL" },
+ { REFCLK_GPS_TRAK, "TRAK 8810 GPS Receiver (2)",
+ "GPS_TRAK" },
+ { REFCLK_WWV_PST, "PSTI/Traconex WWV/WWVH Receiver (3)",
+ "WWV_PST" },
+ { REFCLK_WWVB_SPECTRACOM, "Spectracom WWVB Receiver (4)",
+ "WWVB_SPEC" },
+ { REFCLK_GOES_TRUETIME, "TrueTime GPS/GOES Receivers (5)",
+ "GPS_GOES_TRUE" },
+ { REFCLK_IRIG_AUDIO, "IRIG Audio Decoder (6)",
+ "IRIG_AUDIO" },
+ { REFCLK_CHU, "Scratchbuilt CHU Receiver (7)",
+ "CHU" },
+ { REFCLK_PARSE, "Generic reference clock driver (8)",
+ "GENERIC" },
+ { REFCLK_GPS_MX4200, "Magnavox MX4200 GPS Receiver (9)",
+ "GPS_MX4200" },
+ { REFCLK_GPS_AS2201, "Austron 2201A GPS Receiver (10)",
+ "GPS_AS2201" },
+ { REFCLK_OMEGA_TRUETIME, "TrueTime OM-DC OMEGA Receiver (11)",
+ "OMEGA_TRUE" },
+ { REFCLK_IRIG_TPRO, "KSI/Odetics TPRO/S IRIG Interface (12)",
+ "IRIG_TPRO" },
+ { REFCLK_ATOM_LEITCH, "Leitch CSD 5300 Master Clock Controller (13)",
+ "ATOM_LEITCH" },
+ { REFCLK_MSF_EES, "EES M201 MSF Receiver (14)",
+ "MSF_EES" },
+ { REFCLK_GPSTM_TRUETIME, "TrueTime GPS/TM-TMD Receiver (15)",
+ "GPS_TRUE" },
+ { REFCLK_IRIG_BANCOMM, "Bancomm GPS/IRIG Receiver (16)",
+ "GPS_BANC" },
+ { REFCLK_GPS_DATUM, "Datum Precision Time System (17)",
+ "GPS_DATUM" },
+ { REFCLK_NIST_ACTS, "NIST Automated Computer Time Service (18)",
+ "NIST_ACTS" },
+ { REFCLK_WWV_HEATH, "Heath WWV/WWVH Receiver (19)",
+ "WWV_HEATH" },
+ { REFCLK_GPS_NMEA, "Generic NMEA GPS Receiver (20)",
+ "GPS_NMEA" },
+ { REFCLK_GPS_MOTO, "Motorola Six Gun GPS Receiver (21)",
+ "GPS_MOTO" },
+ { REFCLK_ATOM_PPS, "PPS Clock Discipline (22)",
+ "ATOM_PPS" },
+ { -1, "", "" }
+};
+
+const char *
+clockname(num)
+ int num;
+{
+ register struct clktype *clk;
+
+ for (clk = clktypes; clk->code != -1; clk++) {
+ if (num == clk->code)
+ return (clk->abbrev);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/xntpd/lib/decodenetnum.c b/usr.sbin/xntpd/lib/decodenetnum.c
new file mode 100644
index 0000000..f19b137
--- /dev/null
+++ b/usr.sbin/xntpd/lib/decodenetnum.c
@@ -0,0 +1,58 @@
+/*
+ * decodenetnum - return a net number (this is crude, but careful)
+ */
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "ntp_stdlib.h"
+
+int
+decodenetnum(num, netnum)
+ const char *num;
+ u_long *netnum;
+{
+ register const char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ register int eos;
+ char buf[80]; /* will core dump on really stupid stuff */
+
+ cp = num;
+ *netnum = 0;
+
+ if (*cp == '[') {
+ eos = ']';
+ cp++;
+ } else {
+ eos = '\0';
+ }
+
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != eos)
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ *netnum <<= 8;
+ *netnum += temp;
+ }
+
+ if (i < 4)
+ return 0;
+ *netnum = htonl(*netnum);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/dofptoa.c b/usr.sbin/xntpd/lib/dofptoa.c
new file mode 100644
index 0000000..bfa9c71
--- /dev/null
+++ b/usr.sbin/xntpd/lib/dofptoa.c
@@ -0,0 +1,117 @@
+/*
+ * dofptoa - do the grunge work to convert an fp number to ascii
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+char *
+dofptoa(fpv, neg, ndec, msec)
+ u_fp fpv;
+ int neg;
+ int ndec;
+ int msec;
+{
+ register u_char *cp, *cpend;
+ register u_long val;
+ register short dec;
+ u_char cbuf[12];
+ u_char *cpdec;
+ char *buf;
+ char *bp;
+
+ /*
+ * Get a string buffer before starting
+ */
+ LIB_GETBUF(buf);
+
+ /*
+ * Zero out the buffer
+ */
+ memset((char *)cbuf, 0, sizeof cbuf);
+
+ /*
+ * Set the pointers to point at the first
+ * decimal place. Get a local copy of the value.
+ */
+ cp = cpend = &cbuf[5];
+ val = fpv;
+
+ /*
+ * If we have to, decode the integral part
+ */
+ if (!(val & 0xffff0000))
+ cp--;
+ else {
+ register u_short sv = (u_short)(val >> 16);
+ register u_short tmp;
+ register u_short ten = 10;
+
+ do {
+ tmp = sv;
+ sv /= ten;
+ *(--cp) = tmp - ((sv<<3) + (sv<<1));
+ } while (sv != 0);
+ }
+
+ /*
+ * Figure out how much of the fraction to do
+ */
+ if (msec) {
+ dec = ndec + 3;
+ if (dec < 3)
+ dec = 3;
+ cpdec = &cbuf[8];
+ } else {
+ dec = ndec;
+ cpdec = cpend;
+ }
+
+ if (dec > 6)
+ dec = 6;
+
+ if (dec > 0) {
+ do {
+ val &= 0xffff;
+ val = (val << 3) + (val << 1);
+ *cpend++ = (u_char)(val >> 16);
+ } while (--dec > 0);
+ }
+
+ if (val & 0x8000) {
+ register u_char *tp;
+ /*
+ * Round it. Ick.
+ */
+ tp = cpend;
+ *(--tp) += 1;
+ while (*tp >= 10) {
+ *tp = 0;
+ *(--tp) += 1;
+ }
+ }
+
+ /*
+ * Remove leading zeroes if necessary
+ */
+ while (cp < (cpdec -1) && *cp == 0)
+ cp++;
+
+ /*
+ * Copy it into the buffer, asciizing as we go.
+ */
+ bp = buf;
+ if (neg)
+ *bp++ = '-';
+
+ while (cp < cpend) {
+ if (cp == cpdec)
+ *bp++ = '.';
+ *bp++ = (char)(*cp++ + '0');
+ }
+ *bp = '\0';
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/dolfptoa.c b/usr.sbin/xntpd/lib/dolfptoa.c
new file mode 100644
index 0000000..24fc213
--- /dev/null
+++ b/usr.sbin/xntpd/lib/dolfptoa.c
@@ -0,0 +1,161 @@
+/*
+ * dolfptoa - do the grunge work of converting an l_fp number to decimal
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+char *
+dolfptoa(fpi, fpv, neg, ndec, msec)
+ u_long fpi;
+ u_long fpv;
+ int neg;
+ int ndec;
+ int msec;
+{
+ register u_char *cp, *cpend;
+ register u_long lwork;
+ register int dec;
+ u_char cbuf[24];
+ u_char *cpdec;
+ char *buf;
+ char *bp;
+
+ /*
+ * Get a string buffer before starting
+ */
+ LIB_GETBUF(buf);
+
+ /*
+ * Zero the character buffer
+ */
+ memset((char *) cbuf, 0, sizeof(cbuf));
+
+ /*
+ * Work on the integral part. This is biased by what I know
+ * compiles fairly well for a 68000.
+ */
+ cp = cpend = &cbuf[10];
+ lwork = fpi;
+ if (lwork & 0xffff0000) {
+ register u_long lten = 10;
+ register u_long ltmp;
+
+ do {
+ ltmp = lwork;
+ lwork /= lten;
+ ltmp -= (lwork << 3) + (lwork << 1);
+ *--cp = (u_char)ltmp;
+ } while (lwork & 0xffff0000);
+ }
+ if (lwork != 0) {
+ register u_short sten = 10;
+ register u_short stmp;
+ register u_short swork = (u_short)lwork;
+
+ do {
+ stmp = swork;
+ swork /= sten;
+ stmp -= (swork<<3) + (swork<<1);
+ *--cp = (u_char)stmp;
+ } while (swork != 0);
+ }
+
+ /*
+ * Done that, now deal with the problem of the fraction. First
+ * determine the number of decimal places.
+ */
+ if (msec) {
+ dec = ndec + 3;
+ if (dec < 3)
+ dec = 3;
+ cpdec = &cbuf[13];
+ } else {
+ dec = ndec;
+ if (dec < 0)
+ dec = 0;
+ cpdec = &cbuf[10];
+ }
+ if (dec > 12)
+ dec = 12;
+
+ /*
+ * If there's a fraction to deal with, do so.
+ */
+ if (fpv != 0) {
+ l_fp work;
+
+ work.l_ui = 0;
+ work.l_uf = fpv;
+ while (dec > 0) {
+ l_fp ftmp;
+
+ dec--;
+ /*
+ * The scheme here is to multiply the
+ * fraction (0.1234...) by ten. This moves
+ * a junk of BCD into the units part.
+ * record that and iterate.
+ */
+ work.l_ui = 0;
+ L_LSHIFT(&work);
+ ftmp = work;
+ L_LSHIFT(&work);
+ L_LSHIFT(&work);
+ L_ADD(&work, &ftmp);
+ *cpend++ = (u_char)work.l_ui;
+ if (work.l_uf == 0)
+ break;
+ }
+
+ /*
+ * Rounding is rotten
+ */
+ if (work.l_uf & 0x80000000) {
+ register u_char *tp = cpend;
+
+ *(--tp) += 1;
+ while (*tp >= 10) {
+ *tp = 0;
+ *(--tp) += 1;
+ };
+ if (tp < cp)
+ cp = tp;
+ }
+ }
+ cpend += dec;
+
+
+ /*
+ * We've now got the fraction in cbuf[], with cp pointing at
+ * the first character, cpend pointing past the last, and
+ * cpdec pointing at the first character past the decimal.
+ * Remove leading zeros, then format the number into the
+ * buffer.
+ */
+ while (cp < cpdec) {
+ if (*cp != 0)
+ break;
+ cp++;
+ }
+ if (cp == cpdec)
+ --cp;
+
+ bp = buf;
+ if (neg)
+ *bp++ = '-';
+ while (cp < cpend) {
+ if (cp == cpdec)
+ *bp++ = '.';
+ *bp++ = (char)(*cp++ + '0'); /* ascii dependent? */
+ }
+ *bp = '\0';
+
+ /*
+ * Done!
+ */
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/emalloc.c b/usr.sbin/xntpd/lib/emalloc.c
new file mode 100644
index 0000000..61d4cb6
--- /dev/null
+++ b/usr.sbin/xntpd/lib/emalloc.c
@@ -0,0 +1,20 @@
+/*
+ * emalloc - return new memory obtained from the system. Belch if none.
+ */
+#include "ntp_types.h"
+#include "ntp_malloc.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+char *
+emalloc(size)
+ u_int size;
+{
+ char *mem;
+
+ if ((mem = (char *)malloc(size)) == 0) {
+ syslog(LOG_ERR, "No more memory!");
+ exit(1);
+ }
+ return mem;
+}
diff --git a/usr.sbin/xntpd/lib/findconfig.c b/usr.sbin/xntpd/lib/findconfig.c
new file mode 100755
index 0000000..a65f32c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/findconfig.c
@@ -0,0 +1,62 @@
+#ifdef SYS_HPUX
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+char *
+FindConfig(base)
+ char *base;
+{
+ static char result[BUFSIZ];
+ char hostname[BUFSIZ], *cp;
+ struct stat sbuf;
+ struct utsname unamebuf;
+
+ /* All keyed by initial target being a directory */
+ (void) strcpy(result, base);
+ if (stat(result, &sbuf) == 0) {
+ if (S_ISDIR(sbuf.st_mode)) {
+
+ /* First choice is my hostname */
+ if (gethostname(hostname, BUFSIZ) >= 0) {
+ (void) sprintf(result, "%s/%s", base, hostname);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+
+ /* Second choice is of form default.835 */
+ (void) uname(&unamebuf);
+ if (strncmp(unamebuf.machine, "9000/", 5) == 0)
+ cp = unamebuf.machine + 5;
+ else
+ cp = unamebuf.machine;
+ (void) sprintf(result, "%s/default.%s", base, cp);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+
+ /* Last choice is just default */
+ (void) sprintf(result, "%s/default", base);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+ (void) strcpy(result, "/not/found");
+ }
+ }
+ }
+ }
+ }
+ }
+outahere:
+ return(result);
+}
+#else
+char *
+FindConfig(base)
+ char *base;
+{
+ return base;
+}
+#endif
diff --git a/usr.sbin/xntpd/lib/fptoa.c b/usr.sbin/xntpd/lib/fptoa.c
new file mode 100644
index 0000000..b0d7a14
--- /dev/null
+++ b/usr.sbin/xntpd/lib/fptoa.c
@@ -0,0 +1,24 @@
+/*
+ * fptoa - return an asciized representation of an s_fp number
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+fptoa(fpv, ndec)
+ s_fp fpv;
+ int ndec;
+{
+ u_fp plusfp;
+ int neg;
+
+ if (fpv < 0) {
+ plusfp = (u_fp)(-fpv);
+ neg = 1;
+ } else {
+ plusfp = (u_fp)fpv;
+ neg = 0;
+ }
+
+ return dofptoa(plusfp, neg, ndec, 0);
+}
diff --git a/usr.sbin/xntpd/lib/fptoms.c b/usr.sbin/xntpd/lib/fptoms.c
new file mode 100644
index 0000000..f8ae6ba
--- /dev/null
+++ b/usr.sbin/xntpd/lib/fptoms.c
@@ -0,0 +1,23 @@
+/*
+ * fptoms - return an asciized s_fp number in milliseconds
+ */
+#include "ntp_fp.h"
+
+char *
+fptoms(fpv, ndec)
+ s_fp fpv;
+ int ndec;
+{
+ u_fp plusfp;
+ int neg;
+
+ if (fpv < 0) {
+ plusfp = (u_fp)(-fpv);
+ neg = 1;
+ } else {
+ plusfp = (u_fp)fpv;
+ neg = 0;
+ }
+
+ return dofptoa(plusfp, neg, ndec, 1);
+}
diff --git a/usr.sbin/xntpd/lib/getopt.c b/usr.sbin/xntpd/lib/getopt.c
new file mode 100644
index 0000000..b5164c60
--- /dev/null
+++ b/usr.sbin/xntpd/lib/getopt.c
@@ -0,0 +1,105 @@
+/*
+ * getopt - get option letter from argv
+ *
+ * This is a version of the public domain getopt() implementation by
+ * Henry Spencer, changed for 4.3BSD compatibility (in addition to System V).
+ * It allows rescanning of an option list by setting optind to 0 before
+ * calling, which is why we use it even if the system has its own (in fact,
+ * this one has a unique name so as not to conflict with the system's).
+ * Thanks to Dennis Ferguson for the appropriate modifications.
+ *
+ * This file is in the Public Domain.
+ */
+
+/*LINTLIBRARY*/
+
+#include <stdio.h>
+
+#include "ntp_stdlib.h"
+
+#ifdef lint
+#undef putc
+#define putc fputc
+#endif /* lint */
+
+char *ntp_optarg; /* Global argument pointer. */
+int ntp_optind = 0; /* Global argv index. */
+int ntp_opterr = 1; /* for compatibility, should error be printed? */
+int ntp_optopt; /* for compatibility, option character checked */
+
+static char *scan = NULL; /* Private scan pointer. */
+static char *prog = "amnesia";
+
+/*
+ * Print message about a bad option.
+ */
+static int
+badopt(mess, ch)
+ char *mess;
+ int ch;
+{
+ if (ntp_opterr) {
+ fputs(prog, stderr);
+ fputs(mess, stderr);
+ (void) putc(ch, stderr);
+ (void) putc('\n', stderr);
+ }
+ return ('?');
+}
+
+int
+ntp_getopt(argc, argv, optstring)
+ int argc;
+ char *argv[];
+ char *optstring;
+{
+ register char c;
+ register char *place;
+
+ prog = argv[0];
+ ntp_optarg = NULL;
+
+ if (ntp_optind == 0) {
+ scan = NULL;
+ ntp_optind++;
+ }
+
+ if (scan == NULL || *scan == '\0') {
+ if (ntp_optind >= argc
+ || argv[ntp_optind][0] != '-'
+ || argv[ntp_optind][1] == '\0') {
+ return (EOF);
+ }
+ if (argv[ntp_optind][1] == '-'
+ && argv[ntp_optind][2] == '\0') {
+ ntp_optind++;
+ return (EOF);
+ }
+
+ scan = argv[ntp_optind++]+1;
+ }
+
+ c = *scan++;
+ ntp_optopt = c & 0377;
+ for (place = optstring; place != NULL && *place != '\0'; ++place)
+ if (*place == c)
+ break;
+
+ if (place == NULL || *place == '\0' || c == ':' || c == '?') {
+ return (badopt(": unknown option -", c));
+ }
+
+ place++;
+ if (*place == ':') {
+ if (*scan != '\0') {
+ ntp_optarg = scan;
+ scan = NULL;
+ } else if (ntp_optind >= argc) {
+ return (badopt(": option requires argument -", c));
+ } else {
+ ntp_optarg = argv[ntp_optind++];
+ }
+ }
+
+ return (c & 0377);
+}
diff --git a/usr.sbin/xntpd/lib/gettstamp.c b/usr.sbin/xntpd/lib/gettstamp.c
new file mode 100644
index 0000000..53b124a
--- /dev/null
+++ b/usr.sbin/xntpd/lib/gettstamp.c
@@ -0,0 +1,29 @@
+/*
+ * gettstamp - return the system time in timestamp format
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+void
+gettstamp(ts)
+ l_fp *ts;
+{
+ struct timeval tv;
+
+ /*
+ * Quickly get the time of day and convert it
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ if (tv.tv_usec >= 1000000) { /* bum solaris */
+ tv.tv_usec -= 1000000;
+ tv.tv_sec++;
+ }
+ TVTOTS(&tv, ts);
+ ts->l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+ ts->l_ui += JAN_1970;
+ ts->l_uf &= TS_MASK;
+}
diff --git a/usr.sbin/xntpd/lib/hextoint.c b/usr.sbin/xntpd/lib/hextoint.c
new file mode 100644
index 0000000..23f8c08
--- /dev/null
+++ b/usr.sbin/xntpd/lib/hextoint.c
@@ -0,0 +1,38 @@
+/*
+ * hextoint - convert an ascii string in hex to an unsigned
+ * long, with error checking
+ */
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+hextoint(str, ival)
+ const char *str;
+ u_long *ival;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isxdigit(*cp))
+ return 0;
+ if (u >= 0x10000000)
+ return 0; /* overflow */
+ u <<= 4;
+ if (*cp <= '9') /* very ascii dependent */
+ u += *cp++ - '0';
+ else if (*cp >= 'a')
+ u += *cp++ - 'a' + 10;
+ else
+ u += *cp++ - 'A' + 10;
+ }
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/hextolfp.c b/usr.sbin/xntpd/lib/hextolfp.c
new file mode 100644
index 0000000..104446c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/hextolfp.c
@@ -0,0 +1,66 @@
+/*
+ * hextolfp - convert an ascii hex string to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+int
+hextolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register const char *cpstart;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind = NULL;
+ static char *digits = "0123456789abcdefABCDEF";
+
+ dec_i = dec_f = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces]8_hex_digits[.]8_hex_digits[spaces|\n|\0]
+ */
+ while (isspace(*cp))
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = dec_i << 4; /* multiply by 16 */
+ dec_i += ((ind - digits) > 15) ? (ind - digits) - 6
+ : (ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+ if (*cp == '.')
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_f = dec_f << 4; /* multiply by 16 */
+ dec_f += ((ind - digits) > 15) ? (ind - digits) - 6
+ : (ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/humandate.c b/usr.sbin/xntpd/lib/humandate.c
new file mode 100644
index 0000000..b7b89bc
--- /dev/null
+++ b/usr.sbin/xntpd/lib/humandate.c
@@ -0,0 +1,61 @@
+/*
+ * humandate - convert an NTP (or the current) time to something readable
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+static char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+static char *days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+char *
+humandate(ntptime)
+ u_long ntptime;
+{
+ char *bp;
+ struct tm *tm;
+ time_t sec;
+
+ LIB_GETBUF(bp);
+
+ sec = ntptime - JAN_1970;
+ tm = localtime(&sec);
+
+ (void) sprintf(bp, "%s, %s %2d %4d %2d:%02d:%02d",
+ days[tm->tm_wday], months[tm->tm_mon], tm->tm_mday,
+ 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return bp;
+}
+
+
+/* This is used in msyslog.c; we don't want to clutter up the log with
+ the year and day of the week, etc.; just the minimal date and time. */
+
+char *
+humanlogtime()
+{
+ char *bp;
+ time_t cursec = time((time_t *) 0);
+ struct tm *tm = localtime(&cursec);
+
+ LIB_GETBUF(bp);
+
+ (void) sprintf(bp, "%2d %s %02d:%02d:%02d",
+ tm->tm_mday, months[tm->tm_mon],
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/inttoa.c b/usr.sbin/xntpd/lib/inttoa.c
new file mode 100644
index 0000000..ff886f5
--- /dev/null
+++ b/usr.sbin/xntpd/lib/inttoa.c
@@ -0,0 +1,19 @@
+/*
+ * inttoa - return an asciized signed integer
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+inttoa(ival)
+ long ival;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%ld", (long)ival);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/lib_strbuf.c b/usr.sbin/xntpd/lib/lib_strbuf.c
new file mode 100644
index 0000000..47df66d
--- /dev/null
+++ b/usr.sbin/xntpd/lib/lib_strbuf.c
@@ -0,0 +1,21 @@
+/*
+ * lib_strbuf - library string storage
+ */
+
+#include "lib_strbuf.h"
+
+/*
+ * Storage declarations
+ */
+char lib_stringbuf[LIB_NUMBUFS][LIB_BUFLENGTH];
+int lib_nextbuf;
+
+
+/*
+ * initialization routine. Might be needed if the code is ROMized.
+ */
+void
+init_lib()
+{
+ lib_nextbuf = 0;
+}
diff --git a/usr.sbin/xntpd/lib/lib_strbuf.h b/usr.sbin/xntpd/lib/lib_strbuf.h
new file mode 100644
index 0000000..699b978
--- /dev/null
+++ b/usr.sbin/xntpd/lib/lib_strbuf.h
@@ -0,0 +1,22 @@
+/*
+ * lib_strbuf.h - definitions for routines which use the common string buffers
+ */
+
+/*
+ * Sizes of things
+ */
+#define LIB_NUMBUFS 20
+#define LIB_BUFLENGTH 80
+
+/*
+ * Macro to get a pointer to the next buffer
+ */
+#define LIB_GETBUF(buf) \
+ do { \
+ buf = &lib_stringbuf[lib_nextbuf][0]; \
+ if (++lib_nextbuf >= LIB_NUMBUFS) \
+ lib_nextbuf = 0; \
+ } while (0)
+
+extern char lib_stringbuf[LIB_NUMBUFS][LIB_BUFLENGTH];
+extern int lib_nextbuf;
diff --git a/usr.sbin/xntpd/lib/machines.c b/usr.sbin/xntpd/lib/machines.c
new file mode 100644
index 0000000..2fc7785
--- /dev/null
+++ b/usr.sbin/xntpd/lib/machines.c
@@ -0,0 +1,61 @@
+/* machines.c - provide special support for peculiar architectures
+ *
+ * Real bummers unite !
+ *
+ * $Id$
+ */
+
+#include "ntp_stdlib.h"
+
+#ifdef SYS_PTX
+#include <sys/types.h>
+#include <sys/procstats.h>
+
+int
+settimeofday(tvp)
+ struct timeval *tvp;
+{
+ return (stime(&tvp->tv_sec)); /* lie as bad as SysVR4 */
+}
+
+int
+gettimeofday(tvp)
+ struct timeval *tvp;
+{
+ /*
+ * hi, this is Sequents sneak path to get to a clock
+ * this is also the most logical syscall for such a function
+ */
+ return (get_process_stats(tvp, PS_SELF, (struct procstats *) 0,
+ (struct procstats *) 0));
+}
+#endif
+
+#if !defined(NTP_POSIX_SOURCE) || defined(NTP_NEED_BOPS)
+void
+ntp_memset(a, x, c)
+ char *a;
+ int x, c;
+{
+ while (c-- > 0)
+ *a++ = x;
+}
+#endif /*POSIX*/
+
+#if defined(USE_CLOCK_SETTIME)
+
+#include <time.h>
+
+int
+settimeofday(tvp)
+ struct timeval *tvp;
+{
+ struct timespec ts;
+
+ /* Convert timeval to timespec */
+ ts.tv_sec = tvp->tv_sec;
+ ts.tv_nsec = 1000 * tvp->tv_usec;
+
+ return clock_settime(CLOCK_REALTIME, &ts);
+}
+#endif /* USE_CLOCK_SETTIME */
diff --git a/usr.sbin/xntpd/lib/md5.c b/usr.sbin/xntpd/lib/md5.c
new file mode 100644
index 0000000..a5aca7a
--- /dev/null
+++ b/usr.sbin/xntpd/lib/md5.c
@@ -0,0 +1,322 @@
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform ();
+
+#ifdef __STDC__
+static const
+#else
+static
+#endif
+unsigned char PADDING[64] = {
+ 0x80, 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, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#if defined(FAST_MD5) && defined(__GNUC__) && defined(mc68000)
+/*
+ * If we're on a 68000 based CPU and using a GNU C compiler with
+ * inline assembly code, we can speed this up a bit.
+ */
+inline UINT4 ROTATE_LEFT(UINT4 x, int n)
+{
+ asm("roll %2,%0" : "=d" (x) : "0" (x), "Ir" (n));
+ return x;
+}
+#else
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+#endif
+
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+
+void MD5Final (mdContext)
+MD5_CTX *mdContext;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+
+ FF ( a, b, c, d, in[ 0], S11, 0xd76aa478); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, 0xe8c7b756); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, 0x242070db); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, 0xc1bdceee); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, 0xf57c0faf); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, 0x4787c62a); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, 0xa8304613); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, 0xfd469501); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, 0x698098d8); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, 0x8b44f7af); /* 10 */
+ FF ( c, d, a, b, in[10], S13, 0xffff5bb1); /* 11 */
+ FF ( b, c, d, a, in[11], S14, 0x895cd7be); /* 12 */
+ FF ( a, b, c, d, in[12], S11, 0x6b901122); /* 13 */
+ FF ( d, a, b, c, in[13], S12, 0xfd987193); /* 14 */
+ FF ( c, d, a, b, in[14], S13, 0xa679438e); /* 15 */
+ FF ( b, c, d, a, in[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, 0xf61e2562); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, 0xc040b340); /* 18 */
+ GG ( c, d, a, b, in[11], S23, 0x265e5a51); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, 0xd62f105d); /* 21 */
+ GG ( d, a, b, c, in[10], S22, 0x2441453); /* 22 */
+ GG ( c, d, a, b, in[15], S23, 0xd8a1e681); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, 0x21e1cde6); /* 25 */
+ GG ( d, a, b, c, in[14], S22, 0xc33707d6); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, 0xf4d50d87); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, 0x455a14ed); /* 28 */
+ GG ( a, b, c, d, in[13], S21, 0xa9e3e905); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, 0x676f02d9); /* 31 */
+ GG ( b, c, d, a, in[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, 0xfffa3942); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, 0x8771f681); /* 34 */
+ HH ( c, d, a, b, in[11], S33, 0x6d9d6122); /* 35 */
+ HH ( b, c, d, a, in[14], S34, 0xfde5380c); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, 0xa4beea44); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH ( b, c, d, a, in[10], S34, 0xbebfbc70); /* 40 */
+ HH ( a, b, c, d, in[13], S31, 0x289b7ec6); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, 0xeaa127fa); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, 0xd4ef3085); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, 0x4881d05); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, 0xd9d4d039); /* 45 */
+ HH ( d, a, b, c, in[12], S32, 0xe6db99e5); /* 46 */
+ HH ( c, d, a, b, in[15], S33, 0x1fa27cf8); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, 0xf4292244); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, 0x432aff97); /* 50 */
+ II ( c, d, a, b, in[14], S43, 0xab9423a7); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, 0xfc93a039); /* 52 */
+ II ( a, b, c, d, in[12], S41, 0x655b59c3); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, 0x8f0ccc92); /* 54 */
+ II ( c, d, a, b, in[10], S43, 0xffeff47d); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, 0x85845dd1); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, 0x6fa87e4f); /* 57 */
+ II ( d, a, b, c, in[15], S42, 0xfe2ce6e0); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, 0xa3014314); /* 59 */
+ II ( b, c, d, a, in[13], S44, 0x4e0811a1); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, 0xf7537e82); /* 61 */
+ II ( d, a, b, c, in[11], S42, 0xbd3af235); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, 0xeb86d391); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/lib/mfptoa.c b/usr.sbin/xntpd/lib/mfptoa.c
new file mode 100644
index 0000000..7de3b6e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mfptoa.c
@@ -0,0 +1,22 @@
+/*
+ * mfptoa - Return an asciized representation of a signed long fp number
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+mfptoa(fpi, fpf, ndec)
+ u_long fpi;
+ u_long fpf;
+ int ndec;
+{
+ int isneg;
+
+ if (M_ISNEG(fpi, fpf)) {
+ isneg = 1;
+ M_NEG(fpi, fpf);
+ } else
+ isneg = 0;
+
+ return dolfptoa(fpi, fpf, isneg, ndec, 0);
+}
diff --git a/usr.sbin/xntpd/lib/mfptoms.c b/usr.sbin/xntpd/lib/mfptoms.c
new file mode 100644
index 0000000..01a6f09
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mfptoms.c
@@ -0,0 +1,22 @@
+/*
+ * mfptoms - Return an asciized signed long fp number in milliseconds
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+mfptoms(fpi, fpf, ndec)
+ u_long fpi;
+ u_long fpf;
+ int ndec;
+{
+ int isneg;
+
+ if (M_ISNEG(fpi, fpf)) {
+ isneg = 1;
+ M_NEG(fpi, fpf);
+ } else
+ isneg = 0;
+
+ return dolfptoa(fpi, fpf, isneg, ndec, 1);
+}
diff --git a/usr.sbin/xntpd/lib/modetoa.c b/usr.sbin/xntpd/lib/modetoa.c
new file mode 100644
index 0000000..e8292b3
--- /dev/null
+++ b/usr.sbin/xntpd/lib/modetoa.c
@@ -0,0 +1,33 @@
+/*
+ * modetoa - return an asciized mode
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+modetoa(mode)
+ int mode;
+{
+ char *bp;
+ static char *modestrings[] = {
+ "unspec",
+ "sym_active",
+ "sym_passive",
+ "client",
+ "server",
+ "broadcast",
+ "control",
+ "private",
+ "bclient",
+ };
+
+ if (mode < 0 || mode >= (sizeof modestrings)/sizeof(char *)) {
+ LIB_GETBUF(bp);
+ (void)sprintf(bp, "mode#%d", mode);
+ return bp;
+ }
+
+ return modestrings[mode];
+}
diff --git a/usr.sbin/xntpd/lib/mstolfp.c b/usr.sbin/xntpd/lib/mstolfp.c
new file mode 100644
index 0000000..e4c4d46
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mstolfp.c
@@ -0,0 +1,99 @@
+/*
+ * mstolfp - convert an ascii string in milliseconds to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+int
+mstolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register char *bp;
+ register const char *cpdec;
+ char buf[100];
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-][digits][.][digits][spaces|\n|\0]
+ *
+ * This is one enormous hack. Since I didn't feel like
+ * rewriting the decoding routine for milliseconds, what
+ * is essentially done here is to make a copy of the string
+ * with the decimal moved over three places so the seconds
+ * decoding routine can be used.
+ */
+ bp = buf;
+ cp = str;
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '-') {
+ *bp++ = '-';
+ cp++;
+ }
+
+ if (*cp != '.' && !isdigit(*cp))
+ return 0;
+
+
+ /*
+ * Search forward for the decimal point or the end of the string.
+ */
+ cpdec = cp;
+ while (isdigit(*cpdec))
+ cpdec++;
+
+ /*
+ * Found something. If we have more than three digits copy the
+ * excess over, else insert a leading 0.
+ */
+ if ((cpdec - cp) > 3) {
+ do {
+ *bp++ = (char)*cp++;
+ } while ((cpdec - cp) > 3);
+ } else {
+ *bp++ = '0';
+ }
+
+ /*
+ * Stick the decimal in. If we've got less than three digits in
+ * front of the millisecond decimal we insert the appropriate number
+ * of zeros.
+ */
+ *bp++ = '.';
+ if ((cpdec - cp) < 3) {
+ register int i = 3 - (cpdec - cp);
+
+ do {
+ *bp++ = '0';
+ } while (--i > 0);
+ }
+
+ /*
+ * Copy the remainder up to the millisecond decimal. If cpdec
+ * is pointing at a decimal point, copy in the trailing number too.
+ */
+ while (cp < cpdec)
+ *bp++ = (char)*cp++;
+
+ if (*cp == '.') {
+ cp++;
+ while (isdigit(*cp))
+ *bp++ = (char)*cp++;
+ }
+ *bp = '\0';
+
+ /*
+ * Check to make sure the string is properly terminated. If
+ * so, give the buffer to the decoding routine.
+ */
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+ return atolfp(buf, lfp);
+}
diff --git a/usr.sbin/xntpd/lib/msutotsf.c b/usr.sbin/xntpd/lib/msutotsf.c
new file mode 100644
index 0000000..eb3babe
--- /dev/null
+++ b/usr.sbin/xntpd/lib/msutotsf.c
@@ -0,0 +1,35 @@
+/*
+ * msutotsf - tables for converting from a subsecond millisecond value
+ * to a time stamp fraction.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+
+/*
+ * Index each of these tables with five bits of the (less than) 10
+ * bit millisecond value. Note that the tables are rounded (not
+ * truncated). The error in the result will thus be +-1 low order
+ * bit in the time stamp fraction.
+ */
+u_long msutotsflo[32] = {
+ 0x00000000, 0x00418937, 0x0083126f, 0x00c49ba6,
+ 0x010624dd, 0x0147ae14, 0x0189374c, 0x01cac083,
+ 0x020c49ba, 0x024dd2f2, 0x028f5c29, 0x02d0e560,
+ 0x03126e98, 0x0353f7cf, 0x03958106, 0x03d70a3d,
+ 0x04189375, 0x045a1cac, 0x049ba5e3, 0x04dd2f1b,
+ 0x051eb852, 0x05604189, 0x05a1cac1, 0x05e353f8,
+ 0x0624dd2f, 0x06666666, 0x06a7ef9e, 0x06e978d5,
+ 0x072b020c, 0x076c8b44, 0x07ae147b, 0x07ef9db2
+};
+
+u_long msutotsfhi[32] = {
+ 0x00000000, 0x083126e9, 0x10624dd3, 0x189374bc,
+ 0x20c49ba6, 0x28f5c28f, 0x3126e979, 0x39581062,
+ 0x4189374c, 0x49ba5e35, 0x51eb851f, 0x5a1cac08,
+ 0x624dd2f2, 0x6a7ef9db, 0x72b020c5, 0x7ae147ae,
+ 0x83126e98, 0x8b439581, 0x9374bc6a, 0x9ba5e354,
+ 0xa3d70a3d, 0xac083127, 0xb4395810, 0xbc6a7efa,
+ 0xc49ba5e3, 0xcccccccd, 0xd4fdf3b6, 0xdd2f1aa0,
+ 0xe5604189, 0xed916873, 0xf5c28f5c, 0xfdf3b646
+};
diff --git a/usr.sbin/xntpd/lib/msyslog.c b/usr.sbin/xntpd/lib/msyslog.c
new file mode 100644
index 0000000..d71b075
--- /dev/null
+++ b/usr.sbin/xntpd/lib/msyslog.c
@@ -0,0 +1,111 @@
+/*
+ * msyslog - either send a message to the terminal or print it on
+ * the standard output.
+ *
+ * Converted to use varargs, much better ... jks
+ */
+#include <stdio.h>
+#include <errno.h>
+
+/* alternative, as Solaris 2.x defines __STDC__ as 0 in a largely standard
+ conforming environment
+ #if __STDC__ || (defined(SOLARIS) && defined(__STDC__))
+*/
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_syslog.h"
+#include "ntp_stdlib.h"
+
+#undef syslog
+
+int syslogit = 1;
+FILE *syslog_file = NULL;
+
+extern int errno;
+extern char *progname;
+
+#if defined(__STDC__)
+void msyslog(int level, char *fmt, ...)
+#else
+/*VARARGS*/
+void msyslog(va_alist)
+ va_dcl
+#endif
+{
+#ifndef __STDC__
+ int level;
+ char *fmt;
+#endif
+ va_list ap;
+ char buf[1025], nfmt[256], xerr[50];
+ const char *err;
+ register int c, l;
+ register char *n, *f, *prog;
+#ifndef SYS_44BSD
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+#endif
+ int olderrno;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+
+ level = va_arg(ap, int);
+ fmt = va_arg(ap, char *);
+#endif
+
+ olderrno = errno;
+ n = nfmt;
+ f = fmt;
+ while ((c = *f++) != '\0' && c != '\n' && n < &nfmt[252]) {
+ if (c != '%') {
+ *n++ = c;
+ continue;
+ }
+ if ((c = *f++) != 'm') {
+ *n++ = '%';
+ *n++ = c;
+ continue;
+ }
+ if ((unsigned)olderrno > sys_nerr)
+ sprintf((char *)(err = xerr), "error %d", olderrno);
+ else
+ err = sys_errlist[olderrno];
+ if (n + (l = strlen(err)) < &nfmt[254]) {
+ strcpy(n, err);
+ n += strlen(err);
+ }
+ }
+ if (!syslogit)
+ *n++ = '\n';
+ *n = '\0';
+
+ vsprintf(buf, nfmt, ap);
+ if (syslogit)
+ syslog(level, buf);
+ else {
+ extern char * humanlogtime P((void));
+
+ FILE *out_file = syslog_file ? syslog_file
+ : level <= LOG_ERR ? stderr : stdout;
+ /* syslog() provides the timestamp, so if we're not using
+ syslog, we must provide it. */
+ prog = strrchr(progname, '/');
+ if (prog == NULL)
+ prog = progname;
+ else
+ prog++;
+ (void) fprintf(out_file, "%s ", humanlogtime ());
+ (void) fprintf(out_file, "%s: %s", prog, buf);
+ fflush (out_file);
+ }
+ va_end(ap);
+}
diff --git a/usr.sbin/xntpd/lib/netof.c b/usr.sbin/xntpd/lib/netof.c
new file mode 100644
index 0000000..1823fb2
--- /dev/null
+++ b/usr.sbin/xntpd/lib/netof.c
@@ -0,0 +1,24 @@
+/*
+ * netof - return the net address part of an ip address
+ * (zero out host part)
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+u_long
+netof(num)
+ u_long num;
+{
+ register u_long netnum;
+
+ netnum = num;
+ if(IN_CLASSC(netnum))
+ netnum &= IN_CLASSC_NET;
+ else if (IN_CLASSB(netnum))
+ netnum &= IN_CLASSB_NET;
+ else /* treat all other like class A */
+ netnum &= IN_CLASSA_NET;
+ return netnum;
+}
diff --git a/usr.sbin/xntpd/lib/numtoa.c b/usr.sbin/xntpd/lib/numtoa.c
new file mode 100644
index 0000000..ef291c8
--- /dev/null
+++ b/usr.sbin/xntpd/lib/numtoa.c
@@ -0,0 +1,22 @@
+/*
+ * numtoa - return asciized network numbers store in local array space
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+numtoa(num)
+ u_long num;
+{
+ register u_long netnum;
+ register char *buf;
+
+ netnum = ntohl(num);
+ LIB_GETBUF(buf);
+ (void) sprintf(buf, "%lu.%lu.%lu.%lu", (netnum >> 24) & 0xff,
+ (netnum >> 16) & 0xff, (netnum >> 8) & 0xff, netnum & 0xff);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/numtohost.c b/usr.sbin/xntpd/lib/numtohost.c
new file mode 100644
index 0000000..9d83584
--- /dev/null
+++ b/usr.sbin/xntpd/lib/numtohost.c
@@ -0,0 +1,38 @@
+/*
+ * numtohost - convert network number to host name.
+ */
+#include <netdb.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "lib_strbuf.h"
+
+#define LOOPBACKNET 0x7f000000
+#define LOOPBACKHOST 0x7f000001
+#define LOOPBACKNETMASK 0xff000000
+
+char *
+numtohost(netnum)
+ u_long netnum;
+{
+ char *bp;
+ struct hostent *hp;
+
+ /*
+ * This is really gross, but saves lots of hanging looking for
+ * hostnames for the radio clocks. Don't bother looking up
+ * addresses on the loopback network except for the loopback
+ * host itself.
+ */
+ if ((((ntohl(netnum) & LOOPBACKNETMASK) == LOOPBACKNET)
+ && (ntohl(netnum) != LOOPBACKHOST))
+ || ((hp = gethostbyaddr((char *)&netnum, sizeof netnum, AF_INET))
+ == 0))
+ return numtoa(netnum);
+
+ LIB_GETBUF(bp);
+
+ bp[LIB_BUFLENGTH-1] = '\0';
+ (void) strncpy(bp, hp->h_name, LIB_BUFLENGTH-1);
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/octtoint.c b/usr.sbin/xntpd/lib/octtoint.c
new file mode 100644
index 0000000..7598d72
--- /dev/null
+++ b/usr.sbin/xntpd/lib/octtoint.c
@@ -0,0 +1,34 @@
+/*
+ * octtoint - convert an ascii string in octal to an unsigned
+ * long, with error checking
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+octtoint(str, ival)
+ const char *str;
+ u_long *ival;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp) || *cp == '8' || *cp == '9')
+ return 0;
+ if (u >= 0x20000000)
+ return 0; /* overflow */
+ u <<= 3;
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/prettydate.c b/usr.sbin/xntpd/lib/prettydate.c
new file mode 100644
index 0000000..fd4a8b1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/prettydate.c
@@ -0,0 +1,44 @@
+/*
+ * prettydate - convert a time stamp to something readable
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+char *
+prettydate(ts)
+ l_fp *ts;
+{
+ char *bp;
+ struct tm *tm;
+ time_t sec;
+ u_long msec;
+ static char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ static char *days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+
+ LIB_GETBUF(bp);
+
+ sec = ts->l_ui - JAN_1970;
+ msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */
+
+ tm = localtime(&sec);
+
+ (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu",
+ (u_long)ts->l_ui, (u_long)ts->l_uf, days[tm->tm_wday],
+ months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year,
+ tm->tm_hour,tm->tm_min, tm->tm_sec, msec);
+
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/ranny.c b/usr.sbin/xntpd/lib/ranny.c
new file mode 100644
index 0000000..3fe7b22
--- /dev/null
+++ b/usr.sbin/xntpd/lib/ranny.c
@@ -0,0 +1,81 @@
+/*
+ * Random number generator is:
+ *
+ * Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
+ * This will be free software, but only when it is finished.
+ *
+ * Used in xntp by permission of the author. If copyright is
+ * annoying to you, read no further. Instead, look up the reference,
+ * write me an equivalent to this and send it back to me.
+ */
+
+/*
+ * Random number generator; see Knuth Vol 2. 2nd ed. p.27 (section 3.2.2)
+ */
+#include "ntp_stdlib.h"
+
+extern time_t time P((time_t *loc));
+
+/*
+ * 55 random numbers, not all even. Note we don't initialize ran_y
+ * directly since I have had thoughts of putting this in an EPROM
+ */
+static time_t ran_y[55];
+
+static time_t init_ran_y[55] = {
+ 1860909544, 231033423, 437666411, 1349655137, 2014584962,
+ 504613712, 656256107, 1246027206, 573713775, 643466871,
+ 540235388, 1630565153, 443649364, 729302839, 1933991552,
+ 944681982, 949111118, 406212522, 1065063137, 1712954727,
+ 73280612, 787623973, 1874130997, 801658492, 73395958,
+ 739165367, 596047144, 490055249, 1131094323, 662727104,
+ 483614097, 844520219, 893760527, 921280508, 46691708,
+ 760861842, 1425894220, 702947816, 2006889048, 1999607995,
+ 1346414687, 399640789, 1482689501, 1790064052, 1128943628,
+ 1269197405, 587262386, 2078054746, 1675409928, 1652325524,
+ 1643525825, 1748690540, 292465849, 1370173174, 402865384
+};
+
+static int ran_j;
+static int ran_k;
+
+
+/*
+ * ranp2 - return a random integer in the range 0 .. (1 << m) - 1
+ */
+u_long
+ranp2(m)
+ int m;
+{
+ time_t r;
+
+ ran_y[ran_k] += ran_y[ran_j]; /* overflow does a mod */
+ r = ran_y[ran_k];
+ if (ran_k-- == 0)
+ ran_k = 54;
+ if (ran_j-- == 0)
+ ran_j = 54;
+ return (u_long)(r & ((1 << m ) - 1));
+}
+
+/*
+ * init_random - do initialization of random number routine
+ */
+void
+init_random()
+{
+ register int i;
+ register time_t now;
+
+ ran_j = 23;
+ ran_k = 54;
+
+ /*
+ * Randomize the seed array some more. The time of day
+ * should be initialized by now.
+ */
+ now = time((time_t *)0) | 01;
+
+ for (i = 0; i < 55; ++i)
+ ran_y[i] = now * init_ran_y[i]; /* overflow does a mod */
+}
diff --git a/usr.sbin/xntpd/lib/refnumtoa.c b/usr.sbin/xntpd/lib/refnumtoa.c
new file mode 100644
index 0000000..8bb7d38
--- /dev/null
+++ b/usr.sbin/xntpd/lib/refnumtoa.c
@@ -0,0 +1,30 @@
+/*
+ * refnumtoa - return asciized refclock addresses stored in local array space
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+refnumtoa(num)
+ u_long num;
+{
+ register u_long netnum;
+ register char *buf;
+ register const char *rclock;
+
+ netnum = ntohl(num);
+
+ LIB_GETBUF(buf);
+
+ rclock = clockname((int)((netnum >> 8) & 0xff));
+
+ if (rclock != NULL)
+ (void)sprintf(buf, "%s(%lu)", rclock, netnum & 0xff);
+ else
+ (void)sprintf(buf, "REFCLK(%lu,%lu)",
+ (netnum >> 8) & 0xff, netnum&0xff);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/syssignal.c b/usr.sbin/xntpd/lib/syssignal.c
new file mode 100644
index 0000000..f8abfdd
--- /dev/null
+++ b/usr.sbin/xntpd/lib/syssignal.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "ntp_stdlib.h"
+
+#if defined(NTP_POSIX_SOURCE)
+#include <errno.h>
+
+extern int errno;
+
+void
+signal_no_reset(sig, func)
+int sig;
+void (*func)();
+{
+ int n;
+ struct sigaction vec;
+
+ vec.sa_handler = func;
+ sigemptyset(&vec.sa_mask);
+ vec.sa_flags = 0;
+
+ while (1) {
+ n = sigaction(sig, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+}
+
+#else
+RETSIGTYPE
+signal_no_reset(sig, func)
+int sig;
+RETSIGTYPE (*func)();
+{
+ signal(sig, func);
+
+}
+#endif
+
diff --git a/usr.sbin/xntpd/lib/systime.c b/usr.sbin/xntpd/lib/systime.c
new file mode 100644
index 0000000..53f0e67
--- /dev/null
+++ b/usr.sbin/xntpd/lib/systime.c
@@ -0,0 +1,376 @@
+/*
+ * systime -- routines to fiddle a UNIX clock.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/param.h>
+#include <utmp.h>
+#endif
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+#endif
+
+#include "ntp_fp.h"
+#include "ntp_syslog.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined(STEP_SLEW)
+#define SLEWALWAYS
+#endif
+
+extern int debug;
+
+/*
+ * These routines (init_systime, get_systime, step_systime, adj_systime)
+ * implement an interface between the (more or less) system independent
+ * bits of NTP and the peculiarities of dealing with the Unix system
+ * clock. These routines will run with good precision fairly independently
+ * of your kernel's value of tickadj. I couldn't tell the difference
+ * between tickadj==40 and tickadj==5 on a microvax, though I prefer
+ * to set tickadj == 500/hz when in doubt. At your option you
+ * may compile this so that your system's clock is always slewed to the
+ * correct time even for large corrections. Of course, all of this takes
+ * a lot of code which wouldn't be needed with a reasonable tickadj and
+ * a willingness to let the clock be stepped occasionally. Oh well.
+ */
+
+/*
+ * Clock variables. We round calls to adjtime() to adj_precision
+ * microseconds, and limit the adjustment to tvu_maxslew microseconds
+ * (tsf_maxslew fractional sec) in one adjustment interval. As we are
+ * thus limited in the speed and precision with which we can adjust the
+ * clock, we compensate by keeping the known "error" in the system time
+ * in sys_clock_offset. This is added to timestamps returned by get_systime().
+ * We also remember the clock precision we computed from the kernel in
+ * case someone asks us.
+ */
+ long sys_clock;
+
+ long adj_precision; /* adj precision in usec (tickadj) */
+ long tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */
+
+ u_long tsf_maxslew; /* same as above, as long format */
+
+ l_fp sys_clock_offset; /* correction for current system time */
+
+/*
+ * get_systime - return the system time in timestamp format
+ * As a side effect, update sys_clock.
+ */
+void
+get_systime(ts)
+ l_fp *ts;
+{
+ struct timeval tv;
+
+#if !defined(SLEWALWAYS)
+ /*
+ * Quickly get the time of day and convert it
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)0);
+ TVTOTS(&tv, ts);
+ ts->l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+#else
+ /*
+ * Get the time of day, convert to time stamp format
+ * and add in the current time offset. Then round
+ * appropriately.
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)0);
+ TVTOTS(&tv, ts);
+ L_ADD(ts, &sys_clock_offset);
+ if (ts->l_uf & TS_ROUNDBIT)
+ L_ADDUF(ts, TS_ROUNDBIT);
+#endif /* !defined(SLEWALWAYS) */
+ ts->l_ui += JAN_1970;
+ ts->l_uf &= TS_MASK;
+
+ sys_clock = ts->l_ui;
+}
+
+/*
+ * step_systime - do a step adjustment in the system time (at least from
+ * NTP's point of view.
+ */
+int
+step_systime(ts)
+ l_fp *ts;
+{
+#ifdef SLEWALWAYS
+#ifdef STEP_SLEW
+ register u_long tmp_ui;
+ register u_long tmp_uf;
+ int isneg;
+ int n;
+
+ /*
+ * Take the absolute value of the offset
+ */
+ tmp_ui = ts->l_ui;
+ tmp_uf = ts->l_uf;
+ if (M_ISNEG(tmp_ui, tmp_uf)) {
+ M_NEG(tmp_ui, tmp_uf);
+ isneg = 1;
+ } else
+ isneg = 0;
+
+ if (tmp_ui >= 3) { /* Step it and slew we might win */
+ n = step_systime_real(ts);
+ if (!n) return n;
+ if (isneg)
+ ts->l_ui = ~0;
+ else
+ ts->l_ui = ~0;
+ }
+#endif
+ /*
+ * Just add adjustment into the current offset. The update
+ * routine will take care of bringing the system clock into
+ * line.
+ */
+ L_ADD(&sys_clock_offset, ts);
+ return 1;
+#else /* SLEWALWAYS */
+ return step_systime_real(ts);
+#endif /* SLEWALWAYS */
+}
+
+int max_no_complete = 20;
+
+/*
+ * adj_systime - called once every 1<<CLOCK_ADJ seconds to make system time
+ * adjustments.
+ */
+int
+adj_systime(ts)
+ l_fp *ts;
+{
+ register u_long offset_i, offset_f;
+ register long temp;
+ register u_long residual;
+ register int isneg = 0;
+ struct timeval adjtv, oadjtv;
+ l_fp oadjts;
+ long adj = ts->l_f;
+ int rval;
+
+ adjtv.tv_sec = adjtv.tv_usec = 0;
+
+ /*
+ * Move the current offset into the registers
+ */
+ offset_i = sys_clock_offset.l_ui;
+ offset_f = sys_clock_offset.l_uf;
+
+ /*
+ * Add the new adjustment into the system offset. Adjust the
+ * system clock to minimize this.
+ */
+ M_ADDF(offset_i, offset_f, adj);
+ if (M_ISNEG(offset_i, offset_f)) {
+ isneg = 1;
+ M_NEG(offset_i, offset_f);
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ syslog(LOG_DEBUG, "adj_systime(%s): offset = %s%s\n",
+ mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
+ umfptoa(offset_i, offset_f, 9));
+#endif
+
+ adjtv.tv_sec = 0;
+ if (offset_i > 0 || offset_f >= tsf_maxslew) {
+ /*
+ * Slew is bigger than we can complete in
+ * the adjustment interval. Make a maximum
+ * sized slew and reduce sys_clock_offset by this
+ * much.
+ */
+ M_SUBUF(offset_i, offset_f, tsf_maxslew);
+ if (!isneg) {
+ adjtv.tv_usec = tvu_maxslew;
+ } else {
+ adjtv.tv_usec = -tvu_maxslew;
+ M_NEG(offset_i, offset_f);
+ }
+
+#ifdef DEBUG
+ if (debug > 4)
+ printf("systime: maximum slew: %s%s, remainder = %s\n",
+ isneg?"-":"", umfptoa(0, tsf_maxslew, 9),
+ mfptoa(offset_i, offset_f, 9));
+#endif
+ } else {
+ /*
+ * We can do this slew in the time period. Do our
+ * best approximation (rounded), save residual for
+ * next adjustment.
+ *
+ * Note that offset_i is guaranteed to be 0 here.
+ */
+ TSFTOTVU(offset_f, temp);
+#ifndef ADJTIME_IS_ACCURATE
+ /*
+ * Round value to be an even multiple of adj_precision
+ */
+ residual = temp % adj_precision;
+ temp -= residual;
+ if (residual << 1 >= adj_precision)
+ temp += adj_precision;
+#endif /* ADJTIME_IS_ACCURATE */
+ TVUTOTSF(temp, residual);
+ M_SUBUF(offset_i, offset_f, residual);
+ if (isneg) {
+ adjtv.tv_usec = -temp;
+ M_NEG(offset_i, offset_f);
+ } else {
+ adjtv.tv_usec = temp;
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ printf(
+ "systime: adjtv = %s, adjts = %s, sys_clock_offset = %s\n",
+ tvtoa(&adjtv), umfptoa(0, residual, 9),
+ mfptoa(offset_i, offset_f, 9));
+#endif
+ }
+
+ if (adjtime(&adjtv, &oadjtv) < 0) {
+ syslog(LOG_ERR, "Can't do time adjustment: %m");
+ rval = 0;
+ } else {
+ sys_clock_offset.l_ui = offset_i;
+ sys_clock_offset.l_uf = offset_f;
+ rval = 1;
+
+#ifdef DEBUGRS6000
+ syslog(LOG_ERR, "adj_systime(%s): offset = %s%s\n",
+ mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
+ umfptoa(offset_i, offset_f, 9));
+ syslog(LOG_ERR, "%d %d %d %d\n", (int) adjtv.tv_sec,
+ (int) adjtv.tv_usec, (int) oadjtv.tv_sec, (int)
+ oadjtv.tv_usec);
+#endif /* DEBUGRS6000 */
+
+ if (oadjtv.tv_sec != 0 || oadjtv.tv_usec != 0) {
+ sTVTOTS(&oadjtv, &oadjts);
+ L_ADD(&sys_clock_offset, &oadjts);
+ if (max_no_complete > 0) {
+ syslog(LOG_WARNING,
+ "Previous time adjustment didn't complete");
+#ifdef DEBUG
+ if (debug > 4)
+ syslog(LOG_DEBUG,
+ "Previous adjtime() incomplete, residual = %s\n",
+ tvtoa(&oadjtv));
+#endif
+ if (--max_no_complete == 0)
+ syslog(LOG_WARNING,
+ "*** No more 'Prev time adj didn't complete'");
+ }
+ }
+ }
+ return(rval);
+}
+
+
+/*
+ * This is used by ntpdate even when xntpd does not use it! WLJ
+ */
+int
+step_systime_real(ts)
+ l_fp *ts;
+{
+ struct timeval timetv, adjtv;
+ int isneg = 0;
+#if defined(SYS_HPUX)
+ struct utmp ut;
+ time_t oldtime;
+#endif
+
+ /*
+ * We can afford to be sloppy here since if this is called
+ * the time is really screwed and everything is being reset.
+ */
+ L_ADD(&sys_clock_offset, ts);
+
+ if (L_ISNEG(&sys_clock_offset)) {
+ isneg = 1;
+ L_NEG(&sys_clock_offset);
+ }
+ TSTOTV(&sys_clock_offset, &adjtv);
+
+ (void) GETTIMEOFDAY(&timetv, (struct timezone *)0);
+#if defined(SYS_HPUX)
+ oldtime = timetv.tv_sec;
+#endif
+#ifdef DEBUG
+ if (debug > 3)
+ syslog(LOG_DEBUG, "step: %s, sys_clock_offset = %s, adjtv = %s, timetv = %s\n",
+ lfptoa(ts, 9), lfptoa(&sys_clock_offset, 9), tvtoa(&adjtv),
+ utvtoa(&timetv));
+#endif
+ if (isneg) {
+ timetv.tv_sec -= adjtv.tv_sec;
+ timetv.tv_usec -= adjtv.tv_usec;
+ if (timetv.tv_usec < 0) {
+ timetv.tv_sec--;
+ timetv.tv_usec += 1000000;
+ }
+ } else {
+ timetv.tv_sec += adjtv.tv_sec;
+ timetv.tv_usec += adjtv.tv_usec;
+ if (timetv.tv_usec >= 1000000) {
+ timetv.tv_sec++;
+ timetv.tv_usec -= 1000000;
+ }
+ }
+ if (SETTIMEOFDAY(&timetv, (struct timezone *)0) != 0) {
+ syslog(LOG_ERR, "Can't set time of day: %m");
+ return 0;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ syslog(LOG_DEBUG, "step: new timetv = %s\n", utvtoa(&timetv));
+#endif
+ sys_clock_offset.l_ui = sys_clock_offset.l_uf = 0;
+#if defined(SYS_HPUX)
+#if (SYS_HPUX < 10)
+ /*
+ * CHECKME: is this correct when called by ntpdate?????
+ */
+ _clear_adjtime();
+#endif
+ /*
+ * Write old and new time entries in utmp and wtmp if step adjustment
+ * is greater than one second.
+ */
+ if (oldtime != timetv.tv_sec) {
+ memset((char *)&ut, 0, sizeof(ut));
+ ut.ut_type = OLD_TIME;
+ ut.ut_time = oldtime;
+ (void)strcpy(ut.ut_line, OTIME_MSG);
+ pututline(&ut);
+ setutent();
+ ut.ut_type = NEW_TIME;
+ ut.ut_time = timetv.tv_sec;
+ (void)strcpy(ut.ut_line, NTIME_MSG);
+ pututline(&ut);
+ utmpname(WTMP_FILE);
+ ut.ut_type = OLD_TIME;
+ ut.ut_time = oldtime;
+ (void)strcpy(ut.ut_line, OTIME_MSG);
+ pututline(&ut);
+ ut.ut_type = NEW_TIME;
+ ut.ut_time = timetv.tv_sec;
+ (void)strcpy(ut.ut_line, NTIME_MSG);
+ pututline(&ut);
+ endutent();
+ }
+#endif
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/tsftomsu.c b/usr.sbin/xntpd/lib/tsftomsu.c
new file mode 100644
index 0000000..9904b4f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tsftomsu.c
@@ -0,0 +1,37 @@
+/*
+ * tsftomsu - convert from a time stamp fraction to milliseconds
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+int
+tsftomsu(tsf, round)
+ u_long tsf;
+ int round;
+{
+ register long val_ui, val_uf;
+ register long tmp_ui, tmp_uf;
+ register int i;
+
+ /*
+ * Essentially, multiply by 10 three times in l_fp form.
+ * The integral part is the milliseconds.
+ */
+ val_ui = 0;
+ val_uf = tsf;
+ for (i = 3; i > 0; i--) {
+ M_LSHIFT(val_ui, val_uf);
+ tmp_ui = val_ui;
+ tmp_uf = val_uf;
+ M_LSHIFT(val_ui, val_uf);
+ M_LSHIFT(val_ui, val_uf);
+ M_ADD(val_ui, val_uf, tmp_ui, tmp_uf);
+ }
+
+ /*
+ * Round the value if need be, then return it.
+ */
+ if (round && (val_uf & 0x80000000))
+ val_ui++;
+ return (int)val_ui;
+}
diff --git a/usr.sbin/xntpd/lib/tstotod.c b/usr.sbin/xntpd/lib/tstotod.c
new file mode 100644
index 0000000..a78aea1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tstotod.c
@@ -0,0 +1,21 @@
+#ifdef ELIMINATE
+/* tstotod.c,v 3.1 1993/07/06 01:08:48 jbj Exp
+ * tstotod - compute calendar time given an NTP timestamp
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+void
+tstotod(ts, tod)
+ l_fp *ts;
+ struct calendar *tod;
+{
+ register U_LONG cyclesecs;
+
+ cyclesecs = ts.l_ui - MAR_1900; /* bump forward to March 1900 */
+
+}
+#endif /* ELIMINATE */
diff --git a/usr.sbin/xntpd/lib/tstotv.c b/usr.sbin/xntpd/lib/tstotv.c
new file mode 100644
index 0000000..be4bdd4
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tstotv.c
@@ -0,0 +1,135 @@
+/*
+ * tstotv - tables for converting from NTP time stamps to struct timeval
+ */
+
+#include "ntp_types.h"
+
+/*
+ * Tables to convert from a time stamp fraction to usecs. Note that
+ * the units of these tables are actually (usec<<3). We carry three
+ * guard bits so that the result can be properly truncated (or rounded)
+ * to be correct to the least significant bit.
+ *
+ * These tables are rounded.
+ */
+
+long tstoushi[256] = {
+ 0x000000, 0x007a12, 0x00f424, 0x016e36,
+ 0x01e848, 0x02625a, 0x02dc6c, 0x03567e,
+ 0x03d090, 0x044aa2, 0x04c4b4, 0x053ec6,
+ 0x05b8d8, 0x0632ea, 0x06acfc, 0x07270e,
+ 0x07a120, 0x081b32, 0x089544, 0x090f56,
+ 0x098968, 0x0a037a, 0x0a7d8c, 0x0af79e,
+ 0x0b71b0, 0x0bebc2, 0x0c65d4, 0x0cdfe6,
+ 0x0d59f8, 0x0dd40a, 0x0e4e1c, 0x0ec82e,
+ 0x0f4240, 0x0fbc52, 0x103664, 0x10b076,
+ 0x112a88, 0x11a49a, 0x121eac, 0x1298be,
+ 0x1312d0, 0x138ce2, 0x1406f4, 0x148106,
+ 0x14fb18, 0x15752a, 0x15ef3c, 0x16694e,
+ 0x16e360, 0x175d72, 0x17d784, 0x185196,
+ 0x18cba8, 0x1945ba, 0x19bfcc, 0x1a39de,
+ 0x1ab3f0, 0x1b2e02, 0x1ba814, 0x1c2226,
+ 0x1c9c38, 0x1d164a, 0x1d905c, 0x1e0a6e,
+ 0x1e8480, 0x1efe92, 0x1f78a4, 0x1ff2b6,
+ 0x206cc8, 0x20e6da, 0x2160ec, 0x21dafe,
+ 0x225510, 0x22cf22, 0x234934, 0x23c346,
+ 0x243d58, 0x24b76a, 0x25317c, 0x25ab8e,
+ 0x2625a0, 0x269fb2, 0x2719c4, 0x2793d6,
+ 0x280de8, 0x2887fa, 0x29020c, 0x297c1e,
+ 0x29f630, 0x2a7042, 0x2aea54, 0x2b6466,
+ 0x2bde78, 0x2c588a, 0x2cd29c, 0x2d4cae,
+ 0x2dc6c0, 0x2e40d2, 0x2ebae4, 0x2f34f6,
+ 0x2faf08, 0x30291a, 0x30a32c, 0x311d3e,
+ 0x319750, 0x321162, 0x328b74, 0x330586,
+ 0x337f98, 0x33f9aa, 0x3473bc, 0x34edce,
+ 0x3567e0, 0x35e1f2, 0x365c04, 0x36d616,
+ 0x375028, 0x37ca3a, 0x38444c, 0x38be5e,
+ 0x393870, 0x39b282, 0x3a2c94, 0x3aa6a6,
+ 0x3b20b8, 0x3b9aca, 0x3c14dc, 0x3c8eee,
+ 0x3d0900, 0x3d8312, 0x3dfd24, 0x3e7736,
+ 0x3ef148, 0x3f6b5a, 0x3fe56c, 0x405f7e,
+ 0x40d990, 0x4153a2, 0x41cdb4, 0x4247c6,
+ 0x42c1d8, 0x433bea, 0x43b5fc, 0x44300e,
+ 0x44aa20, 0x452432, 0x459e44, 0x461856,
+ 0x469268, 0x470c7a, 0x47868c, 0x48009e,
+ 0x487ab0, 0x48f4c2, 0x496ed4, 0x49e8e6,
+ 0x4a62f8, 0x4add0a, 0x4b571c, 0x4bd12e,
+ 0x4c4b40, 0x4cc552, 0x4d3f64, 0x4db976,
+ 0x4e3388, 0x4ead9a, 0x4f27ac, 0x4fa1be,
+ 0x501bd0, 0x5095e2, 0x510ff4, 0x518a06,
+ 0x520418, 0x527e2a, 0x52f83c, 0x53724e,
+ 0x53ec60, 0x546672, 0x54e084, 0x555a96,
+ 0x55d4a8, 0x564eba, 0x56c8cc, 0x5742de,
+ 0x57bcf0, 0x583702, 0x58b114, 0x592b26,
+ 0x59a538, 0x5a1f4a, 0x5a995c, 0x5b136e,
+ 0x5b8d80, 0x5c0792, 0x5c81a4, 0x5cfbb6,
+ 0x5d75c8, 0x5defda, 0x5e69ec, 0x5ee3fe,
+ 0x5f5e10, 0x5fd822, 0x605234, 0x60cc46,
+ 0x614658, 0x61c06a, 0x623a7c, 0x62b48e,
+ 0x632ea0, 0x63a8b2, 0x6422c4, 0x649cd6,
+ 0x6516e8, 0x6590fa, 0x660b0c, 0x66851e,
+ 0x66ff30, 0x677942, 0x67f354, 0x686d66,
+ 0x68e778, 0x69618a, 0x69db9c, 0x6a55ae,
+ 0x6acfc0, 0x6b49d2, 0x6bc3e4, 0x6c3df6,
+ 0x6cb808, 0x6d321a, 0x6dac2c, 0x6e263e,
+ 0x6ea050, 0x6f1a62, 0x6f9474, 0x700e86,
+ 0x708898, 0x7102aa, 0x717cbc, 0x71f6ce,
+ 0x7270e0, 0x72eaf2, 0x736504, 0x73df16,
+ 0x745928, 0x74d33a, 0x754d4c, 0x75c75e,
+ 0x764170, 0x76bb82, 0x773594, 0x77afa6,
+ 0x7829b8, 0x78a3ca, 0x791ddc, 0x7997ee
+};
+
+long tstousmid[256] = {
+ 0x0000, 0x007a, 0x00f4, 0x016e, 0x01e8, 0x0262, 0x02dc, 0x0356,
+ 0x03d1, 0x044b, 0x04c5, 0x053f, 0x05b9, 0x0633, 0x06ad, 0x0727,
+ 0x07a1, 0x081b, 0x0895, 0x090f, 0x0989, 0x0a03, 0x0a7e, 0x0af8,
+ 0x0b72, 0x0bec, 0x0c66, 0x0ce0, 0x0d5a, 0x0dd4, 0x0e4e, 0x0ec8,
+ 0x0f42, 0x0fbc, 0x1036, 0x10b0, 0x112b, 0x11a5, 0x121f, 0x1299,
+ 0x1313, 0x138d, 0x1407, 0x1481, 0x14fb, 0x1575, 0x15ef, 0x1669,
+ 0x16e3, 0x175d, 0x17d8, 0x1852, 0x18cc, 0x1946, 0x19c0, 0x1a3a,
+ 0x1ab4, 0x1b2e, 0x1ba8, 0x1c22, 0x1c9c, 0x1d16, 0x1d90, 0x1e0a,
+ 0x1e84, 0x1eff, 0x1f79, 0x1ff3, 0x206d, 0x20e7, 0x2161, 0x21db,
+ 0x2255, 0x22cf, 0x2349, 0x23c3, 0x243d, 0x24b7, 0x2531, 0x25ac,
+ 0x2626, 0x26a0, 0x271a, 0x2794, 0x280e, 0x2888, 0x2902, 0x297c,
+ 0x29f6, 0x2a70, 0x2aea, 0x2b64, 0x2bde, 0x2c59, 0x2cd3, 0x2d4d,
+ 0x2dc7, 0x2e41, 0x2ebb, 0x2f35, 0x2faf, 0x3029, 0x30a3, 0x311d,
+ 0x3197, 0x3211, 0x328b, 0x3306, 0x3380, 0x33fa, 0x3474, 0x34ee,
+ 0x3568, 0x35e2, 0x365c, 0x36d6, 0x3750, 0x37ca, 0x3844, 0x38be,
+ 0x3938, 0x39b3, 0x3a2d, 0x3aa7, 0x3b21, 0x3b9b, 0x3c15, 0x3c8f,
+ 0x3d09, 0x3d83, 0x3dfd, 0x3e77, 0x3ef1, 0x3f6b, 0x3fe5, 0x405f,
+ 0x40da, 0x4154, 0x41ce, 0x4248, 0x42c2, 0x433c, 0x43b6, 0x4430,
+ 0x44aa, 0x4524, 0x459e, 0x4618, 0x4692, 0x470c, 0x4787, 0x4801,
+ 0x487b, 0x48f5, 0x496f, 0x49e9, 0x4a63, 0x4add, 0x4b57, 0x4bd1,
+ 0x4c4b, 0x4cc5, 0x4d3f, 0x4db9, 0x4e34, 0x4eae, 0x4f28, 0x4fa2,
+ 0x501c, 0x5096, 0x5110, 0x518a, 0x5204, 0x527e, 0x52f8, 0x5372,
+ 0x53ec, 0x5466, 0x54e1, 0x555b, 0x55d5, 0x564f, 0x56c9, 0x5743,
+ 0x57bd, 0x5837, 0x58b1, 0x592b, 0x59a5, 0x5a1f, 0x5a99, 0x5b13,
+ 0x5b8d, 0x5c08, 0x5c82, 0x5cfc, 0x5d76, 0x5df0, 0x5e6a, 0x5ee4,
+ 0x5f5e, 0x5fd8, 0x6052, 0x60cc, 0x6146, 0x61c0, 0x623a, 0x62b5,
+ 0x632f, 0x63a9, 0x6423, 0x649d, 0x6517, 0x6591, 0x660b, 0x6685,
+ 0x66ff, 0x6779, 0x67f3, 0x686d, 0x68e7, 0x6962, 0x69dc, 0x6a56,
+ 0x6ad0, 0x6b4a, 0x6bc4, 0x6c3e, 0x6cb8, 0x6d32, 0x6dac, 0x6e26,
+ 0x6ea0, 0x6f1a, 0x6f94, 0x700f, 0x7089, 0x7103, 0x717d, 0x71f7,
+ 0x7271, 0x72eb, 0x7365, 0x73df, 0x7459, 0x74d3, 0x754d, 0x75c7,
+ 0x7641, 0x76bc, 0x7736, 0x77b0, 0x782a, 0x78a4, 0x791e, 0x7998
+};
+
+long tstouslo[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x33, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
+ 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79
+};
diff --git a/usr.sbin/xntpd/lib/tvtoa.c b/usr.sbin/xntpd/lib/tvtoa.c
new file mode 100644
index 0000000..dd9ee7e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tvtoa.c
@@ -0,0 +1,33 @@
+/*
+ * tvtoa - return an asciized representation of a struct timeval
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+tvtoa(tv)
+ struct timeval *tv;
+{
+ register char *buf;
+ register u_long sec;
+ register u_long usec;
+ register int isneg;
+
+ if (tv->tv_sec < 0 || tv->tv_usec < 0) {
+ sec = -tv->tv_sec;
+ usec = -tv->tv_usec;
+ isneg = 1;
+ } else {
+ sec = tv->tv_sec;
+ usec = tv->tv_usec;
+ isneg = 0;
+ }
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%s%lu.%06lu", (isneg?"-":""), sec, usec);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/tvtots.c b/usr.sbin/xntpd/lib/tvtots.c
new file mode 100644
index 0000000..0bd2b69
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tvtots.c
@@ -0,0 +1,159 @@
+/*
+ * tvtots - tables for converting from Unix struct timeval's to
+ * NTP time stamp format.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+
+/*
+ * Tables to calculate time stamp fractions from usecs. The entries
+ * in these tables are offset into using each of the two low order
+ * bytes plus the next 4 bits in a usec value (from a struct timeval).
+ * These are summed to produce the time stamp fraction.
+ *
+ * Note that these tables are rounded (not truncated) to the nearest
+ * low order bit in the fraction. The timestamp computed should be
+ * +- 1.5 low order bits.
+ */
+
+u_long ustotslo[256] = {
+ 0x00000000, 0x000010c7, 0x0000218e, 0x00003255,
+ 0x0000431c, 0x000053e3, 0x000064aa, 0x00007571,
+ 0x00008638, 0x000096ff, 0x0000a7c6, 0x0000b88d,
+ 0x0000c954, 0x0000da1b, 0x0000eae2, 0x0000fba9,
+ 0x00010c6f, 0x00011d36, 0x00012dfd, 0x00013ec4,
+ 0x00014f8b, 0x00016052, 0x00017119, 0x000181e0,
+ 0x000192a7, 0x0001a36e, 0x0001b435, 0x0001c4fc,
+ 0x0001d5c3, 0x0001e68a, 0x0001f751, 0x00020818,
+ 0x000218df, 0x000229a6, 0x00023a6d, 0x00024b34,
+ 0x00025bfb, 0x00026cc2, 0x00027d89, 0x00028e50,
+ 0x00029f17, 0x0002afde, 0x0002c0a5, 0x0002d16c,
+ 0x0002e233, 0x0002f2fa, 0x000303c0, 0x00031487,
+ 0x0003254e, 0x00033615, 0x000346dc, 0x000357a3,
+ 0x0003686a, 0x00037931, 0x000389f8, 0x00039abf,
+ 0x0003ab86, 0x0003bc4d, 0x0003cd14, 0x0003dddb,
+ 0x0003eea2, 0x0003ff69, 0x00041030, 0x000420f7,
+ 0x000431be, 0x00044285, 0x0004534c, 0x00046413,
+ 0x000474da, 0x000485a1, 0x00049668, 0x0004a72f,
+ 0x0004b7f6, 0x0004c8bd, 0x0004d984, 0x0004ea4b,
+ 0x0004fb12, 0x00050bd8, 0x00051c9f, 0x00052d66,
+ 0x00053e2d, 0x00054ef4, 0x00055fbb, 0x00057082,
+ 0x00058149, 0x00059210, 0x0005a2d7, 0x0005b39e,
+ 0x0005c465, 0x0005d52c, 0x0005e5f3, 0x0005f6ba,
+ 0x00060781, 0x00061848, 0x0006290f, 0x000639d6,
+ 0x00064a9d, 0x00065b64, 0x00066c2b, 0x00067cf2,
+ 0x00068db9, 0x00069e80, 0x0006af47, 0x0006c00e,
+ 0x0006d0d5, 0x0006e19c, 0x0006f263, 0x00070329,
+ 0x000713f0, 0x000724b7, 0x0007357e, 0x00074645,
+ 0x0007570c, 0x000767d3, 0x0007789a, 0x00078961,
+ 0x00079a28, 0x0007aaef, 0x0007bbb6, 0x0007cc7d,
+ 0x0007dd44, 0x0007ee0b, 0x0007fed2, 0x00080f99,
+ 0x00082060, 0x00083127, 0x000841ee, 0x000852b5,
+ 0x0008637c, 0x00087443, 0x0008850a, 0x000895d1,
+ 0x0008a698, 0x0008b75f, 0x0008c826, 0x0008d8ed,
+ 0x0008e9b4, 0x0008fa7b, 0x00090b41, 0x00091c08,
+ 0x00092ccf, 0x00093d96, 0x00094e5d, 0x00095f24,
+ 0x00096feb, 0x000980b2, 0x00099179, 0x0009a240,
+ 0x0009b307, 0x0009c3ce, 0x0009d495, 0x0009e55c,
+ 0x0009f623, 0x000a06ea, 0x000a17b1, 0x000a2878,
+ 0x000a393f, 0x000a4a06, 0x000a5acd, 0x000a6b94,
+ 0x000a7c5b, 0x000a8d22, 0x000a9de9, 0x000aaeb0,
+ 0x000abf77, 0x000ad03e, 0x000ae105, 0x000af1cc,
+ 0x000b0292, 0x000b1359, 0x000b2420, 0x000b34e7,
+ 0x000b45ae, 0x000b5675, 0x000b673c, 0x000b7803,
+ 0x000b88ca, 0x000b9991, 0x000baa58, 0x000bbb1f,
+ 0x000bcbe6, 0x000bdcad, 0x000bed74, 0x000bfe3b,
+ 0x000c0f02, 0x000c1fc9, 0x000c3090, 0x000c4157,
+ 0x000c521e, 0x000c62e5, 0x000c73ac, 0x000c8473,
+ 0x000c953a, 0x000ca601, 0x000cb6c8, 0x000cc78f,
+ 0x000cd856, 0x000ce91d, 0x000cf9e4, 0x000d0aaa,
+ 0x000d1b71, 0x000d2c38, 0x000d3cff, 0x000d4dc6,
+ 0x000d5e8d, 0x000d6f54, 0x000d801b, 0x000d90e2,
+ 0x000da1a9, 0x000db270, 0x000dc337, 0x000dd3fe,
+ 0x000de4c5, 0x000df58c, 0x000e0653, 0x000e171a,
+ 0x000e27e1, 0x000e38a8, 0x000e496f, 0x000e5a36,
+ 0x000e6afd, 0x000e7bc4, 0x000e8c8b, 0x000e9d52,
+ 0x000eae19, 0x000ebee0, 0x000ecfa7, 0x000ee06e,
+ 0x000ef135, 0x000f01fb, 0x000f12c2, 0x000f2389,
+ 0x000f3450, 0x000f4517, 0x000f55de, 0x000f66a5,
+ 0x000f776c, 0x000f8833, 0x000f98fa, 0x000fa9c1,
+ 0x000fba88, 0x000fcb4f, 0x000fdc16, 0x000fecdd,
+ 0x000ffda4, 0x00100e6b, 0x00101f32, 0x00102ff9,
+ 0x001040c0, 0x00105187, 0x0010624e, 0x00107315,
+ 0x001083dc, 0x001094a3, 0x0010a56a, 0x0010b631,
+};
+
+u_long ustotsmid[256] = {
+ 0x00000000, 0x0010c6f8, 0x00218def, 0x003254e7,
+ 0x00431bde, 0x0053e2d6, 0x0064a9ce, 0x007570c5,
+ 0x008637bd, 0x0096feb4, 0x00a7c5ac, 0x00b88ca4,
+ 0x00c9539b, 0x00da1a93, 0x00eae18a, 0x00fba882,
+ 0x010c6f7a, 0x011d3671, 0x012dfd69, 0x013ec460,
+ 0x014f8b58, 0x01605250, 0x01711947, 0x0181e03f,
+ 0x0192a736, 0x01a36e2e, 0x01b43526, 0x01c4fc1d,
+ 0x01d5c315, 0x01e68a0c, 0x01f75104, 0x020817fc,
+ 0x0218def3, 0x0229a5eb, 0x023a6ce3, 0x024b33da,
+ 0x025bfad2, 0x026cc1c9, 0x027d88c1, 0x028e4fb9,
+ 0x029f16b0, 0x02afdda8, 0x02c0a49f, 0x02d16b97,
+ 0x02e2328f, 0x02f2f986, 0x0303c07e, 0x03148775,
+ 0x03254e6d, 0x03361565, 0x0346dc5c, 0x0357a354,
+ 0x03686a4b, 0x03793143, 0x0389f83b, 0x039abf32,
+ 0x03ab862a, 0x03bc4d21, 0x03cd1419, 0x03dddb11,
+ 0x03eea208, 0x03ff6900, 0x04102ff7, 0x0420f6ef,
+ 0x0431bde7, 0x044284de, 0x04534bd6, 0x046412cd,
+ 0x0474d9c5, 0x0485a0bd, 0x049667b4, 0x04a72eac,
+ 0x04b7f5a3, 0x04c8bc9b, 0x04d98393, 0x04ea4a8a,
+ 0x04fb1182, 0x050bd879, 0x051c9f71, 0x052d6669,
+ 0x053e2d60, 0x054ef458, 0x055fbb4f, 0x05708247,
+ 0x0581493f, 0x05921036, 0x05a2d72e, 0x05b39e25,
+ 0x05c4651d, 0x05d52c15, 0x05e5f30c, 0x05f6ba04,
+ 0x060780fb, 0x061847f3, 0x06290eeb, 0x0639d5e2,
+ 0x064a9cda, 0x065b63d2, 0x066c2ac9, 0x067cf1c1,
+ 0x068db8b8, 0x069e7fb0, 0x06af46a8, 0x06c00d9f,
+ 0x06d0d497, 0x06e19b8e, 0x06f26286, 0x0703297e,
+ 0x0713f075, 0x0724b76d, 0x07357e64, 0x0746455c,
+ 0x07570c54, 0x0767d34b, 0x07789a43, 0x0789613a,
+ 0x079a2832, 0x07aaef2a, 0x07bbb621, 0x07cc7d19,
+ 0x07dd4410, 0x07ee0b08, 0x07fed200, 0x080f98f7,
+ 0x08205fef, 0x083126e6, 0x0841edde, 0x0852b4d6,
+ 0x08637bcd, 0x087442c5, 0x088509bc, 0x0895d0b4,
+ 0x08a697ac, 0x08b75ea3, 0x08c8259b, 0x08d8ec92,
+ 0x08e9b38a, 0x08fa7a82, 0x090b4179, 0x091c0871,
+ 0x092ccf68, 0x093d9660, 0x094e5d58, 0x095f244f,
+ 0x096feb47, 0x0980b23e, 0x09917936, 0x09a2402e,
+ 0x09b30725, 0x09c3ce1d, 0x09d49514, 0x09e55c0c,
+ 0x09f62304, 0x0a06e9fb, 0x0a17b0f3, 0x0a2877ea,
+ 0x0a393ee2, 0x0a4a05da, 0x0a5accd1, 0x0a6b93c9,
+ 0x0a7c5ac1, 0x0a8d21b8, 0x0a9de8b0, 0x0aaeafa7,
+ 0x0abf769f, 0x0ad03d97, 0x0ae1048e, 0x0af1cb86,
+ 0x0b02927d, 0x0b135975, 0x0b24206d, 0x0b34e764,
+ 0x0b45ae5c, 0x0b567553, 0x0b673c4b, 0x0b780343,
+ 0x0b88ca3a, 0x0b999132, 0x0baa5829, 0x0bbb1f21,
+ 0x0bcbe619, 0x0bdcad10, 0x0bed7408, 0x0bfe3aff,
+ 0x0c0f01f7, 0x0c1fc8ef, 0x0c308fe6, 0x0c4156de,
+ 0x0c521dd5, 0x0c62e4cd, 0x0c73abc5, 0x0c8472bc,
+ 0x0c9539b4, 0x0ca600ab, 0x0cb6c7a3, 0x0cc78e9b,
+ 0x0cd85592, 0x0ce91c8a, 0x0cf9e381, 0x0d0aaa79,
+ 0x0d1b7171, 0x0d2c3868, 0x0d3cff60, 0x0d4dc657,
+ 0x0d5e8d4f, 0x0d6f5447, 0x0d801b3e, 0x0d90e236,
+ 0x0da1a92d, 0x0db27025, 0x0dc3371d, 0x0dd3fe14,
+ 0x0de4c50c, 0x0df58c03, 0x0e0652fb, 0x0e1719f3,
+ 0x0e27e0ea, 0x0e38a7e2, 0x0e496ed9, 0x0e5a35d1,
+ 0x0e6afcc9, 0x0e7bc3c0, 0x0e8c8ab8, 0x0e9d51b0,
+ 0x0eae18a7, 0x0ebedf9f, 0x0ecfa696, 0x0ee06d8e,
+ 0x0ef13486, 0x0f01fb7d, 0x0f12c275, 0x0f23896c,
+ 0x0f345064, 0x0f45175c, 0x0f55de53, 0x0f66a54b,
+ 0x0f776c42, 0x0f88333a, 0x0f98fa32, 0x0fa9c129,
+ 0x0fba8821, 0x0fcb4f18, 0x0fdc1610, 0x0fecdd08,
+ 0x0ffda3ff, 0x100e6af7, 0x101f31ee, 0x102ff8e6,
+ 0x1040bfde, 0x105186d5, 0x10624dcd, 0x107314c4,
+ 0x1083dbbc, 0x1094a2b4, 0x10a569ab, 0x10b630a3,
+};
+
+u_long ustotshi[16] = {
+ 0x00000000, 0x10c6f79a, 0x218def35, 0x3254e6cf,
+ 0x431bde6a, 0x53e2d604, 0x64a9cd9f, 0x7570c539,
+ 0x8637bcd3, 0x96feb46e, 0xa7c5ac08, 0xb88ca3a3,
+ 0xc9539b3d, 0xda1a92d7, 0xeae18a72, 0xfba8820c,
+};
diff --git a/usr.sbin/xntpd/lib/uglydate.c b/usr.sbin/xntpd/lib/uglydate.c
new file mode 100644
index 0000000..25a8e56
--- /dev/null
+++ b/usr.sbin/xntpd/lib/uglydate.c
@@ -0,0 +1,49 @@
+/*
+ * uglydate - convert a time stamp to something barely readable
+ * The string returned is 37 characters long.
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+char *
+uglydate(ts)
+ l_fp *ts;
+{
+ char *bp;
+ char *timep;
+ struct tm *tm;
+ time_t sec;
+ long msec;
+ int year;
+
+ timep = ulfptoa(ts, 6); /* returns max 17 characters */
+ LIB_GETBUF(bp);
+ sec = ts->l_ui - JAN_1970;
+ msec = ts->l_uf / 4294967; /* fract / (2**32/1000) */
+ tm = gmtime(&sec);
+ if (ts->l_ui == 0) {
+ /*
+ * Probably not a real good thing to do. Oh, well.
+ */
+ year = 0;
+ tm->tm_yday = 0;
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ } else {
+ year = tm->tm_year;
+ while (year >= 100)
+ year -= 100;
+ }
+ (void) sprintf(bp, "%17s %02d:%03d:%02d:%02d:%02d.%03ld",
+ timep, year, tm->tm_yday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, msec);
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/uinttoa.c b/usr.sbin/xntpd/lib/uinttoa.c
new file mode 100644
index 0000000..7c406dd
--- /dev/null
+++ b/usr.sbin/xntpd/lib/uinttoa.c
@@ -0,0 +1,19 @@
+/*
+ * uinttoa - return an asciized unsigned integer
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+uinttoa(uval)
+ u_long uval;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%lu", (u_long)uval);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/utvtoa.c b/usr.sbin/xntpd/lib/utvtoa.c
new file mode 100644
index 0000000..5a192e1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/utvtoa.c
@@ -0,0 +1,21 @@
+/*
+ * utvtoa - return an asciized representation of an unsigned struct timeval
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+utvtoa(tv)
+ struct timeval *tv;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%lu.%06lu", (u_long)tv->tv_sec,
+ (u_long)tv->tv_usec);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/ntpdate/Makefile b/usr.sbin/xntpd/ntpdate/Makefile
new file mode 100644
index 0000000..3b388a2
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id: Makefile,v 1.9 1998/03/07 09:46:05 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= ntpdate
+MAN8= ${.CURDIR}/../doc/ntpdate.8
+CLEANFILES+= .version version.c
+
+SRCS= ntpdate.c version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntpdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntpdate/README b/usr.sbin/xntpd/ntpdate/README
new file mode 100644
index 0000000..fd2dbe2
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/README
@@ -0,0 +1,7 @@
+README file for directory ./ntpdate of the NTP Version 3 distribution
+
+This directory contains the sources for the ntpdate utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
+
diff --git a/usr.sbin/xntpd/ntpdate/ntpdate.c b/usr.sbin/xntpd/ntpdate/ntpdate.c
new file mode 100644
index 0000000..bb5ede5
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/ntpdate.c
@@ -0,0 +1,1586 @@
+/*
+ * ntpdate - set the time of day by polling one or more NTP servers
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(SYS_HPUX)
+#include <utmp.h>
+#endif
+
+#ifdef SYS_LINUX
+#include <sys/timex.h>
+#endif
+
+#ifndef SYSLOG_FILE
+#define SYSLOG_FILE /* we want to go through the syslog/printf/file code */
+#endif
+
+#include "ntp_select.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntpdate.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPDATE_PRIO (-12)
+
+/*
+ * Compatibility stuff for Version 2
+ */
+#define NTP_MAXSKW 0x28f /* 0.01 sec in fp format */
+#define NTP_MINDIST 0x51f /* 0.02 sec in fp format */
+#define PEER_MAXDISP (64*FP_SECOND) /* maximum dispersion (fp 64) */
+#define NTP_INFIN 15 /* max stratum, infinity a la Bellman-Ford */
+#define NTP_MAXWGT (8*FP_SECOND) /* maximum select weight 8 seconds */
+#define NTP_MAXLIST 5 /* maximum select list size */
+#define PEER_SHIFT 8 /* 8 suitable for crystal time base */
+
+/*
+ * Debugging flag
+ */
+int debug = 0;
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+int fd;
+fd_set fdmask;
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing = 1;
+
+/*
+ * Alarm flag. Set when an alarm occurs
+ */
+int alarm_flag = 0;
+
+/*
+ * Simple query flag.
+ */
+int simple_query = 0;
+
+/*
+ * Program name.
+ */
+char *progname;
+
+/*
+ * Systemwide parameters and flags
+ */
+int sys_samples = DEFSAMPLES; /* number of samples/server */
+u_long sys_timeout = DEFTIMEOUT; /* timeout time, in TIMER_HZ units */
+struct server **sys_servers; /* the server list */
+int sys_numservers = 0; /* number of servers to poll */
+int sys_maxservers = 0; /* max number of servers to deal with */
+int sys_authenticate = 0; /* true when authenticating */
+u_long sys_authkey = 0; /* set to authentication key in use */
+u_long sys_authdelay = 0; /* authentication delay */
+int sys_version = NTP_VERSION; /* version to poll with */
+
+/*
+ * The current internal time
+ */
+u_long current_time = 0;
+
+/*
+ * Counter for keeping track of completed servers
+ */
+int complete_servers = 0;
+
+/*
+ * File of encryption keys
+ */
+#ifndef KEYFILE
+#define KEYFILE "/etc/ntp.keys"
+#endif /* KEYFILE */
+
+char *key_file = KEYFILE;
+
+/*
+ * Miscellaneous flags
+ */
+extern int syslogit;
+int verbose = 0;
+int always_step = 0;
+
+extern int errno;
+
+static void transmit P((struct server *));
+static void receive P((struct recvbuf *));
+static void server_data P((struct server *, s_fp, l_fp *, u_fp));
+static void clock_filter P((struct server *));
+static struct server *clock_select P((void));
+static int clock_adjust P((void));
+static void addserver P((char *));
+static struct server *findserver P((struct sockaddr_in *));
+static void timer P((void));
+static void init_alarm P((void));
+static RETSIGTYPE alarming P((int));
+static void init_io P((void));
+static struct recvbuf *getrecvbufs P((void));
+static void freerecvbuf P((struct recvbuf *));
+static void sendpkt P((struct sockaddr_in *, struct pkt *, int));
+static void input_handler P((void));
+
+static int l_adj_systime P((l_fp *));
+static int l_step_systime P((l_fp *));
+
+static int getnetnum P((char *, u_long *));
+static void printserver P((struct server *, FILE *));
+
+/*
+ * Main program. Initialize us and loop waiting for I/O and/or
+ * timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int was_alarmed;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+ l_fp tmp;
+ int errflg;
+ int c;
+ extern char *ntp_optarg;
+ extern int ntp_optind;
+ extern char *Version;
+
+ errflg = 0;
+ progname = argv[0];
+ syslogit = 0;
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, "a:bde:k:o:p:qst:v")) != EOF)
+ switch (c) {
+ case 'a':
+ c = atoi(ntp_optarg);
+ sys_authenticate = 1;
+ sys_authkey = c;
+ break;
+ case 'b':
+ always_step++;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'e':
+ if (!atolfp(ntp_optarg, &tmp)
+ || tmp.l_ui != 0) {
+ (void) fprintf(stderr,
+ "%s: encryption delay %s is unlikely\n",
+ progname, ntp_optarg);
+ errflg++;
+ } else {
+ sys_authdelay = tmp.l_uf;
+ }
+ break;
+ case 'k':
+ key_file = ntp_optarg;
+ break;
+ case 'o':
+ sys_version = atoi(ntp_optarg);
+ break;
+ case 'p':
+ c = atoi(ntp_optarg);
+ if (c <= 0 || c > NTP_SHIFT) {
+ (void) fprintf(stderr,
+ "%s: number of samples (%d) is invalid\n",
+ progname, c);
+ errflg++;
+ } else {
+ sys_samples = c;
+ }
+ break;
+ case 'q':
+ simple_query = 1;
+ break;
+ case 's':
+ syslogit = 1;
+ break;
+ case 't':
+ if (!atolfp(ntp_optarg, &tmp)) {
+ (void) fprintf(stderr,
+ "%s: timeout %s is undecodeable\n",
+ progname, ntp_optarg);
+ errflg++;
+ } else {
+ sys_timeout = ((LFPTOFP(&tmp) * TIMER_HZ)
+ + 0x8000) >> 16;
+ if (sys_timeout == 0)
+ sys_timeout = 1;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ sys_maxservers = argc - ntp_optind;
+ if (errflg || sys_maxservers == 0) {
+ (void) fprintf(stderr,
+"usage: %s [-bqs] [-a key#] [-k file] [-p samples] [-t timeo] server ...\n",
+ progname);
+ exit(2);
+ }
+
+ sys_servers = (struct server **)
+ emalloc(sys_maxservers * sizeof(struct server *));
+
+ if (debug || simple_query) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+ /*
+ * Logging. Open the syslog if we have to
+ */
+ if (syslogit) {
+#ifndef LOG_DAEMON
+ openlog("ntpdate", LOG_PID);
+#else
+
+#ifndef LOG_NTP
+#define LOG_NTP LOG_DAEMON
+#endif
+ openlog("ntpdate", LOG_PID | LOG_NDELAY, LOG_NTP);
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif /* LOG_DAEMON */
+ }
+
+ if (debug || verbose)
+ syslog(LOG_NOTICE, "%s", Version);
+
+ /*
+ * Add servers we are going to be polling
+ */
+ for ( ; ntp_optind < argc; ntp_optind++)
+ addserver(argv[ntp_optind]);
+
+ if (sys_numservers == 0) {
+ syslog(LOG_ERR, "no servers can be used, exiting");
+ exit(1);
+ }
+
+ /*
+ * Initialize the time of day routines and the I/O subsystem
+ */
+ if (sys_authenticate) {
+ init_auth();
+ if (!authreadkeys(key_file)) {
+ syslog(LOG_ERR, "no key file, exitting");
+ exit(1);
+ }
+ if (!authhavekey(sys_authkey)) {
+ char buf[10];
+
+ (void) sprintf(buf, "%lu", (unsigned long)sys_authkey);
+ syslog(LOG_ERR, "authentication key %s unknown", buf);
+ exit(1);
+ }
+ }
+ init_io();
+ init_alarm();
+
+ /*
+ * Set the priority.
+ */
+#if defined(HAVE_ATT_NICE)
+ nice (NTPDATE_PRIO);
+#endif
+#if defined(HAVE_BSD_NICE)
+ (void) setpriority(PRIO_PROCESS, 0, NTPDATE_PRIO);
+#endif
+
+ initializing = 0;
+
+ was_alarmed = 0;
+ rbuflist = (struct recvbuf *)0;
+ while (complete_servers < sys_numservers) {
+ fd_set rdfdes;
+ int nfound;
+
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+
+ if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+ rdfdes = fdmask;
+ nfound = select(fd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+ if (nfound > 0)
+ input_handler();
+
+ else if (nfound == -1 && errno != EINTR) {
+ syslog(LOG_ERR, "select() error: %m");
+ }
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+
+ }
+
+ /*
+ * Out here, signals are unblocked. Call receive
+ * procedure for each incoming packet.
+ */
+ while (rbuflist != (struct recvbuf *)0) {
+ rbuf = rbuflist;
+ rbuflist = rbuf->next;
+ receive(rbuf);
+ freerecvbuf(rbuf);
+ }
+
+ /*
+ * Call timer to process any timeouts
+ */
+ if (was_alarmed) {
+ timer();
+ was_alarmed = 0;
+ }
+
+ /*
+ * Go around again
+ */
+ }
+
+ /*
+ * When we get here we've completed the polling of all servers.
+ * Adjust the clock, then exit.
+ */
+ exit(clock_adjust());
+}
+
+
+/*
+ * transmit - transmit a packet to the given server, or mark it completed.
+ * This is called by the timeout routine and by the receive
+ * procedure.
+ */
+static void
+transmit(server)
+ register struct server *server;
+{
+ struct pkt xpkt;
+
+ if (debug)
+ printf("transmit(%s)\n", ntoa(&server->srcadr));
+
+ if (server->filter_nextpt < server->xmtcnt) {
+ l_fp ts;
+ /*
+ * Last message to this server timed out. Shift
+ * zeros into the filter.
+ */
+ L_CLR(&ts);
+ server_data(server, 0, &ts, 0);
+ }
+
+ if ((int)server->filter_nextpt >= sys_samples) {
+ /*
+ * Got all the data we need. Mark this guy
+ * completed and return.
+ */
+ server->event_time = 0;
+ complete_servers++;
+ return;
+ }
+
+ /*
+ * If we're here, send another message to the server. Fill in
+ * the packet and let 'er rip.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ sys_version, MODE_CLIENT);
+ xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
+ xpkt.ppoll = NTP_MINPOLL;
+ xpkt.precision = NTPDATE_PRECISION;
+ xpkt.rootdelay = htonl(NTPDATE_DISTANCE);
+ xpkt.rootdispersion = htonl(NTPDATE_DISP);
+ xpkt.refid = htonl(NTPDATE_REFID);
+ L_CLR(&xpkt.reftime);
+ L_CLR(&xpkt.org);
+ L_CLR(&xpkt.rec);
+
+ /*
+ * Determine whether to authenticate or not. If so,
+ * fill in the extended part of the packet and do it.
+ * If not, just timestamp it and send it away.
+ */
+ if (sys_authenticate) {
+ int len;
+
+ xpkt.keyid = htonl(sys_authkey);
+ auth1crypt(sys_authkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ get_systime(&server->xmt);
+ L_ADDUF(&server->xmt, sys_authdelay);
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ len = auth2crypt(sys_authkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC + len);
+
+ if (debug > 1)
+ printf("transmit auth to %s\n",
+ ntoa(&(server->srcadr)));
+ } else {
+ get_systime(&(server->xmt));
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC);
+
+ if (debug > 1)
+ printf("transmit to %s\n", ntoa(&(server->srcadr)));
+ }
+
+ /*
+ * Update the server timeout and transmit count
+ */
+ server->event_time = current_time + sys_timeout;
+ server->xmtcnt++;
+}
+
+
+/*
+ * receive - receive and process an incoming frame
+ */
+static void
+receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pkt *rpkt;
+ register struct server *server;
+ register s_fp di;
+ l_fp t10, t23;
+ l_fp org;
+ l_fp rec;
+ l_fp ci;
+ int has_mac;
+ int is_authentic;
+
+ if (debug)
+ printf("receive(%s)\n", ntoa(&rbufp->srcadr));
+ /*
+ * Check to see if the packet basically looks like something
+ * intended for us.
+ */
+ if (rbufp->recv_length == LEN_PKT_NOMAC)
+ has_mac = 0;
+ else if (rbufp->recv_length >= LEN_PKT_NOMAC)
+ has_mac = 1;
+ else {
+ if (debug)
+ printf("receive: packet length %d\n",
+ rbufp->recv_length);
+ return; /* funny length packet */
+ }
+
+ rpkt = &(rbufp->recv_pkt);
+ if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
+ PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
+ return;
+ }
+
+ if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
+ && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
+ || rpkt->stratum > NTP_MAXSTRATUM) {
+ if (debug)
+ printf("receive: mode %d stratum %d\n",
+ PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
+ return;
+ }
+
+ /*
+ * So far, so good. See if this is from a server we know.
+ */
+ server = findserver(&(rbufp->srcadr));
+ if (server == NULL) {
+ if (debug)
+ printf("receive: server not found\n");
+ return;
+ }
+
+ /*
+ * Decode the org timestamp and make sure we're getting a response
+ * to our last request.
+ */
+ NTOHL_FP(&rpkt->org, &org);
+ if (!L_ISEQU(&org, &server->xmt)) {
+ if (debug)
+ printf("receive: pkt.org and peer.xmt differ\n");
+ return;
+ }
+
+ /*
+ * Check out the authenticity if we're doing that.
+ */
+ if (!sys_authenticate)
+ is_authentic = 1;
+ else {
+ is_authentic = 0;
+
+ if (debug > 3)
+ printf("receive: rpkt keyid=%ld sys_authkey=%ld decrypt=%ld\n",
+ (long int)ntohl(rpkt->keyid), (long int)sys_authkey,
+ (long int)authdecrypt(sys_authkey, (U_LONG *)rpkt,
+ LEN_PKT_NOMAC));
+
+ if (has_mac && ntohl(rpkt->keyid) == sys_authkey &&
+ authdecrypt(sys_authkey, (U_LONG *)rpkt, LEN_PKT_NOMAC))
+ is_authentic = 1;
+ if (debug)
+ printf("receive: authentication %s\n",
+ is_authentic ? "passed" : "failed");
+ }
+ server->trust <<= 1;
+ if (!is_authentic)
+ server->trust |= 1;
+
+ /*
+ * Looks good. Record info from the packet.
+ */
+ server->leap = PKT_LEAP(rpkt->li_vn_mode);
+ server->stratum = PKT_TO_STRATUM(rpkt->stratum);
+ server->precision = rpkt->precision;
+ server->rootdelay = ntohl(rpkt->rootdelay);
+ server->rootdispersion = ntohl(rpkt->rootdispersion);
+ server->refid = rpkt->refid;
+ NTOHL_FP(&rpkt->reftime, &server->reftime);
+ NTOHL_FP(&rpkt->rec, &rec);
+ NTOHL_FP(&rpkt->xmt, &server->org);
+
+ /*
+ * Make sure the server is at least somewhat sane. If not, try
+ * again.
+ */
+ if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
+ transmit(server);
+ return;
+ }
+
+ /*
+ * Calculate the round trip delay (di) and the clock offset (ci).
+ * We use the equations (reordered from those in the spec):
+ *
+ * d = (t2 - t3) - (t1 - t0)
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ */
+ t10 = server->org; /* pkt.xmt == t1 */
+ L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/
+
+ t23 = rec; /* pkt.rec == t2 */
+ L_SUB(&t23, &org); /* pkt->org == t3 */
+
+ /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */
+ ci = t10;
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+
+ /*
+ * Calculate di in t23 in full precision, then truncate
+ * to an s_fp.
+ */
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+
+ if (debug > 3)
+ printf("offset: %s, delay %s\n", lfptoa(&ci, 6), fptoa(di, 5));
+
+ di += (FP_SECOND >> (-(int)NTPDATE_PRECISION))
+ + (FP_SECOND >> (-(int)server->precision)) + NTP_MAXSKW;
+
+ if (di <= 0) { /* value still too raunchy to use? */
+ L_CLR(&ci);
+ di = 0;
+ } else {
+ di = max(di, NTP_MINDIST);
+ }
+
+ /*
+ * Shift this data in, then transmit again.
+ */
+ server_data(server, (u_fp) di, &ci, 0);
+ transmit(server);
+}
+
+
+/*
+ * server_data - add a sample to the server's filter registers
+ */
+static void
+server_data(server, d, c, e)
+ register struct server *server;
+ s_fp d;
+ l_fp *c;
+ u_fp e;
+{
+ register int i;
+
+ i = server->filter_nextpt;
+ if (i < NTP_SHIFT) {
+ server->filter_delay[i] = d;
+ server->filter_offset[i] = *c;
+ server->filter_soffset[i] = LFPTOFP(c);
+ server->filter_error[i] = e;
+ server->filter_nextpt = i + 1;
+ }
+}
+
+
+/*
+ * clock_filter - determine a server's delay, dispersion and offset
+ */
+static void
+clock_filter(server)
+ register struct server *server;
+{
+ register int i, j;
+ int ord[NTP_SHIFT];
+
+ /*
+ * Sort indices into increasing delay order
+ */
+ for (i = 0; i < sys_samples; i++)
+ ord[i] = i;
+
+ for (i = 0; i < (sys_samples-1); i++) {
+ for (j = i+1; j < sys_samples; j++) {
+ if (server->filter_delay[ord[j]] == 0)
+ continue;
+ if (server->filter_delay[ord[i]] == 0
+ || (server->filter_delay[ord[i]]
+ > server->filter_delay[ord[j]])) {
+ register int tmp;
+
+ tmp = ord[i];
+ ord[i] = ord[j];
+ ord[j] = tmp;
+ }
+ }
+ }
+
+ /*
+ * Now compute the dispersion, and assign values to delay and
+ * offset. If there are no samples in the register, delay and
+ * offset go to zero and dispersion is set to the maximum.
+ */
+ if (server->filter_delay[ord[0]] == 0) {
+ server->delay = 0;
+ L_CLR(&server->offset);
+ server->soffset = 0;
+ server->dispersion = PEER_MAXDISP;
+ } else {
+ register s_fp d;
+
+ server->delay = server->filter_delay[ord[0]];
+ server->offset = server->filter_offset[ord[0]];
+ server->soffset = LFPTOFP(&server->offset);
+ server->dispersion = 0;
+ for (i = 1; i < sys_samples; i++) {
+ if (server->filter_delay[ord[i]] == 0)
+ d = PEER_MAXDISP;
+ else {
+ d = server->filter_soffset[ord[i]]
+ - server->filter_soffset[ord[0]];
+ if (d < 0)
+ d = -d;
+ if (d > PEER_MAXDISP)
+ d = PEER_MAXDISP;
+ }
+ /*
+ * XXX This *knows* PEER_FILTER is 1/2
+ */
+ server->dispersion += (u_fp)(d) >> i;
+ }
+ }
+ /*
+ * We're done
+ */
+}
+
+
+/*
+ * clock_select - select the pick-of-the-litter clock from the samples
+ * we've got.
+ */
+static struct server *
+clock_select()
+{
+ register struct server *server;
+ register int i;
+ register int nlist;
+ register s_fp d;
+ register int j;
+ register int n;
+ s_fp local_threshold;
+ struct server *server_list[NTP_MAXCLOCK];
+ u_fp server_badness[NTP_MAXCLOCK];
+ struct server *sys_server;
+
+ /*
+ * This first chunk of code is supposed to go through all
+ * servers we know about to find the NTP_MAXLIST servers which
+ * are most likely to succeed. We run through the list
+ * doing the sanity checks and trying to insert anyone who
+ * looks okay. We are at all times aware that we should
+ * only keep samples from the top two strata and we only need
+ * NTP_MAXLIST of them.
+ */
+ nlist = 0; /* none yet */
+ for (n = 0; n < sys_numservers; n++) {
+ server = sys_servers[n];
+ if (server->delay == 0)
+ continue; /* no data */
+ if (server->stratum > NTP_INFIN)
+ continue; /* stratum no good */
+ if (server->delay > NTP_MAXWGT) {
+ continue; /* too far away */
+ }
+ if (server->leap == LEAP_NOTINSYNC)
+ continue; /* he's in trouble */
+ if (!L_ISHIS(&server->org, &server->reftime)) {
+ continue; /* very broken host */
+ }
+ if ((server->org.l_ui - server->reftime.l_ui)
+ >= NTP_MAXAGE) {
+ continue; /* too long without sync */
+ }
+ if (server->trust != 0) {
+ continue;
+ }
+
+ /*
+ * This one seems sane. Find where he belongs
+ * on the list.
+ */
+ d = server->dispersion + server->dispersion;
+ for (i = 0; i < nlist; i++)
+ if (server->stratum <= server_list[i]->stratum)
+ break;
+ for ( ; i < nlist; i++) {
+ if (server->stratum < server_list[i]->stratum)
+ break;
+ if (d < server_badness[i])
+ break;
+ }
+
+ /*
+ * If i points past the end of the list, this
+ * guy is a loser, else stick him in.
+ */
+ if (i >= NTP_MAXLIST)
+ continue;
+ for (j = nlist; j > i; j--)
+ if (j < NTP_MAXLIST) {
+ server_list[j] = server_list[j-1];
+ server_badness[j]
+ = server_badness[j-1];
+ }
+
+ server_list[i] = server;
+ server_badness[i] = d;
+ if (nlist < NTP_MAXLIST)
+ nlist++;
+ }
+
+ /*
+ * Got the five-or-less best. Cut the list where the number of
+ * strata exceeds two.
+ */
+ j = 0;
+ for (i = 1; i < nlist; i++)
+ if (server_list[i]->stratum > server_list[i-1]->stratum)
+ if (++j == 2) {
+ nlist = i;
+ break;
+ }
+
+ /*
+ * Whew! What we should have by now is 0 to 5 candidates for
+ * the job of syncing us. If we have none, we're out of luck.
+ * If we have one, he's a winner. If we have more, do falseticker
+ * detection.
+ */
+
+ if (nlist == 0)
+ sys_server = 0;
+ else if (nlist == 1) {
+ sys_server = server_list[0];
+ } else {
+ /*
+ * Re-sort by stratum, bdelay estimate quality and
+ * server.delay.
+ */
+ for (i = 0; i < nlist-1; i++)
+ for (j = i+1; j < nlist; j++) {
+ if (server_list[i]->stratum
+ < server_list[j]->stratum)
+ break; /* already sorted by stratum */
+ if (server_list[i]->delay
+ < server_list[j]->delay)
+ continue;
+ server = server_list[i];
+ server_list[i] = server_list[j];
+ server_list[j] = server;
+ }
+
+ /*
+ * Calculate the fixed part of the dispersion limit
+ */
+ local_threshold = (FP_SECOND >> (-(int)NTPDATE_PRECISION))
+ + NTP_MAXSKW;
+
+ /*
+ * Now drop samples until we're down to one.
+ */
+ while (nlist > 1) {
+ for (n = 0; n < nlist; n++) {
+ server_badness[n] = 0;
+ for (j = 0; j < nlist; j++) {
+ if (j == n) /* with self? */
+ continue;
+ d = server_list[j]->soffset
+ - server_list[n]->soffset;
+ if (d < 0) /* absolute value */
+ d = -d;
+ /*
+ * XXX This code *knows* that
+ * NTP_SELECT is 3/4
+ */
+ for (i = 0; i < j; i++)
+ d = (d>>1) + (d>>2);
+ server_badness[n] += d;
+ }
+ }
+
+ /*
+ * We now have an array of nlist badness
+ * coefficients. Find the badest. Find
+ * the minimum precision while we're at
+ * it.
+ */
+ i = 0;
+ n = server_list[0]->precision;;
+ for (j = 1; j < nlist; j++) {
+ if (server_badness[j] >= server_badness[i])
+ i = j;
+ if (n > server_list[j]->precision)
+ n = server_list[j]->precision;
+ }
+
+ /*
+ * i is the index of the server with the worst
+ * dispersion. If his dispersion is less than
+ * the threshold, stop now, else delete him and
+ * continue around again.
+ */
+ if (server_badness[i] < (local_threshold
+ + (FP_SECOND >> (-n))))
+ break;
+ for (j = i + 1; j < nlist; j++)
+ server_list[j-1] = server_list[j];
+ nlist--;
+ }
+
+ /*
+ * What remains is a list of less than 5 servers. Take
+ * the best.
+ */
+ sys_server = server_list[0];
+ }
+
+ /*
+ * That's it. Return our server.
+ */
+ return sys_server;
+}
+
+
+/*
+ * clock_adjust - process what we've received, and adjust the time
+ * if we got anything decent.
+ */
+static int
+clock_adjust()
+{
+ register int i;
+ register struct server *server;
+ s_fp absoffset;
+ int dostep;
+
+ for (i = 0; i < sys_numservers; i++)
+ clock_filter(sys_servers[i]);
+ server = clock_select();
+
+ if (debug || simple_query) {
+ for (i = 0; i < sys_numservers; i++)
+ printserver(sys_servers[i], stdout);
+ }
+
+ if (server == 0) {
+ syslog(LOG_ERR,
+ "no server suitable for synchronization found");
+ return(1);
+ }
+
+ dostep = 1;
+ if (!always_step) {
+ absoffset = server->soffset;
+ if (absoffset < 0)
+ absoffset = -absoffset;
+ if (absoffset < NTPDATE_THRESHOLD)
+ dostep = 0;
+ }
+
+ if (dostep) {
+ if (simple_query || l_step_systime(&server->offset)) {
+ syslog(LOG_NOTICE, "step time server %s offset %s",
+ ntoa(&server->srcadr),
+ lfptoa(&server->offset, 6));
+ }
+ } else {
+ if (simple_query || l_adj_systime(&server->offset)) {
+ syslog(LOG_NOTICE, "adjust time server %s offset %s",
+ ntoa(&server->srcadr),
+ lfptoa(&server->offset, 6));
+ }
+ }
+ return(0);
+}
+
+
+/* XXX ELIMINATE: merge BIG slew into adj_systime in lib/systime.c */
+/*
+ * addserver - determine a server's address and allocate a new structure
+ * for it.
+ */
+static void
+addserver(serv)
+ char *serv;
+{
+ register struct server *server;
+ u_long netnum;
+ static int toomany = 0;
+
+ if (sys_numservers >= sys_maxservers) {
+ if (!toomany) {
+ /*
+ * This is actually a `can't happen' now. Leave
+ * the error message in anyway, though
+ */
+ toomany = 1;
+ syslog(LOG_ERR,
+ "too many servers (> %d) specified, remainder not used",
+ sys_maxservers);
+ }
+ return;
+ }
+
+ if (!getnetnum(serv, &netnum)) {
+ syslog(LOG_ERR, "can't find host %s\n", serv);
+ return;
+ }
+
+ server = (struct server *)emalloc(sizeof(struct server));
+ memset((char *)server, 0, sizeof(struct server));
+
+ server->srcadr.sin_family = AF_INET;
+ server->srcadr.sin_addr.s_addr = netnum;
+ server->srcadr.sin_port = htons(NTP_PORT);
+
+ sys_servers[sys_numservers++] = server;
+ server->event_time = sys_numservers;
+}
+
+
+/*
+ * findserver - find a server in the list given its address
+ */
+static struct server *
+findserver(addr)
+ struct sockaddr_in *addr;
+{
+ register int i;
+ register u_long netnum;
+
+ if (htons(addr->sin_port) != NTP_PORT)
+ return 0;
+ netnum = addr->sin_addr.s_addr;
+
+ for (i = 0; i < sys_numservers; i++) {
+ if (netnum == sys_servers[i]->srcadr.sin_addr.s_addr)
+ return sys_servers[i];
+ }
+ return 0;
+}
+
+
+/*
+ * timer - process a timer interrupt
+ */
+static void
+timer()
+{
+ register int i;
+
+ /*
+ * Bump the current idea of the time
+ */
+ current_time++;
+
+ /*
+ * Search through the server list looking for guys
+ * who's event timers have expired. Give these to
+ * the transmit routine.
+ */
+ for (i = 0; i < sys_numservers; i++) {
+ if (sys_servers[i]->event_time != 0
+ && sys_servers[i]->event_time <= current_time)
+ transmit(sys_servers[i]);
+ }
+}
+
+
+
+/*
+ * init_alarm - set up the timer interrupt
+ */
+static void
+init_alarm()
+{
+ struct itimerval itimer;
+
+ alarm_flag = 0;
+
+ /*
+ * Set up the alarm interrupt. The first comes 1/(2*TIMER_HZ)
+ * seconds from now and they continue on every 1/TIMER_HZ seconds.
+ */
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
+ itimer.it_interval.tv_usec = 1000000/TIMER_HZ;
+ itimer.it_value.tv_usec = 1000000/(TIMER_HZ<<1);
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+
+/*
+ * alarming - record the occurance of an alarm interrupt
+ */
+static RETSIGTYPE
+alarming(sig)
+int sig;
+{
+ alarm_flag++;
+}
+
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see if the socket is ready and read the
+ * packets from it into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We allocate a number of recvbufs equal to the number of servers
+ * plus 2. This should be plenty.
+ */
+
+/*
+ * recvbuf lists
+ */
+struct recvbuf *freelist; /* free buffers */
+struct recvbuf *fulllist; /* buffers with data */
+
+int full_recvbufs; /* number of full ones */
+int free_recvbufs;
+
+
+/*
+ * init_io - initialize I/O data and open socket
+ */
+static void
+init_io()
+{
+ register int i;
+ register struct recvbuf *rb;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ rb = (struct recvbuf *)
+ emalloc((sys_numservers + 2) * sizeof(struct recvbuf));
+ freelist = 0;
+ for (i = sys_numservers + 2; i > 0; i--) {
+ rb->next = freelist;
+ freelist = rb;
+ rb++;
+ }
+
+ fulllist = 0;
+ full_recvbufs = 0;
+ free_recvbufs = sys_numservers + 2;
+
+ /*
+ * Open the socket
+ */
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * bind the socket to the NTP port
+ */
+ if (!debug && !simple_query) {
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ if (errno == EADDRINUSE)
+ syslog(LOG_ERR,
+ "the NTP socket is in use, exiting");
+ else
+ syslog(LOG_ERR, "bind() fails: %m");
+ exit(1);
+ }
+ }
+
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+
+ /*
+ * set non-blocking,
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* O_NONBLOCK */
+#if defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* FNDELAY */
+Need non blocking I/O
+#endif /* FNDELAY */
+#endif /* O_NONBLOCK */
+}
+
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * getrecvbufs - get receive buffers which have data in them
+ *
+ * ***N.B. must be called with SIGIO blocked***
+ */
+static struct recvbuf *
+getrecvbufs()
+{
+ struct recvbuf *rb;
+
+ if (full_recvbufs == 0) {
+ return (struct recvbuf *)0; /* nothing has arrived */
+ }
+
+ /*
+ * Get the fulllist chain and mark it empty
+ */
+ rb = fulllist;
+ fulllist = 0;
+ full_recvbufs = 0;
+
+ /*
+ * Return the chain
+ */
+ return rb;
+}
+
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+static void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+}
+
+
+/*
+ * sendpkt - send a packet to the specified destination
+ */
+static void
+sendpkt(dest, pkt, len)
+ struct sockaddr_in *dest;
+ struct pkt *pkt;
+ int len;
+{
+ int cc;
+
+ cc = sendto(fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ if (errno != EWOULDBLOCK && errno != ENOBUFS)
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+}
+
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+static void
+input_handler()
+{
+ register int n;
+ register struct recvbuf *rb;
+ struct timeval tvzero;
+ int fromlen;
+ l_fp ts;
+ fd_set fds;
+
+ /*
+ * Do a poll to see if we have data
+ */
+ for (;;) {
+ fds = fdmask;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ /*
+ * If nothing to do, just return. If an error occurred,
+ * complain and return. If we've got some, freeze a
+ * timestamp.
+ */
+ if (n == 0)
+ return;
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return;
+ }
+ get_systime(&ts);
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on the wild card socket, just dump the packet.
+ */
+ if (initializing || free_recvbufs == 0) {
+ char buf[100];
+
+ (void) read(fd, buf, sizeof buf);
+ continue;
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt,
+ sizeof(rb->recv_pkt), 0,
+ (struct sockaddr *)&rb->srcadr, &fromlen);
+ if (rb->recv_length == -1) {
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list.
+ */
+ rb->recv_time = ts;
+ rb->next = fulllist;
+ fulllist = rb;
+ full_recvbufs++;
+ }
+}
+
+
+/*
+ * adj_systime - do a big long slew of the system time
+ */
+static int
+l_adj_systime(ts)
+ l_fp *ts;
+{
+ struct timeval adjtv, oadjtv;
+ int isneg = 0;
+ l_fp offset;
+ l_fp overshoot;
+
+ /*
+ * Take the absolute value of the offset
+ */
+ offset = *ts;
+ if (L_ISNEG(&offset)) {
+ isneg = 1;
+ L_NEG(&offset);
+ }
+
+#ifndef STEP_SLEW
+ /*
+ * Calculate the overshoot. XXX N.B. This code *knows*
+ * ADJ_OVERSHOOT is 1/2.
+ */
+ overshoot = offset;
+ L_RSHIFTU(&overshoot);
+ if (overshoot.l_ui != 0 || (overshoot.l_uf > ADJ_MAXOVERSHOOT)) {
+ overshoot.l_ui = 0;
+ overshoot.l_uf = ADJ_MAXOVERSHOOT;
+ }
+ L_ADD(&offset, &overshoot);
+#endif
+ TSTOTV(&offset, &adjtv);
+
+ if (isneg) {
+ adjtv.tv_sec = -adjtv.tv_sec;
+ adjtv.tv_usec = -adjtv.tv_usec;
+ }
+
+ if (adjtv.tv_usec != 0 && !debug) {
+ if (adjtime(&adjtv, &oadjtv) < 0) {
+ syslog(LOG_ERR, "Can't adjust the time of day: %m");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * This fuction is not the same as lib/systime step_systime!!!
+ */
+static int
+l_step_systime(ts)
+ l_fp *ts;
+{
+#ifdef SLEWALWAYS
+#ifdef STEP_SLEW
+ l_fp ftmp;
+ int isneg;
+ int n;
+
+ if (debug) return 1;
+ /*
+ * Take the absolute value of the offset
+ */
+ ftmp = ts;
+ if (L_ISNEG(&ftmp)) {
+ L_NEG(&tmp);
+ isneg = 1;
+ } else
+ isneg = 0;
+
+ if (tmp_ui >= 3) { /* Step it and slew - we might win */
+ n = step_systime_real(ts);
+ if (!n)
+ return n;
+ if (isneg)
+ ts->l_ui = ~0;
+ else
+ ts->l_ui = ~0;
+ }
+ /*
+ * Just add adjustment into the current offset. The update
+ * routine will take care of bringing the system clock into
+ * line.
+ */
+#endif
+ if (debug)
+ return 1;
+#ifdef FORCE_NTPDATE_STEP
+ return step_systime_real(ts);
+#else
+ l_adj_systime(ts);
+ return 1;
+#endif
+#else /* SLEWALWAYS */
+ if (debug)
+ return 1;
+ return step_systime_real(ts);
+#endif /* SLEWALWAYS */
+}
+
+/*
+ * getnetnum - given a host name, return its net number
+ */
+static int
+getnetnum(host, num)
+ char *host;
+ u_long *num;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(U_LONG));
+ return (1);
+ }
+ return (0);
+}
+
+/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */
+/*
+ * printserver - print detail information for a server
+ */
+static void
+printserver(pp, fp)
+ register struct server *pp;
+ FILE *fp;
+{
+ register int i;
+ char junk[5];
+ char *str;
+
+ if (!debug) {
+ (void) fprintf(fp, "server %s, stratum %d, offset %s, delay %s\n",
+ ntoa(&pp->srcadr), pp->stratum,
+ lfptoa(&pp->offset, 6), fptoa(pp->delay, 5));
+ return;
+ }
+
+ (void) fprintf(fp, "server %s, port %d\n",
+ ntoa(&pp->srcadr), ntohs(pp->srcadr.sin_port));
+
+ (void) fprintf(fp, "stratum %d, precision %d, leap %c%c, trust %03o\n",
+ pp->stratum, pp->precision,
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0',
+ pp->trust);
+
+ if (pp->stratum == 1) {
+ junk[4] = 0;
+ memmove(junk, (char *)&pp->refid, 4);
+ str = junk;
+ } else {
+ str = numtoa(pp->refid);
+ }
+ (void) fprintf(fp,
+ "refid [%s], delay %s, dispersion %s\n",
+ str, fptoa(pp->delay, 5),
+ ufptoa(pp->dispersion, 5));
+
+ (void) fprintf(fp, "transmitted %d, in filter %d\n",
+ pp->xmtcnt, pp->filter_nextpt);
+
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&pp->reftime));
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&pp->org));
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&pp->xmt));
+
+ (void) fprintf(fp, "filter delay: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s", fptoa(pp->filter_delay[i], 5));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter offset:");
+ for (i = 0; i < PEER_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s", lfptoa(&pp->filter_offset[i], 6));
+ if (i == (PEER_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "delay %s, dispersion %s\n",
+ fptoa(pp->delay, 5), ufptoa(pp->dispersion, 5));
+
+ (void) fprintf(fp, "offset %s\n\n",
+ lfptoa(&pp->offset, 6));
+}
+
+#if defined(NEED_VSPRINTF)
+/*
+ * This nugget for pre-tahoe 4.3bsd systems
+ */
+#if !defined(__STDC__) || !__STDC__
+#define const
+#endif
+
+int
+vsprintf(str, fmt, ap)
+ char *str;
+ const char *fmt;
+ va_list ap;
+{
+ FILE f;
+ int len;
+
+ f._flag = _IOWRT+_IOSTRG;
+ f._ptr = str;
+ f._cnt = 32767;
+ len = _doprnt(fmt, ap, &f);
+ *f._ptr = 0;
+ return (len);
+}
+#endif
+
diff --git a/usr.sbin/xntpd/ntpdate/ntpdate.h b/usr.sbin/xntpd/ntpdate/ntpdate.h
new file mode 100644
index 0000000..cfbe90a
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/ntpdate.h
@@ -0,0 +1,89 @@
+/*
+ * ntpdate.h - declarations for the ntpdate program
+ */
+
+#include "ntp_malloc.h"
+
+/*
+ * The server structure is a much simplified version of the
+ * peer structure, for ntpdate's use. Since we always send
+ * in client mode and expect to receive in server mode, this
+ * leaves only a very limited number of things we need to
+ * remember about the server.
+ */
+struct server {
+ struct sockaddr_in srcadr; /* address of remote host */
+ u_char leap; /* leap indicator */
+ u_char stratum; /* stratum of remote server */
+ s_char precision; /* server's clock precision */
+ u_char trust; /* trustability of the filtered data */
+ u_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ u_long event_time; /* time for next timeout */
+ u_short xmtcnt; /* number of packets transmitted */
+ u_short filter_nextpt; /* index into filter shift register */
+ s_fp filter_delay[NTP_SHIFT]; /* delay part of shift register */
+ l_fp filter_offset[NTP_SHIFT]; /* offset part of shift register */
+ s_fp filter_soffset[NTP_SHIFT]; /* offset in s_fp format, for disp */
+ u_fp filter_error[NTP_SHIFT]; /* error part of shift register */
+ l_fp org; /* peer's originate time stamp */
+ l_fp xmt; /* transmit time stamp */
+ u_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+ s_fp soffset; /* fp version of above */
+};
+
+
+/*
+ * ntpdate runs everything on a simple, short timeout. It sends a
+ * packet and sets the timeout (by default, to a small value suitable
+ * for a LAN). If it receives a response it sends another request.
+ * If it times out it shifts zeroes into the filter and sends another
+ * request.
+ *
+ * The timer routine is run often (once every 1/5 second currently)
+ * so that time outs are done with reasonable precision.
+ */
+#define TIMER_HZ (5) /* 5 per second */
+
+/*
+ * ntpdate will make a long adjustment using adjtime() if the times
+ * are close, or step the time if the times are farther apart. The
+ * following defines what is "close".
+ */
+#ifdef linux
+#define NTPDATE_THRESHOLD (FP_SECOND / 8) /* 1/8 second */
+#else
+#define NTPDATE_THRESHOLD (FP_SECOND >> 1) /* 1/2 second */
+#endif
+
+/*
+ * When doing adjustments, ntpdate actually overadjusts (currently
+ * by 50%, though this may change). While this will make it take longer
+ * to reach a steady state condition, it will typically result in
+ * the clock keeping more accurate time, on average. The amount of
+ * overshoot is limited.
+ */
+#ifdef NOTNOW
+#define ADJ_OVERSHOOT 1/2 /* this is hard coded */
+#endif /* NOTNOW */
+#define ADJ_MAXOVERSHOOT 0x10000000 /* 50 ms as a ts fraction */
+
+/*
+ * Since ntpdate isn't aware of some of the things that normally get
+ * put in an NTP packet, we fix some values.
+ */
+#define NTPDATE_PRECISION (-6) /* use this precision */
+#define NTPDATE_DISTANCE FP_SECOND /* distance is 1 sec */
+#define NTPDATE_DISP FP_SECOND /* so is the dispersion */
+#define NTPDATE_REFID (0) /* reference ID to use */
+
+
+/*
+ * Some defaults
+ */
+#define DEFTIMEOUT 5 /* 5 timer increments */
+#define DEFSAMPLES 4 /* get 4 samples per server */
diff --git a/usr.sbin/xntpd/ntpq/Makefile b/usr.sbin/xntpd/ntpq/Makefile
new file mode 100644
index 0000000..b630b28
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/Makefile
@@ -0,0 +1,20 @@
+#
+# $Id: Makefile,v 1.9 1998/03/07 09:46:08 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= ntpq
+MAN8= ${.CURDIR}/../doc/ntpq.8
+CLEANFILES+= .version version.c
+BINDIR= /usr/bin
+
+SRCS= ntpq.c ntpq_ops.c version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntpq
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntpq/README b/usr.sbin/xntpd/ntpq/README
new file mode 100644
index 0000000..117c66c
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/README
@@ -0,0 +1,6 @@
+README file for directory ./ntpq of the NTP Version 3 distribution
+
+This directory contains the sources for the ntpq utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/ntpq/ntpq.c b/usr.sbin/xntpd/ntpq/ntpq.c
new file mode 100644
index 0000000..2c5eacf
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq.c
@@ -0,0 +1,3091 @@
+/*
+ * ntpq - query an NTP server using mode 6 commands
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntp_select.h"
+#include "ntpq.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Because we potentially understand a lot of commands we will run
+ * interactive if connected to a terminal.
+ */
+int interactive = 0; /* set to 1 when we should prompt */
+char *prompt = "ntpq> "; /* prompt to ask him about */
+
+
+/*
+ * Keyid used for authenticated requests. Obtained on the fly.
+ */
+u_long info_auth_keyid = -1;
+
+/*
+ * Type of key md5 or des
+ */
+#define KEY_TYPE_DES 3
+#define KEY_TYPE_MD5 4
+
+int info_auth_keytype = KEY_TYPE_DES; /* DES */
+
+/*
+ * Flag which indicates we should always send authenticated requests
+ */
+int always_auth = 0;
+
+/*
+ * Flag which indicates raw mode output.
+ */
+int rawmode = 0;
+
+/*
+ * Packet version number we use
+ */
+u_char pktversion = NTP_VERSION;
+
+/*
+ * Don't jump if no set jmp.
+ */
+int jump = 0;
+
+/*
+ * Format values
+ */
+#define PADDING 0
+#define TS 1 /* time stamp */
+#define FL 2 /* l_fp type value */
+#define FU 3 /* u_fp type value */
+#define FS 4 /* s_fp type value */
+#define UI 5 /* unsigned integer value */
+#define IN 6 /* signed integer value */
+#define HA 7 /* host address */
+#define NA 8 /* network address */
+#define ST 9 /* string value */
+#define RF 10 /* refid (sometimes string, sometimes not) */
+#define LP 11 /* leap (print in binary) */
+#define OC 12 /* integer, print in octal */
+#define MD 13 /* mode */
+#define AR 14 /* array of times */
+#define TST 15 /* test flags */
+#define EOV 255 /* end of table */
+
+
+/*
+ * System variable values. The array can be indexed by
+ * the variable index to find the textual name.
+ */
+struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, LP, "leap" }, /* 1 */
+ { CS_STRATUM, UI, "stratum" }, /* 2 */
+ { CS_PRECISION, IN, "precision" }, /* 3 */
+ { CS_ROOTDELAY, FS, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
+ { CS_REFID, RF, "refid" }, /* 6 */
+ { CS_REFTIME, TS, "reftime" }, /* 7 */
+ { CS_POLL, UI, "poll" }, /* 8 */
+ { CS_PEERID, UI, "peer" }, /* 9 */
+ { CS_OFFSET, FL, "phase" }, /* 10 */
+ { CS_DRIFT, FS, "freq" }, /* 11 */
+ { CS_COMPLIANCE, FU, "error" }, /* 12 */
+ { CS_CLOCK, TS, "clock" }, /* 13 */
+ { CS_LEAPIND, LP, "leapindicator" }, /* 14 */
+ { CS_LEAPWARNING, LP, "leapwarning" }, /* 15 */
+ { CS_PROCESSOR, ST, "processor" }, /* 16 */
+ { CS_SYSTEM, ST, "system" }, /* 17 */
+ { CS_KEYID, UI, "keyid" }, /* 18 */
+ { CS_REFSKEW, FL, "refskew" }, /* 19 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variable list
+ */
+struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, UI, "config" }, /* 1 */
+ { CP_AUTHENABLE, UI, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, UI, "authentic" }, /* 3 */
+ { CP_SRCADR, HA, "srcadr" }, /* 4 */
+ { CP_SRCPORT, UI, "srcport" }, /* 5 */
+ { CP_DSTADR, NA, "dstadr" }, /* 6 */
+ { CP_DSTPORT, UI, "dstport" }, /* 7 */
+ { CP_LEAP, LP, "leap" }, /* 8 */
+ { CP_HMODE, MD, "hmode" }, /* 9 */
+ { CP_STRATUM, UI, "stratum" }, /* 10 */
+ { CP_PPOLL, UI, "ppoll" }, /* 11 */
+ { CP_HPOLL, UI, "hpoll" }, /* 12 */
+ { CP_PRECISION, IN, "precision" }, /* 13 */
+ { CP_ROOTDELAY, FS, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
+ { CP_REFID, RF, "refid" }, /* 16 */
+ { CP_REFTIME, TS, "reftime" }, /* 17 */
+ { CP_ORG, TS, "org" }, /* 18 */
+ { CP_REC, TS, "rec" }, /* 19 */
+ { CP_XMT, TS, "xmt" }, /* 20 */
+ { CP_REACH, OC, "reach" }, /* 21 */
+ { CP_VALID, UI, "valid" }, /* 22 */
+ { CP_TIMER, UI, "timer" }, /* 23 */
+ { CP_DELAY, AR, "delay" }, /* 24 */
+ { CP_OFFSET, AR, "offset" }, /* 25 */
+ { CP_DISPERSION, FU, "dispersion" }, /* 26 */
+ { CP_KEYID, UI, "keyid" }, /* 27 */
+ { CP_FILTDELAY, AR, "filtdelay" }, /* 28 */
+ { CP_FILTOFFSET, AR, "filtoffset" }, /* 29 */
+ { CP_PMODE, ST, "pmode" }, /* 30 */
+ { CP_RECEIVED, UI, "received" }, /* 31 */
+ { CP_SENT, UI, "sent" }, /* 32 */
+ { CP_FILTERROR, AR, "filterror" }, /* 33 */
+ { CP_FLASH, TST, "flash"}, /* 34 */
+ { CP_DISP, AR, "disp" }, /* 35 */
+ /*
+ * These are duplicate entries so that we can
+ * process deviant version of the xntp protocal.
+ */
+ { CP_SRCADR, HA, "peeraddr" }, /* 4 */
+ { CP_SRCPORT, UI, "peerport" }, /* 5 */
+ { CP_PPOLL, UI, "peerpoll" }, /* 11 */
+ { CP_HPOLL, UI, "hostpoll" }, /* 12 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Clock variable list
+ */
+struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, UI, "type" }, /* 1 */
+ { CC_TIMECODE, ST, "timecode" }, /* 2 */
+ { CC_POLL, UI, "poll" }, /* 3 */
+ { CC_NOREPLY, UI, "noreply" }, /* 4 */
+ { CC_BADFORMAT, UI, "badformat" }, /* 5 */
+ { CC_BADDATA, UI, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, FL, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, FL, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, UI, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RF, "refid" }, /* 10 */
+ { CC_FLAGS, UI, "flags" }, /* 11 */
+ { CC_DEVICE, ST, "device" }, /* 12 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Structure for turning various constants into a readable string.
+ */
+struct codestring {
+ int code;
+ char *string;
+};
+
+/*
+ * flasher bits
+ */
+static char *tstflagnames[] = {
+ "DUPLICATE PKT",
+ "BOGUS PKT",
+ "PROTO UNSYNC",
+ "PEER BOUNDS",
+ "BAD AUTH",
+ "PEER CLOCK UNSYNC",
+ "BAD STRATUM",
+ "ROOT BOUNDS"
+};
+
+/*
+ * Leap values
+ */
+struct codestring leap_codes[] = {
+ { 0, "leap_none" },
+ { 1, "leap_add_sec" },
+ { 2, "leap_del_sec" },
+ { 3, "sync_alarm" },
+ { -1, "leap" }
+};
+
+
+/*
+ * Clock source
+ */
+struct codestring sync_codes[] = {
+ { CTL_SST_TS_UNSPEC, "sync_unspec" },
+ { CTL_SST_TS_ATOM, "sync_atomic" },
+ { CTL_SST_TS_LF, "sync_lf_clock" },
+ { CTL_SST_TS_HF, "sync_hf_clock" },
+ { CTL_SST_TS_UHF, "sync_uhf_clock" },
+ { CTL_SST_TS_LOCAL, "sync_local_proto" },
+ { CTL_SST_TS_NTP, "sync_ntp" },
+ { CTL_SST_TS_UDPTIME, "sync_udp/time" },
+ { CTL_SST_TS_WRSTWTCH, "sync_wristwatch" },
+ { CTL_SST_TS_TELEPHONE, "sync_telephone" },
+ { -1, "sync" }
+};
+
+
+/*
+ * Peer selection
+ */
+struct codestring select_codes[] = {
+ { CTL_PST_SEL_REJECT, "sel_reject" },
+ { CTL_PST_SEL_SANE, "sel_sane" },
+ { CTL_PST_SEL_CORRECT, "sel_correct" },
+ { CTL_PST_SEL_SELCAND, "sel_candidate" },
+ { CTL_PST_SEL_SYNCCAND, "sel_sync" },
+ { CTL_PST_SEL_DISTSYSPEER, "sel_sys.peer, hi_dist" },
+ { CTL_PST_SEL_SYSPEER, "sel_sys.peer" },
+ { -1, "sel" }
+};
+
+
+/*
+ * Clock status
+ */
+struct codestring clock_codes[] = {
+ { CTL_CLK_OKAY, "clk_okay" },
+ { CTL_CLK_NOREPLY, "clk_noreply" },
+ { CTL_CLK_BADFORMAT, "clk_badformat" },
+ { CTL_CLK_FAULT, "clk_fault" },
+ { CTL_CLK_PROPAGATION, "clk_propagation" },
+ { CTL_CLK_BADDATE, "clk_baddate" },
+ { CTL_CLK_BADTIME, "clk_badtime" },
+ { -1, "clk" }
+};
+
+
+/*
+ * System Events
+ */
+struct codestring sys_codes[] = {
+ { EVNT_UNSPEC, "event_unspec" },
+ { EVNT_SYSRESTART, "event_restart" },
+ { EVNT_SYSFAULT, "event_fault" },
+ { EVNT_SYNCCHG, "event_sync_chg" },
+ { EVNT_PEERSTCHG, "event_peer/strat_chg" },
+ { EVNT_CLOCKRESET, "event_clock_reset" },
+ { EVNT_BADDATETIM, "event_bad_date" },
+ { EVNT_CLOCKEXCPT, "event_clock_excptn" },
+ { -1, "event" }
+};
+
+/*
+ * Peer Events
+ */
+struct codestring peer_codes[] = {
+ { EVNT_UNSPEC, "event_unspec" },
+ { EVNT_PEERIPERR & ~PEER_EVENT, "event_ip_err" },
+ { EVNT_PEERAUTH & ~PEER_EVENT, "event_authen" },
+ { EVNT_UNREACH & ~PEER_EVENT, "event_unreach" },
+ { EVNT_REACH & ~PEER_EVENT, "event_reach" },
+#if 0
+ { EVNT_PEERSTRAT & ~PEER_EVENT, "event_stratum_chg" },
+#endif
+ { -1, "event" }
+};
+
+
+/*
+ * Built in command handler declarations
+ */
+static int openhost P((char *));
+static int sendpkt P((char *, int));
+static int getresponse P((int, int, u_short *, int *, char **, int));
+static int sendrequest P((int, int, int, int, char *));
+static void getcmds P((void));
+static RETSIGTYPE abortcmd P((int));
+static void docmd P((char *));
+static void tokenize P((char *, char **, int *));
+static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
+static int getarg P((char *, int, arg_v *));
+static int rtdatetolfp P((char *, l_fp *));
+static int decodearr P((char *, int *, l_fp *));
+static char * getcode P((int, struct codestring *));
+static void help P((struct parse *, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int helpsort P((const void *, const void *));
+#else
+static int helpsort P((char **, char **));
+#endif /* sgi */
+static void printusage P((struct xcmd *, FILE *));
+static void timeout P((struct parse *, FILE *));
+static void delay P((struct parse *, FILE *));
+static void host P((struct parse *, FILE *));
+static void ntp_poll P((struct parse *, FILE *));
+static void keyid P((struct parse *, FILE *));
+static void keytype P((struct parse *, FILE *));
+static void passwd P((struct parse *, FILE *));
+static void hostnames P((struct parse *, FILE *));
+static void setdebug P((struct parse *, FILE *));
+static void quit P((struct parse *, FILE *));
+static void version P((struct parse *, FILE *));
+static void raw P((struct parse *, FILE *));
+static void cooked P((struct parse *, FILE *));
+static void authenticate P((struct parse *, FILE *));
+static void ntpversion P((struct parse *, FILE *));
+static void warning P((char *, char *, char *));
+static void error P((char *, char *, char *));
+static u_long getkeyid P((char *));
+static void atoascii P((int, char *, char *));
+static void makeascii P((int, char *, FILE *));
+static char * getevents P((int));
+static char * statustoa P((int, int));
+static void rawprint P((int, int, char *, int, FILE *));
+static void startoutput P((void));
+static void output P((FILE *, char *, char *));
+static void endoutput P((FILE *));
+static void outputarr P((FILE *, char *, int, l_fp *));
+static void cookedprint P((int, int, char *, int, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int assoccmp P((const void *, const void *));
+#else
+static int assoccmp P((struct association *, struct association *));
+#endif /* sgi || bsdi */
+
+
+/*
+ * Built-in commands we understand
+ */
+struct xcmd builtins[] = {
+ { "?", help, { OPT|STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "help", help, { OPT|STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "timeout", timeout, { OPT|UINT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the primary receive time out" },
+ { "delay", delay, { OPT|INT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the delay added to encryption time stamps" },
+ { "host", host, { OPT|STR, NO, NO, NO },
+ { "hostname", "", "", "" },
+ "specify the host whose NTP server we talk to" },
+ { "poll", ntp_poll, { OPT|UINT, OPT|STR, NO, NO },
+ { "n", "verbose", "", "" },
+ "poll an NTP server in client mode `n' times" },
+ { "passwd", passwd, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "specify a password to use for authenticated requests"},
+ { "hostnames", hostnames, { OPT|STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "specify whether hostnames or net numbers are printed"},
+ { "debug", setdebug, { OPT|STR, NO, NO, NO },
+ { "no|more|less", "", "", "" },
+ "set/change debugging level" },
+ { "quit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit ntpq" },
+ { "keyid", keyid, { OPT|UINT, NO, NO, NO },
+ { "key#", "", "", "" },
+ "set keyid to use for authenticated requests" },
+ { "version", version, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print version number" },
+ { "raw", raw, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do raw mode variable output" },
+ { "cooked", cooked, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do cooked mode variable output" },
+ { "authenticate", authenticate, { OPT|STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "always authenticate requests to this server" },
+ { "ntpversion", ntpversion, { OPT|UINT, NO, NO, NO },
+ { "version number", "", "", "" },
+ "set the NTP version number to use for requests" },
+ { "keytype", keytype, { STR, NO, NO, NO },
+ { "key type (md5|des)", "", "", "" },
+ "set key type to use for authenticated requests (des|md5)" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Default values we use.
+ */
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define DEFHOST "localhost" /* default host name */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 100 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
+#define MAXVARLEN 256 /* maximum length of a variable name */
+#define MAXVALLEN 256 /* maximum length of a variable value */
+#define MAXOUTLINE 72 /* maximum length of an output line */
+
+/*
+ * Some variables used and manipulated locally
+ */
+struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
+l_fp delay_time; /* delay time */
+char currenthost[LENHOSTNAME]; /* current host name */
+struct sockaddr_in hostaddr = { 0 }; /* host address */
+int showhostnames = 1; /* show host names by default */
+
+int sockfd; /* fd socket is openned on */
+int havehost = 0; /* set to 1 when host open */
+struct servent *server_entry = NULL; /* server entry for ntp */
+
+/*
+ * Sequence number used for requests. It is incremented before
+ * it is used.
+ */
+u_short sequence;
+
+/*
+ * Holds data returned from queries. Declare buffer long to be sure of
+ * alignment.
+ */
+#define MAXFRAGS 24 /* maximum number of fragments */
+#define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
+long pktdata[DATASIZE/sizeof(long)];
+
+/*
+ * Holds association data for use with the &n operator.
+ */
+struct association assoc_cache[MAXASSOC];
+int numassoc = 0; /* number of cached associations */
+
+/*
+ * For commands typed on the command line (with the -c option)
+ */
+int numcmds = 0;
+char *ccmds[MAXCMDS];
+#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
+
+/*
+ * When multiple hosts are specified.
+ */
+int numhosts = 0;
+char *chosts[MAXHOSTS];
+#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_UNSPEC 256
+#define ERR_INCOMPLETE 257
+#define ERR_TIMEOUT 258
+#define ERR_TOOMUCH 259
+
+/*
+ * Macro definitions we use
+ */
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Jump buffer for longjumping back to the command level
+ */
+jmp_buf interrupt_buf;
+
+/*
+ * Points at file being currently printed into
+ */
+FILE *current_output;
+
+/*
+ * Command table imported from ntpdc_ops.c
+ */
+extern struct xcmd opcmds[];
+
+char *progname;
+int debug;
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ delay_time.l_ui = 0;
+ delay_time.l_uf = DEFDELAY;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "c:dinp")) != EOF)
+ switch (c) {
+ case 'c':
+ ADDCMD(ntp_optarg);
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'n':
+ showhostnames = 0;
+ break;
+ case 'p':
+ ADDCMD("peers");
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr,
+ "usage: %s [-dinp] [-c cmd] host ...\n",
+ progname);
+ exit(2);
+ }
+ if (ntp_optind == argc) {
+ ADDHOST(DEFHOST);
+ } else {
+ for (; ntp_optind < argc; ntp_optind++)
+ ADDHOST(argv[ntp_optind]);
+ }
+
+ if (numcmds == 0 && interactive == 0
+ && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
+ interactive = 1;
+ }
+
+ if (interactive)
+ (void) signal_no_reset(SIGINT, abortcmd);
+
+ if (numcmds == 0) {
+ (void) openhost(chosts[0]);
+ getcmds();
+ } else {
+ int ihost;
+ int icmd;
+
+ for (ihost = 0; ihost < numhosts; ihost++) {
+ if (openhost(chosts[ihost]))
+ for (icmd = 0; icmd < numcmds; icmd++)
+ docmd(ccmds[icmd]);
+ }
+ }
+ exit(0);
+}
+
+
+/*
+ * openhost - open a socket to a host
+ */
+static int
+openhost(hname)
+ char *hname;
+{
+ u_long netnum;
+ char temphost[LENHOSTNAME];
+
+ if (server_entry == NULL) {
+ server_entry = getservbyname("ntp", "udp");
+ if (server_entry == NULL) {
+ (void) fprintf(stderr, "%s: ntp/udp: unknown service\n",
+ progname);
+ exit(1);
+ }
+ if (debug > 2)
+ printf("Got ntp/udp service entry\n");
+ }
+
+ if (!getnetnum(hname, &netnum, temphost))
+ return 0;
+
+ if (debug > 2)
+ printf("Opening host %s\n", temphost);
+
+ if (havehost == 1) {
+ if (debug > 2)
+ printf("Closing old host %s\n", currenthost);
+ (void) close(sockfd);
+ havehost = 0;
+ }
+ (void) strcpy(currenthost, temphost);
+
+ hostaddr.sin_family = AF_INET;
+ hostaddr.sin_port = server_entry->s_port;
+ hostaddr.sin_addr.s_addr = netnum;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1)
+ error("socket", "", "");
+
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+#ifdef SO_RCVBUF
+ { int rbufsize = DATASIZE + 2048; /* 2K for slop */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ &rbufsize, sizeof(int)) == -1)
+ error("setsockopt", "", "");
+ }
+#endif
+#endif
+
+ if (connect(sockfd, (struct sockaddr *)&hostaddr,
+ sizeof(hostaddr)) == -1)
+ error("connect", "", "");
+
+ havehost = 1;
+ return 1;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the remote host
+ */
+static int
+sendpkt(xdata, xdatalen)
+ char *xdata;
+ int xdatalen;
+{
+ if (debug >= 3)
+ printf("Sending %d octets\n", xdatalen);
+
+ if (write(sockfd, xdata, xdatalen) == -1) {
+ warning("write to %s failed", currenthost, "");
+ return -1;
+ }
+
+ if (debug >= 4) {
+ int first = 8;
+ printf("Packet data:\n");
+ while (xdatalen-- > 0) {
+ if (first-- == 0) {
+ printf("\n");
+ first = 7;
+ }
+ printf(" %02x", *xdata++ & 0xff);
+ }
+ printf("\n");
+ }
+ return 0;
+}
+
+
+
+/*
+ * getresponse - get a (series of) response packet(s) and return the data
+ */
+static int
+getresponse(opcode, associd, rstatus, rsize, rdata, timeo)
+ int opcode;
+ int associd;
+ u_short *rstatus;
+ int *rsize;
+ char **rdata;
+ int timeo;
+{
+ struct ntp_control rpkt;
+ struct timeval tvo;
+ u_short offsets[MAXFRAGS+1];
+ u_short counts[MAXFRAGS+1];
+ u_short offset;
+ u_short count;
+ int numfrags;
+ int seenlastfrag;
+ fd_set fds;
+ int n;
+
+ /*
+ * This is pretty tricky. We may get between 1 and MAXFRAG packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how much data we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *rsize = 0;
+ if (rstatus)
+ *rstatus = 0;
+ *rdata = (char *)pktdata;
+
+ numfrags = 0;
+ seenlastfrag = 0;
+
+ FD_ZERO(&fds);
+
+again:
+ if (numfrags == 0)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+
+ FD_SET(sockfd, &fds);
+ n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+
+ if (debug >= 1)
+ printf("select() returns %d\n", n);
+
+ if (n == -1) {
+ warning("select fails", "", "");
+ return -1;
+ }
+ if (n == 0) {
+ /*
+ * Timed out. Return what we have
+ */
+ if (numfrags == 0) {
+ if (timeo)
+ (void) fprintf(stderr,
+ "%s: timed out, nothing received\n",
+ currenthost);
+ return ERR_TIMEOUT;
+ } else {
+ if (timeo)
+ (void) fprintf(stderr,
+ "%s: timed out with incomplete data\n",
+ currenthost);
+ if (debug) {
+ printf("Received fragments:\n");
+ for (n = 0; n < numfrags; n++)
+ printf("%4d %d\n", offsets[n],
+ counts[n]);
+ if (seenlastfrag)
+ printf("last fragment received\n");
+ else
+ printf("last fragment not received\n");
+ }
+ return ERR_INCOMPLETE;
+ }
+ }
+
+ n = read(sockfd, (char *)&rpkt, sizeof(rpkt));
+ if (n == -1) {
+ warning("read", "", "");
+ return -1;
+ }
+
+ if (debug >= 4) {
+ int len = n, first = 8;
+ char *data = (char *)&rpkt;
+
+ printf("Packet data:\n");
+ while (len-- > 0) {
+ if (first-- == 0) {
+ printf("\n");
+ first = 7;
+ }
+ printf(" %02x", *data++ & 0xff);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < CTL_HEADER_LEN) {
+ if (debug)
+ printf("Short (%d byte) packet received\n", n);
+ goto again;
+ }
+ if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
+ || PKT_VERSION(rpkt.li_vn_mode) <= NTP_OLDVERSION) {
+ if (debug)
+ printf("Packet received with version %d\n",
+ PKT_VERSION(rpkt.li_vn_mode));
+ goto again;
+ }
+ if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
+ if (debug)
+ printf("Packet received with mode %d\n",
+ PKT_MODE(rpkt.li_vn_mode));
+ goto again;
+ }
+ if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received request packet, wanted response\n");
+ goto again;
+ }
+
+ /*
+ * Check opcode and sequence number for a match.
+ * Could be old data getting to us.
+ */
+ if (ntohs(rpkt.sequence) != sequence) {
+ if (debug)
+ printf(
+ "Received sequnce number %d, wanted %d\n",
+ ntohs(rpkt.sequence), sequence);
+ goto again;
+ }
+ if (CTL_OP(rpkt.r_m_e_op) != opcode) {
+ if (debug)
+ printf(
+ "Received opcode %d, wanted %d (sequence number okay)\n",
+ CTL_OP(rpkt.r_m_e_op), opcode);
+ goto again;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (CTL_ISERROR(rpkt.r_m_e_op)) {
+ int errcode;
+
+ errcode = (ntohs(rpkt.status) >> 8) & 0xff;
+ if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
+ printf("Error code %d received on not-final packet\n",
+ errcode);
+ }
+ if (errcode == CERR_UNSPEC)
+ return ERR_UNSPEC;
+ return errcode;
+ }
+
+ /*
+ * Check the association ID to make sure it matches what
+ * we sent.
+ */
+ if (ntohs(rpkt.associd) != associd) {
+ if (debug)
+ printf("Association ID %d doesn't match expected %d\n",
+ ntohs(rpkt.associd), associd);
+ /*
+ * Hack for silly fuzzballs which, at the time of writing,
+ * return an assID of sys.peer when queried for system variables.
+ */
+#ifdef notdef
+ goto again;
+#endif
+ }
+
+ /*
+ * Collect offset and count. Make sure they make sense.
+ */
+ offset = ntohs(rpkt.offset);
+ count = ntohs(rpkt.count);
+
+ if (debug >= 3) {
+ int shouldbesize;
+ u_long key;
+ u_long *lpkt;
+ int maclen;
+
+ /*
+ * Usually we ignore authentication, but for debugging purposes
+ * we watch it here.
+ */
+ shouldbesize = CTL_HEADER_LEN + count;
+
+ /* round to 8 octet boundary */
+ shouldbesize = (shouldbesize + 7) & ~7;
+
+ if (n & 0x3) {
+ printf("Packet not padded, size = %d\n", n);
+ } if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
+ printf(
+"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
+ n, shouldbesize, maclen);
+ lpkt = (u_long *)&rpkt;
+ printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
+ key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
+ printf("Authenticated with keyid %lu\n", key);
+ if (key != 0 && key != info_auth_keyid) {
+ printf("We don't know that key\n");
+ } else {
+ if (authdecrypt(key, (U_LONG *)&rpkt,
+ (n - maclen))) {
+ printf("Auth okay!\n");
+ } else {
+ printf("Auth failed!\n");
+ }
+ }
+ }
+ }
+
+ if (debug >= 2)
+ printf("Got packet, size = %d\n", n);
+ if (count > (u_short)(n-CTL_HEADER_LEN)) {
+ if (debug)
+ printf(
+ "Received count of %d octets, data in packet is %d\n",
+ count, n-CTL_HEADER_LEN);
+ goto again;
+ }
+ if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received count of 0 in non-final fragment\n");
+ goto again;
+ }
+ if (offset + count > sizeof(pktdata)) {
+ if (debug)
+ printf("Offset %d, count %d, too big for buffer\n",
+ offset, count);
+ return ERR_TOOMUCH;
+ }
+ if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received second last fragment packet\n");
+ goto again;
+ }
+
+ /*
+ * So far, so good. Record this fragment, making sure it doesn't
+ * overlap anything.
+ */
+ if (debug >= 2)
+ printf("Packet okay\n");;
+
+ if (numfrags == MAXFRAGS) {
+ if (debug)
+ printf("Number of fragments exceeds maximum\n");
+ return ERR_TOOMUCH;
+ }
+
+ for (n = 0; n < numfrags; n++) {
+ if (offset == offsets[n])
+ goto again; /* duplicate */
+ if (offset < offsets[n])
+ break;
+ }
+
+ if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
+ goto overlap;
+ if (n < numfrags && (u_short)(offset + count) > offsets[n])
+ goto overlap;
+
+ {
+ register int i;
+
+ for (i = numfrags; i > n; i--) {
+ offsets[i] = offsets[i-1];
+ counts[i] = counts[i-1];
+ }
+ }
+ offsets[n] = offset;
+ counts[n] = count;
+ numfrags++;
+
+ /*
+ * Got that stuffed in right. Figure out if this was the last.
+ * Record status info out of the last packet.
+ */
+ if (!CTL_ISMORE(rpkt.r_m_e_op)) {
+ seenlastfrag = 1;
+ if (rstatus != 0)
+ *rstatus = ntohs(rpkt.status);
+ }
+
+ /*
+ * Copy the data into the data buffer.
+ */
+ memmove((char *)pktdata + offset, (char *)rpkt.data, count);
+
+ /*
+ * If we've seen the last fragment, look for holes in the sequence.
+ * If there aren't any, we're done.
+ */
+ if (seenlastfrag && offsets[0] == 0) {
+ for (n = 1; n < numfrags; n++) {
+ if (offsets[n-1] + counts[n-1] != offsets[n])
+ break;
+ }
+ if (n == numfrags) {
+ *rsize = offsets[numfrags-1] + counts[numfrags-1];
+ return 0;
+ }
+ }
+ goto again;
+
+overlap:
+ /*
+ * Print debugging message about overlapping fragments
+ */
+ if (debug)
+ printf("Overlapping fragments returned in response\n");
+ goto again;
+}
+
+
+/*
+ * sendrequest - format and send a request packet
+ */
+static int
+sendrequest(opcode, associd, auth, qsize, qdata)
+ int opcode;
+ int associd;
+ int auth;
+ int qsize;
+ char *qdata;
+{
+ struct ntp_control qpkt;
+ int pktsize;
+
+ /*
+ * Check to make sure the data will fit in one packet
+ */
+ if (qsize > CTL_MAX_DATA_LEN) {
+ (void) fprintf(stderr,
+ "***Internal error! qsize (%d) too large\n",
+ qsize);
+ return 1;
+ }
+
+ /*
+ * Fill in the packet
+ */
+ qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
+ qpkt.r_m_e_op = (u_char)opcode & CTL_OP_MASK;
+ qpkt.sequence = htons(sequence);
+ qpkt.status = 0;
+ qpkt.associd = htons((u_short)associd);
+ qpkt.offset = 0;
+ qpkt.count = htons((u_short)qsize);
+
+ /*
+ * If we have data, copy it in and pad it out to a 64
+ * bit boundary.
+ */
+ if (qsize > 0) {
+ memmove((char *)qpkt.data, qdata, qsize);
+ pktsize = qsize + CTL_HEADER_LEN;
+ while (pktsize & (sizeof(u_long) - 1)) {
+ qpkt.data[qsize++] = 0;
+ pktsize++;
+ }
+ } else {
+ pktsize = CTL_HEADER_LEN;
+ }
+
+ /*
+ * If it isn't authenticated we can just send it. Otherwise
+ * we're going to have to think about it a little.
+ */
+ if (!auth && !always_auth) {
+ return sendpkt((char *)&qpkt, pktsize);
+ } else {
+ char *pass;
+
+ /*
+ * Pad out packet to a multiple of 8 octets to be sure
+ * receiver can handle it.
+ */
+ while (pktsize & 7) {
+ qpkt.data[qsize++] = 0;
+ pktsize++;
+ }
+
+ /*
+ * Get the keyid and the password if we don't have one.
+ */
+ if (info_auth_keyid == -1) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == -1) {
+ (void) fprintf(stderr,
+ "Keyid must be defined, request not sent\n");
+ return 1;
+ }
+ }
+ if (!auth_havekey(info_auth_keyid)) {
+ pass = getpass("Password: ");
+ if (*pass != '\0')
+ authusekey(info_auth_keyid,
+ info_auth_keytype, pass);
+ }
+ if (auth_havekey(info_auth_keyid)) {
+ int maclen;
+
+ /*
+ * Stick the keyid in the packet where
+ * cp currently points. Cp should be aligned
+ * properly. Then do the encryptions.
+ */
+ *(u_long *)(&qpkt.data[qsize]) = htonl(info_auth_keyid);
+ maclen = authencrypt(info_auth_keyid, (U_LONG *)&qpkt,
+ pktsize);
+ return sendpkt((char *)&qpkt, pktsize + maclen);
+ } else {
+ (void) fprintf(stderr,
+ "No password, request not sent\n");
+ return 1;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * doquery - send a request and process the response
+ */
+int
+doquery(opcode, associd, auth, qsize, qdata, rstatus, rsize, rdata)
+ int opcode;
+ int associd;
+ int auth;
+ int qsize;
+ char *qdata;
+ u_short *rstatus;
+ int *rsize;
+ char **rdata;
+{
+ int res;
+ int done;
+
+ /*
+ * Check to make sure host is open
+ */
+ if (!havehost) {
+ (void) fprintf(stderr, "***No host open, use `host' command\n");
+ return -1;
+ }
+
+ done = 0;
+ sequence++;
+
+again:
+ /*
+ * send a request
+ */
+ res = sendrequest(opcode, associd, auth, qsize, qdata);
+ if (res != 0)
+ return res;
+
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
+
+ if (res > 0) {
+ if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
+ if (res == ERR_INCOMPLETE) {
+ /*
+ * better bump the sequence so we don't
+ * get confused about differing fragments.
+ */
+ sequence++;
+ }
+ done = 1;
+ goto again;
+ }
+ switch(res) {
+ case CERR_BADFMT:
+ (void) fprintf(stderr,
+ "***Server reports a bad format request packet\n");
+ break;
+ case CERR_PERMISSION:
+ (void) fprintf(stderr,
+ "***Server disallowed request (authentication?)\n");
+ break;
+ case CERR_BADOP:
+ (void) fprintf(stderr,
+ "***Server reports a bad opcode in request\n");
+ break;
+ case CERR_BADASSOC:
+ (void) fprintf(stderr,
+ "***Association ID %d unknown to server\n",associd);
+ break;
+ case CERR_UNKNOWNVAR:
+ (void) fprintf(stderr,
+ "***A request variable was unknown to the server\n");
+ break;
+ case CERR_BADVALUE:
+ (void) fprintf(stderr,
+ "***Server indicates a request variable was bad\n");
+ break;
+ case ERR_UNSPEC:
+ (void) fprintf(stderr,
+ "***Server returned an unspecified error\n");
+ break;
+ case ERR_TIMEOUT:
+ (void) fprintf(stderr, "***Request timed out\n");
+ break;
+ case ERR_INCOMPLETE:
+ (void) fprintf(stderr,
+ "***Response from server was incomplete\n");
+ break;
+ case ERR_TOOMUCH:
+ (void) fprintf(stderr,
+ "***Buffer size exceeded for returned data\n");
+ break;
+ default:
+ (void) fprintf(stderr,
+ "***Server returns unknown error code %d\n", res);
+ break;
+ }
+ }
+ return res;
+}
+
+
+/*
+ * getcmds - read commands from the standard input and execute them
+ */
+static void
+getcmds()
+{
+ char line[MAXLINE];
+
+ for (;;) {
+ if (interactive) {
+ (void) fputs(prompt, stderr);
+ (void) fflush(stderr);
+ }
+
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return;
+
+ docmd(line);
+ }
+}
+
+
+/*
+ * abortcmd - catch interrupts and abort the current command
+ */
+static RETSIGTYPE
+abortcmd(sig)
+int sig;
+{
+ if (current_output == stdout)
+ (void) fflush(stdout);
+ putc('\n', stderr);
+ (void) fflush(stderr);
+ if (jump) longjmp(interrupt_buf, 1);
+}
+
+
+/*
+ * docmd - decode the command line and execute a command
+ */
+static void
+docmd(cmdline)
+ char *cmdline;
+{
+ char *tokens[1+MAXARGS+2];
+ struct parse pcmd;
+ int ntok;
+ static int i;
+ struct xcmd *xcmd;
+
+ /*
+ * Tokenize the command line. If nothing on it, return.
+ */
+ tokenize(cmdline, tokens, &ntok);
+ if (ntok == 0)
+ return;
+
+ /*
+ * Find the appropriate command description.
+ */
+ i = findcmd(tokens[0], builtins, opcmds, &xcmd);
+ if (i == 0) {
+ (void) fprintf(stderr, "***Command `%s' unknown\n",
+ tokens[0]);
+ return;
+ } else if (i >= 2) {
+ (void) fprintf(stderr, "***Command `%s' ambiguous\n",
+ tokens[0]);
+ return;
+ }
+
+ /*
+ * Save the keyword, then walk through the arguments, interpreting
+ * as we go.
+ */
+ pcmd.keyword = tokens[0];
+ pcmd.nargs = 0;
+ for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
+ if ((i+1) >= ntok) {
+ if (!(xcmd->arg[i] & OPT)) {
+ printusage(xcmd, stderr);
+ return;
+ }
+ break;
+ }
+ if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
+ break;
+ if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
+ return;
+ pcmd.nargs++;
+ }
+
+ i++;
+ if (i < ntok && *tokens[i] == '>') {
+ char *fname;
+
+ if (*(tokens[i]+1) != '\0')
+ fname = tokens[i]+1;
+ else if ((i+1) < ntok)
+ fname = tokens[i+1];
+ else {
+ (void) fprintf(stderr, "***No file for redirect\n");
+ return;
+ }
+
+ current_output = fopen(fname, "w");
+ if (current_output == NULL) {
+ (void) fprintf(stderr, "***Error opening %s: ", fname);
+ perror("");
+ return;
+ }
+ i = 1; /* flag we need a close */
+ } else {
+ current_output = stdout;
+ i = 0; /* flag no close */
+ }
+
+ if (interactive && setjmp(interrupt_buf)) {
+ return;
+ } else {
+ jump++;
+ (xcmd->handler)(&pcmd, current_output);
+ if (i) (void) fclose(current_output);
+ }
+}
+
+
+/*
+ * tokenize - turn a command line into tokens
+ */
+static void
+tokenize(line, tokens, ntok)
+ char *line;
+ char **tokens;
+ int *ntok;
+{
+ register char *cp;
+ register char *sp;
+ static char tspace[MAXLINE];
+
+ sp = tspace;
+ cp = line;
+ for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
+ tokens[*ntok] = sp;
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ do {
+ *sp++ = *cp++;
+ } while (!ISSPACE(*cp) && !ISEOL(*cp));
+
+ *sp++ = '\0';
+ }
+}
+
+
+
+/*
+ * findcmd - find a command in a command description table
+ */
+static int
+findcmd(str, clist1, clist2, cmd)
+ register char *str;
+ struct xcmd *clist1;
+ struct xcmd *clist2;
+ struct xcmd **cmd;
+{
+ register struct xcmd *cl;
+ register int clen;
+ int nmatch;
+ struct xcmd *nearmatch = NULL;
+ struct xcmd *clist;
+
+ clen = strlen(str);
+ nmatch = 0;
+ if (clist1 != 0)
+ clist = clist1;
+ else if (clist2 != 0)
+ clist = clist2;
+ else
+ return 0;
+
+again:
+ for (cl = clist; cl->keyword != 0; cl++) {
+ /* do a first character check, for efficiency */
+ if (*str != *(cl->keyword))
+ continue;
+ if (strncmp(str, cl->keyword, clen) == 0) {
+ /*
+ * Could be extact match, could be approximate.
+ * Is exact if the length of the keyword is the
+ * same as the str.
+ */
+ if (*((cl->keyword) + clen) == '\0') {
+ *cmd = cl;
+ return 1;
+ }
+ nmatch++;
+ nearmatch = cl;
+ }
+ }
+
+ /*
+ * See if there is more to do. If so, go again. Sorry about the
+ * goto, too much looking at BSD sources...
+ */
+ if (clist == clist1 && clist2 != 0) {
+ clist = clist2;
+ goto again;
+ }
+
+ /*
+ * If we got extactly 1 near match, use it, else return number
+ * of matches.
+ */
+ if (nmatch == 1) {
+ *cmd = nearmatch;
+ return 1;
+ }
+ return nmatch;
+}
+
+
+/*
+ * getarg - interpret an argument token
+ */
+static int
+getarg(str, code, argp)
+ char *str;
+ int code;
+ arg_v *argp;
+{
+ int isneg;
+ char *cp, *np;
+ static char *digits = "0123456789";
+
+ switch (code & ~OPT) {
+ case STR:
+ argp->string = str;
+ break;
+ case ADD:
+ if (!getnetnum(str, &(argp->netnum), (char *)0)) {
+ return 0;
+ }
+ break;
+ case INT:
+ case UINT:
+ isneg = 0;
+ np = str;
+ if (*np == '&') {
+ np++;
+ isneg = atoi(np);
+ if (isneg <= 0) {
+ (void) fprintf(stderr,
+ "***Association value `%s' invalid/undecodable\n", str);
+ return 0;
+ }
+ if (isneg > numassoc) {
+ (void) fprintf(stderr,
+ "***Association for `%s' unknown (max &%d)\n",
+ str, numassoc);
+ return 0;
+ }
+ argp->uval = assoc_cache[isneg-1].assid;
+ break;
+ }
+
+ if (*np == '-') {
+ np++;
+ isneg = 1;
+ }
+
+ argp->uval = 0;
+ do {
+ cp = strchr(digits, *np);
+ if (cp == NULL) {
+ (void) fprintf(stderr,
+ "***Illegal integer value %s\n", str);
+ return 0;
+ }
+ argp->uval *= 10;
+ argp->uval += (cp - digits);
+ } while (*(++np) != '\0');
+
+ if (isneg) {
+ if ((code & ~OPT) == UINT) {
+ (void) fprintf(stderr,
+ "***Value %s should be unsigned\n", str);
+ return 0;
+ }
+ argp->ival = -argp->ival;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+/*
+ * getnetnum - given a host name, return its net number
+ * and (optional) full name
+ */
+int
+getnetnum(host, num, fullhost)
+ char *host;
+ u_long *num;
+ char *fullhost;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ if (fullhost != 0) {
+ (void) sprintf(fullhost, "%lu.%lu.%lu.%lu",
+ (u_long)((htonl(*num) >> 24) & 0xff),
+ (u_long)((htonl(*num) >> 16) & 0xff),
+ (u_long)((htonl(*num) >> 8) & 0xff),
+ (u_long)(htonl(*num) & 0xff));
+ }
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(u_long));
+ if (fullhost != 0)
+ (void) strcpy(fullhost, hp->h_name);
+ return 1;
+ } else {
+ (void) fprintf(stderr, "***Can't find host %s\n", host);
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * nntohost - convert network number to host name. This routine enforces
+ * the showhostnames setting.
+ */
+char *
+nntohost(netnum)
+ u_long netnum;
+{
+ if (!showhostnames)
+ return numtoa(netnum);
+ if ((ntohl(netnum) & REFCLOCK_MASK) == REFCLOCK_ADDR)
+ return refnumtoa(netnum);
+ return numtohost(netnum);
+}
+
+
+/*
+ * rtdatetolfp - decode an RT-11 date into an l_fp
+ */
+static int
+rtdatetolfp(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ register char *cp;
+ register int i;
+ struct calendar cal;
+ char buf[4];
+ static char *months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ cal.yearday = 0;
+
+ /*
+ * An RT-11 date looks like:
+ *
+ * d[d]-Mth-y[y] hh:mm:ss
+ */
+ cp = str;
+ if (!isdigit(*cp)) {
+ if (*cp == '-') {
+ /*
+ * Catch special case
+ */
+ L_CLR(lfp);
+ return 1;
+ }
+ return 0;
+ }
+
+ cal.monthday = *cp++ - '0'; /* ascii dependent */
+ if (isdigit(*cp)) {
+ cal.monthday = (cal.monthday << 3) + (cal.monthday << 1);
+ cal.monthday += *cp++ - '0';
+ }
+
+ if (*cp++ != '-')
+ return 0;
+
+ for (i = 0; i < 3; i++)
+ buf[i] = *cp++;
+ buf[3] = '\0';
+
+ for (i = 0; i < 12; i++)
+ if (STREQ(buf, months[i]))
+ break;
+ if (i == 12)
+ return 0;
+ cal.month = i + 1;
+
+ if (*cp++ != '-')
+ return 0;
+
+ if (!isdigit(*cp))
+ return 0;
+ cal.year = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.year = (cal.year << 3) + (cal.year << 1);
+ cal.year += *cp++ - '0';
+ }
+
+ /*
+ * Catch special case. If cal.year == 0 this is a zero timestamp.
+ */
+ if (cal.year == 0) {
+ L_CLR(lfp);
+ return 1;
+ }
+
+ if (*cp++ != ' ' || !isdigit(*cp))
+ return 0;
+ cal.hour = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.hour = (cal.hour << 3) + (cal.hour << 1);
+ cal.hour += *cp++ - '0';
+ }
+
+ if (*cp++ != ':' || !isdigit(*cp))
+ return 0;
+ cal.minute = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.minute = (cal.minute << 3) + (cal.minute << 1);
+ cal.minute += *cp++ - '0';
+ }
+
+ if (*cp++ != ':' || !isdigit(*cp))
+ return 0;
+ cal.second = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.second = (cal.second << 3) + (cal.second << 1);
+ cal.second += *cp++ - '0';
+ }
+
+ cal.year += 1900;
+ lfp->l_ui = caltontp(&cal);
+ lfp->l_uf = 0;
+ return 1;
+}
+
+
+/*
+ * decodets - decode a timestamp into an l_fp format number, with
+ * consideration of fuzzball formats.
+ */
+int
+decodets(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ /*
+ * If it starts with a 0x, decode as hex.
+ */
+ if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
+ return hextolfp(str+2, lfp);
+
+ /*
+ * If it starts with a '"', try it as an RT-11 date.
+ */
+ if (*str == '"') {
+ register char *cp = str+1;
+ register char *bp;
+ char buf[30];
+
+ bp = buf;
+ while (*cp != '"' && *cp != '\0' && bp < &buf[29])
+ *bp++ = *cp++;
+ *bp = '\0';
+ return rtdatetolfp(buf, lfp);
+ }
+
+ /*
+ * Might still be hex. Check out the first character. Talk
+ * about heuristics!
+ */
+ if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
+ return hextolfp(str, lfp);
+
+ /*
+ * Try it as a decimal. If this fails, try as an unquoted
+ * RT-11 date. This code should go away eventually.
+ */
+ if (atolfp(str, lfp))
+ return 1;
+ return rtdatetolfp(str, lfp);
+}
+
+
+/*
+ * decodetime - decode a time value. It should be in milliseconds
+ */
+int
+decodetime(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ return mstolfp(str, lfp);
+}
+
+
+/*
+ * decodeint - decode an integer
+ */
+int
+decodeint(str, val)
+ char *str;
+ long *val;
+{
+ if (*str == '0') {
+ if (*(str+1) == 'x' || *(str+1) == 'X')
+ return hextoint(str+2, (u_long *)&val);
+ return octtoint(str, (u_long *)&val);
+ }
+ return atoint(str, val);
+}
+
+
+/*
+ * decodeuint - decode an unsigned integer
+ */
+int
+decodeuint(str, val)
+ char *str;
+ u_long *val;
+{
+ if (*str == '0') {
+ if (*(str+1) == 'x' || *(str+1) == 'X')
+ return hextoint(str+2, val);
+ return octtoint(str, val);
+ }
+ return atouint(str, val);
+}
+
+
+/*
+ * decodearr - decode an array of time values
+ */
+static int
+decodearr(str, narr, lfparr)
+ char *str;
+ int *narr;
+ l_fp *lfparr;
+{
+ register char *cp, *bp;
+ register l_fp *lfp;
+ char buf[60];
+
+ lfp = lfparr;
+ cp = str;
+ *narr = 0;
+
+ while (*narr < 8) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ bp = buf;
+ while (!isspace(*cp) && *cp != '\0')
+ *bp++ = *cp++;
+ *bp++ = '\0';
+
+ if (!decodetime(buf, lfp))
+ return 0;
+ (*narr)++;
+ lfp++;
+ }
+ return 1;
+}
+
+
+
+
+/*
+ * getcode - return string corresponding to code
+ */
+static char *
+getcode(code, codetab)
+ int code;
+ struct codestring *codetab;
+{
+ static char buf[30];
+
+ while (codetab->code != -1) {
+ if (codetab->code == code)
+ return codetab->string;
+ codetab++;
+ }
+ (void) sprintf(buf, "%s_%d", codetab->string, code);
+ return buf;
+}
+
+
+/*
+ * Finally, the built in command handlers
+ */
+
+/*
+ * help - tell about commands, or details of a particular command
+ */
+static void
+help(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int n;
+ struct xcmd *xcp;
+ char *cmd;
+ char *cmdsort[100];
+ int length[100];
+ int maxlength;
+ int numperline;
+ static char *spaces = " "; /* 20 spaces */
+
+ if (pcmd->nargs == 0) {
+ n = 0;
+ for (xcp = builtins; xcp->keyword != 0; xcp++) {
+ if (*(xcp->keyword) != '?')
+ cmdsort[n++] = xcp->keyword;
+ }
+ for (xcp = opcmds; xcp->keyword != 0; xcp++)
+ cmdsort[n++] = xcp->keyword;
+
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)cmdsort, n, sizeof(char *), helpsort);
+#else
+ qsort((char *)cmdsort, n, sizeof(char *), helpsort);
+#endif /* sgi || bsdi */
+
+ maxlength = 0;
+ for (i = 0; i < n; i++) {
+ length[i] = strlen(cmdsort[i]);
+ if (length[i] > maxlength)
+ maxlength = length[i];
+ }
+ maxlength++;
+ numperline = 76 / maxlength;
+
+ (void) fprintf(fp, "Commands available:\n");
+ for (i = 0; i < n; i++) {
+ if ((i % numperline) == (numperline-1)
+ || i == (n-1))
+ (void) fprintf(fp, "%s\n", cmdsort[i]);
+ else
+ (void) fprintf(fp, "%s%s", cmdsort[i],
+ spaces+20-maxlength+length[i]);
+ }
+ } else {
+ cmd = pcmd->argval[0].string;
+ n = findcmd(cmd, builtins, opcmds, &xcp);
+ if (n == 0) {
+ (void) fprintf(stderr,
+ "Command `%s' is unknown\n", cmd);
+ return;
+ } else if (n >= 2) {
+ (void) fprintf(stderr,
+ "Command `%s' is ambiguous\n", cmd);
+ return;
+ }
+ (void) fprintf(fp, "function: %s\n", xcp->comment);
+ printusage(xcp, fp);
+ }
+}
+
+
+/*
+ * helpsort - do hostname qsort comparisons
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+helpsort(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const char **name1 = (const char **)t1;
+ const char **name2 = (const char **)t2;
+#else
+helpsort(name1, name2)
+ char **name1;
+ char **name2;
+{
+#endif /* sgi || bsdi */
+ return strcmp(*name1, *name2);
+}
+
+
+/*
+ * printusage - print usage information for a command
+ */
+static void
+printusage(xcp, fp)
+ struct xcmd *xcp;
+ FILE *fp;
+{
+ register int i;
+
+ (void) fprintf(fp, "usage: %s", xcp->keyword);
+ for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
+ if (xcp->arg[i] & OPT)
+ (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
+ else
+ (void) fprintf(fp, " %s", xcp->desc[i]);
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+/*
+ * timeout - set time out time
+ */
+static void
+timeout(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int val;
+
+ if (pcmd->nargs == 0) {
+ val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
+ (void) fprintf(fp, "primary timeout %d ms\n", val);
+ } else {
+ tvout.tv_sec = pcmd->argval[0].uval / 1000;
+ tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
+ * 1000;
+ }
+}
+
+
+/*
+ * delay - set delay for auth requests
+ */
+static void
+delay(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int isneg;
+ u_long val;
+
+ if (pcmd->nargs == 0) {
+ val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
+ (void) fprintf(fp, "delay %lu ms\n", val);
+ } else {
+ if (pcmd->argval[0].ival < 0) {
+ isneg = 1;
+ val = (u_long)(-pcmd->argval[0].ival);
+ } else {
+ isneg = 0;
+ val = (u_long)pcmd->argval[0].ival;
+ }
+
+ delay_time.l_ui = val / 1000;
+ val %= 1000;
+ delay_time.l_uf = val * 4294967; /* 2**32/1000 */
+
+ if (isneg)
+ L_NEG(&delay_time);
+ }
+}
+
+
+/*
+ * host - set the host we are dealing with.
+ */
+static void
+host(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (havehost)
+ (void) fprintf(fp, "current host is %s\n", currenthost);
+ else
+ (void) fprintf(fp, "no current host\n");
+ } else if (openhost(pcmd->argval[0].string)) {
+ (void) fprintf(fp, "current host set to %s\n", currenthost);
+ numassoc = 0;
+ } else {
+ if (havehost)
+ (void) fprintf(fp,
+ "current host remains %s\n", currenthost);
+ else
+ (void) fprintf(fp, "still no current host\n");
+ }
+}
+
+
+/*
+ * poll - do one (or more) polls of the host via NTP
+ */
+/*ARGSUSED*/
+static void
+ntp_poll(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ (void) fprintf(fp, "poll not implemented yet\n");
+}
+
+
+/*
+ * keyid - get a keyid to use for authenticating requests
+ */
+static void
+keyid(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (info_auth_keyid == -1)
+ (void) fprintf(fp, "no keyid defined\n");
+ else
+ (void) fprintf(fp, "keyid is %lu\n", info_auth_keyid);
+ } else {
+ info_auth_keyid = pcmd->argval[0].uval;
+ }
+}
+
+/*
+ * keytype - get type of key to use for authenticating requests
+ */
+static void
+keytype(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0)
+ fprintf(fp, "keytype is %s",
+ (info_auth_keytype == KEY_TYPE_MD5) ? "md5" : "des");
+ else
+ switch (*(pcmd->argval[0].string)) {
+ case 'm':
+ case 'M':
+ info_auth_keytype = KEY_TYPE_MD5;
+ break;
+
+ case 'd':
+ case 'D':
+ info_auth_keytype = KEY_TYPE_DES;
+ break;
+
+ default:
+ fprintf(fp, "keytype must be 'md5' or 'des'\n");
+ }
+}
+
+
+
+/*
+ * passwd - get an authentication key
+ */
+/*ARGSUSED*/
+static void
+passwd(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *pass;
+
+ if (info_auth_keyid == -1) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == -1) {
+ (void)fprintf(fp, "Keyid must be defined\n");
+ return;
+ }
+ }
+ pass = getpass("Password: ");
+ if (*pass == '\0')
+ (void) fprintf(fp, "Password unchanged\n");
+ else
+ authusekey(info_auth_keyid, info_auth_keytype, pass);
+}
+
+
+/*
+ * hostnames - set the showhostnames flag
+ */
+static void
+hostnames(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (showhostnames)
+ (void) fprintf(fp, "hostnames being shown\n");
+ else
+ (void) fprintf(fp, "hostnames not being shown\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes"))
+ showhostnames = 1;
+ else if (STREQ(pcmd->argval[0].string, "no"))
+ showhostnames = 0;
+ else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+
+/*
+ * setdebug - set/change debugging level
+ */
+static void
+setdebug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "debug level is %d\n", debug);
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ debug = 0;
+ } else if (STREQ(pcmd->argval[0].string, "more")) {
+ debug++;
+ } else if (STREQ(pcmd->argval[0].string, "less")) {
+ debug--;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "debug level set to %d\n", debug);
+}
+
+
+/*
+ * quit - stop this nonsense
+ */
+/*ARGSUSED*/
+static void
+quit(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (havehost)
+ (void) close(sockfd); /* cleanliness next to godliness */
+ exit(0);
+}
+
+
+/*
+ * version - print the current version number
+ */
+/*ARGSUSED*/
+static void
+version(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern char *Version;
+
+ (void) fprintf(fp, "%s\n", Version);
+}
+
+
+/*
+ * raw - set raw mode output
+ */
+/*ARGSUSED*/
+static void
+raw(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ rawmode = 1;
+ (void) fprintf(fp, "Output set to raw\n");
+}
+
+
+/*
+ * cooked - set cooked mode output
+ */
+/*ARGSUSED*/
+static void
+cooked(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ rawmode = 0;
+ (void) fprintf(fp, "Output set to cooked\n");
+ return;
+}
+
+
+/*
+ * authenticate - always authenticate requests to this host
+ */
+static void
+authenticate(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (always_auth) {
+ (void) fprintf(fp,
+ "authenticated requests being sent\n");
+ } else
+ (void) fprintf(fp,
+ "unauthenticated requests being sent\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes")) {
+ always_auth = 1;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ always_auth = 0;
+ } else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+/*
+ * ntpversion - choose the NTP version to use
+ */
+static void
+ntpversion(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp,
+ "NTP version being claimed is %d\n", pktversion);
+ } else {
+ if (pcmd->argval[0].uval <= NTP_OLDVERSION
+ || pcmd->argval[0].uval > NTP_VERSION) {
+ (void) fprintf(stderr, "versions %d to %d, please\n",
+ NTP_OLDVERSION+1, NTP_VERSION);
+ } else {
+ pktversion = pcmd->argval[0].uval;
+ }
+ }
+}
+
+
+/*
+ * warning - print a warning message
+ */
+static void
+warning(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, st1, st2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+}
+
+
+/*
+ * error - print a message and exit
+ */
+static void
+error(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ warning(fmt, st1, st2);
+ exit(1);
+}
+
+/*
+ * getkeyid - prompt the user for a keyid to use
+ */
+static u_long
+getkeyid(prompt)
+char *prompt;
+{
+ register char *p;
+ register c;
+ FILE *fi;
+ char pbuf[20];
+
+ if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ fprintf(stderr, "%s", prompt); fflush(stderr);
+ for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+ if (p < &pbuf[18])
+ *p++ = c;
+ }
+ *p = '\0';
+ if (fi != stdin)
+ fclose(fi);
+ if (strcmp(pbuf, "0") == 0)
+ return 0;
+
+ return (u_long) atoi(pbuf);
+}
+
+
+/*
+ * atoascii - printable-ize possibly ascii data using the character
+ * transformations cat -v uses.
+ */
+static void
+atoascii(length, data, outdata)
+ int length;
+ char *data;
+ char *outdata;
+{
+ register u_char *cp;
+ register u_char *ocp;
+ register u_char c;
+
+ if (!data)
+ {
+ *outdata = '\0';
+ return;
+ }
+
+ ocp = (u_char *)outdata;
+ for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
+ c = *cp;
+ if (c == '\0')
+ break;
+ if (c == '\0')
+ break;
+ if (c > 0177) {
+ *ocp++ = 'M';
+ *ocp++ = '-';
+ c &= 0177;
+ }
+
+ if (c < ' ') {
+ *ocp++ = '^';
+ *ocp++ = c + '@';
+ } else if (c == 0177) {
+ *ocp++ = '^';
+ *ocp++ = '?';
+ } else {
+ *ocp++ = c;
+ }
+ if (ocp >= ((u_char *)outdata + length - 4))
+ break;
+ }
+ *ocp++ = '\0';
+}
+
+
+
+/*
+ * makeascii - print possibly ascii data using the character
+ * transformations that cat -v uses.
+ */
+static void
+makeascii(length, data, fp)
+ int length;
+ char *data;
+ FILE *fp;
+{
+ register u_char *cp;
+ register int c;
+
+ for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
+ c = (int)*cp;
+ if (c > 0177) {
+ putc('M', fp);
+ putc('-', fp);
+ c &= 0177;
+ }
+
+ if (c < ' ') {
+ putc('^', fp);
+ putc(c+'@', fp);
+ } else if (c == 0177) {
+ putc('^', fp);
+ putc('?', fp);
+ } else {
+ putc(c, fp);
+ }
+ }
+}
+
+
+/*
+ * asciize - same thing as makeascii except add a newline
+ */
+void
+asciize(length, data, fp)
+ int length;
+ char *data;
+ FILE *fp;
+{
+ makeascii(length, data, fp);
+ putc('\n', fp);
+}
+
+
+/*
+ * Some circular buffer space
+ */
+#define CBLEN 80
+#define NUMCB 6
+
+char circ_buf[NUMCB][CBLEN];
+int nextcb = 0;
+
+
+/*
+ * getevents - return a descriptive string for the event count
+ */
+static char *
+getevents(cnt)
+ int cnt;
+{
+ static char buf[20];
+
+ if (cnt == 0)
+ return "no events";
+ (void) sprintf(buf, "%d event%s", cnt, (cnt==1) ? "" : "s");
+ return buf;
+}
+
+
+/*
+ * statustoa - return a descriptive string for a peer status
+ */
+static char *
+statustoa(type, st)
+ int type;
+ int st;
+{
+ char *cb;
+ u_char pst;
+
+ cb = &circ_buf[nextcb][0];
+ if (++nextcb >= NUMCB)
+ nextcb = 0;
+
+ switch (type) {
+ case TYPE_SYS:
+ (void)strcpy(cb, getcode(CTL_SYS_LI(st), leap_codes));
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_SYS_SOURCE(st) & ~CTL_SST_TS_PPS, sync_codes));
+ if (CTL_SYS_SOURCE(st) & CTL_SST_TS_PPS)
+ (void)strcat(cb, "/PPS");
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getevents(CTL_SYS_NEVNT(st)));
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_SYS_EVENT(st), sys_codes));
+ break;
+
+ case TYPE_PEER:
+ /*
+ * Handcraft the bits
+ */
+ pst = CTL_PEER_STATVAL(st);
+ if (!(pst & CTL_PST_REACH)) {
+ (void)strcpy(cb, "unreach");
+ } else {
+ (void)strcpy(cb, "reach");
+#if 0
+ if (!(pst & CTL_PST_DISP)) {
+ (void)strcat(cb, ", hi_disp");
+ } else {
+ if (pst & CTL_PST_SANE) {
+ if ((pst & 0x3) == CTL_PST_SEL_REJECT)
+ (void)strcat(cb, ", sane");
+ } else {
+ (void)strcat(cb, ", insane");
+ }
+ }
+#endif
+ }
+ if (pst & CTL_PST_CONFIG)
+ (void)strcat(cb, ", conf");
+ if (pst & CTL_PST_AUTHENABLE) {
+ if (!(pst & CTL_PST_REACH) || (pst & CTL_PST_AUTHENTIC))
+ (void)strcat(cb, ", auth");
+ else
+ (void)strcat(cb, ", unauth");
+ }
+
+ /*
+ * Now the codes
+ */
+ if ((pst & 0x7) != CTL_PST_SEL_REJECT) {
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(pst & 0x7, select_codes));
+ }
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getevents(CTL_PEER_NEVNT(st)));
+ if (CTL_PEER_EVENT(st) != EVNT_UNSPEC) {
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_PEER_EVENT(st),
+ peer_codes));
+ }
+ break;
+
+ case TYPE_CLOCK:
+ (void)strcpy(cb, getcode(((st)>>8) & 0xff, clock_codes));
+ (void)strcat(cb, ", last_");
+ (void)strcat(cb, getcode((st) & 0xff, clock_codes));
+ break;
+ }
+ return cb;
+}
+
+
+/*
+ * nextvar - find the next variable in the buffer
+ */
+int
+nextvar(datalen, datap, vname, vvalue)
+ int *datalen;
+ char **datap;
+ char **vname;
+ char **vvalue;
+{
+ register char *cp;
+ register char *np;
+ register char *cpend;
+ int quoted = 0;
+ static char name[MAXVARLEN];
+ static char value[MAXVALLEN];
+
+ cp = *datap;
+ cpend = cp + *datalen;
+
+ /*
+ * Space past commas and white space
+ */
+ while (cp < cpend && (*cp == ',' || isspace(*cp)))
+ cp++;
+ if (cp == cpend)
+ return 0;
+
+ /*
+ * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace
+ * over any white space and terminate it.
+ */
+ np = name;
+ while (cp < cpend && *cp != ',' && *cp != '='
+ && *cp != '\r' && *cp != '\n')
+ *np++ = *cp++;
+ while (isspace(*(np-1)))
+ np--;
+ *np = '\0';
+ *vname = name;
+
+ /*
+ * Check if we hit the end of the buffer or a ','. If so we are done.
+ */
+ if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
+ if (cp != cpend)
+ cp++;
+ *datap = cp;
+ *datalen = cpend - cp;
+ *vvalue = (char *)0;
+ return 1;
+ }
+
+ /*
+ * So far, so good. Copy out the value
+ */
+ cp++; /* past '=' */
+ while (cp < cpend && (isspace(*cp) && *cp != '\r' && *cp != '\n'))
+ cp++;
+ np = value;
+ while (cp < cpend && ((*cp != ',') || quoted))
+ {
+ quoted ^= ((*np++ = *cp++) == '"');
+ }
+
+ while (np > value && isspace(*(np-1)))
+ np--;
+ *np = '\0';
+
+ /*
+ * Return this. All done.
+ */
+ if (cp != cpend)
+ cp++;
+ *datap = cp;
+ *datalen = cpend - cp;
+ *vvalue = value;
+ return 1;
+}
+
+
+/*
+ * findvar - see if this variable is known to us
+ */
+int
+findvar(varname, varlist)
+ char *varname;
+ struct ctl_var *varlist;
+{
+ register char *np;
+ register struct ctl_var *vl;
+
+ vl = varlist;
+ np = varname;
+ while (vl->fmt != EOV) {
+ if (vl->fmt != PADDING && STREQ(np, vl->text))
+ return vl->code;
+ vl++;
+ }
+ return 0;
+}
+
+
+
+/*
+ * printvars - print variables returned in response packet
+ */
+void
+printvars(length, data, status, sttype, fp)
+ int length;
+ char *data;
+ int status;
+ int sttype;
+ FILE *fp;
+{
+ if (rawmode)
+ rawprint(sttype, length, data, status, fp);
+ else
+ cookedprint(sttype, length, data, status, fp);
+}
+
+
+/*
+ * rawprint - do a printout of the data in raw mode
+ */
+static void
+rawprint(datatype, length, data, status, fp)
+ int datatype;
+ int length;
+ char *data;
+ int status;
+ FILE *fp;
+{
+ register char *cp;
+ register char *cpend;
+
+ /*
+ * Essentially print the data as is. We reformat unprintables, though.
+ */
+ cp = data;
+ cpend = data + length;
+
+ (void) fprintf(fp, "status=%04x %s\n", status,
+ statustoa(datatype, status));
+
+ while (cp < cpend) {
+ if (*cp == '\r') {
+ /*
+ * If this is a \r and the next character is a
+ * \n, supress this, else pretty print it. Otherwise
+ * just output the character.
+ */
+ if (cp == (cpend-1) || *(cp+1) != '\n')
+ makeascii(1, cp, fp);
+ } else if (isspace(*cp) || isprint(*cp)) {
+ putc(*cp, fp);
+ } else {
+ makeascii(1, cp, fp);
+ }
+ cp++;
+ }
+}
+
+
+/*
+ * Global data used by the cooked output routines
+ */
+int out_chars; /* number of characters output */
+int out_linecount; /* number of characters output on this line */
+
+
+/*
+ * startoutput - get ready to do cooked output
+ */
+static void
+startoutput()
+{
+ out_chars = 0;
+ out_linecount = 0;
+}
+
+
+/*
+ * output - output a variable=value combination
+ */
+static void
+output(fp, name, value)
+ FILE *fp;
+ char *name;
+ char *value;
+{
+ int lenname;
+ int lenvalue;
+
+ lenname = strlen(name);
+ lenvalue = strlen(value);
+
+ if (out_chars != 0) {
+ putc(',', fp);
+ out_chars++;
+ out_linecount++;
+ if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) {
+ putc('\n', fp);
+ out_chars++;
+ out_linecount = 0;
+ } else {
+ putc(' ', fp);
+ out_chars++;
+ out_linecount++;
+ }
+ }
+
+ fputs(name, fp);
+ putc('=', fp);
+ fputs(value, fp);
+ out_chars += lenname + 1 + lenvalue;
+ out_linecount += lenname + 1 + lenvalue;
+}
+
+
+/*
+ * endoutput - terminate a block of cooked output
+ */
+static void
+endoutput(fp)
+ FILE *fp;
+{
+ if (out_chars != 0)
+ putc('\n', fp);
+}
+
+
+/*
+ * outputarr - output an array of values
+ */
+static void
+outputarr(fp, name, narr, lfp)
+ FILE *fp;
+ char *name;
+ int narr;
+ l_fp *lfp;
+{
+ register char *bp;
+ register char *cp;
+ register int i;
+ register int len;
+ char buf[256];
+
+ bp = buf;
+ /*
+ * Hack to align delay and offset values
+ */
+ if ((int)strlen(name) < 10)
+ *bp++ = ' ';
+
+ for (i = narr; i > 0; i--) {
+ if (i != narr)
+ *bp++ = ' ';
+ cp = lfptoms(lfp, 2);
+ len = strlen(cp);
+ while (len < 7) {
+ *bp++ = ' ';
+ len++;
+ }
+ while (*cp != '\0')
+ *bp++ = *cp++;
+ lfp++;
+ }
+ *bp = '\0';
+ output(fp, name, buf);
+}
+
+static char *
+tstflags(val)
+ u_long val;
+{
+ register char *cb, *s;
+ register int i;
+ register char *sep;
+
+ sep = "";
+ i = 0;
+ s = cb = &circ_buf[nextcb][0];
+ if (++nextcb >= NUMCB)
+ nextcb = 0;
+
+ sprintf(cb, "0x%lx", val);
+ cb += strlen(cb);
+ if (val <= ((1<<8)-1)) {
+ if (!val) {
+ strcat(cb, "<OK>");
+ cb += strlen(cb);
+ } else {
+ *cb++ = '<';
+ while (val) {
+ if (val & 0x1) {
+ sprintf(cb, "%s%s", sep, tstflagnames[i]);
+ sep = ";";
+ cb += strlen(cb);
+ }
+ i++;
+ val >>= 1;
+ }
+ *cb++ = '>';
+ }
+ } else {
+ *cb++ = '?';
+ }
+ *cb = '\0';
+ return s;
+}
+
+/*
+ * cookedprint - output variables in cooked mode
+ */
+static void
+cookedprint(datatype, length, data, status, fp)
+ int datatype;
+ int length;
+ char *data;
+ int status;
+ FILE *fp;
+{
+ register int varid;
+ char *name;
+ char *value;
+ int output_raw;
+ int fmt;
+ struct ctl_var *varlist;
+ l_fp lfp;
+ long ival;
+ u_long hval;
+ u_long uval;
+ l_fp lfparr[8];
+ int narr;
+
+ switch (datatype) {
+ case TYPE_PEER:
+ varlist = peer_var;
+ break;
+ case TYPE_SYS:
+ varlist = sys_var;
+ break;
+ case TYPE_CLOCK:
+ varlist = clock_var;
+ break;
+ default:
+ (void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype);
+ return;
+ break;
+ }
+
+ (void) fprintf(fp, "status=%04x %s\n", status,
+ statustoa(datatype, status));
+
+ startoutput();
+ while (nextvar(&length, &data, &name, &value)) {
+ varid = findvar(name, varlist);
+ if (varid == 0) {
+ output_raw = '*';
+ } else {
+ output_raw = 0;
+ switch((fmt = varlist[varid].fmt)) {
+ case TS:
+ if (!decodets(value, &lfp))
+ output_raw = '?';
+ else
+ output(fp, name, prettydate(&lfp));
+ break;
+ case FL:
+ case FU:
+ case FS:
+ if (!decodetime(value, &lfp))
+ output_raw = '?';
+ else {
+ switch (fmt) {
+ case FL:
+ output(fp, name,
+ lfptoms(&lfp, 3));
+ break;
+ case FU:
+ output(fp, name,
+ ulfptoms(&lfp, 2));
+ break;
+ case FS:
+ output(fp, name,
+ lfptoms(&lfp, 2));
+ break;
+ }
+ }
+ break;
+
+ case UI:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, uinttoa(uval));
+ break;
+
+ case IN:
+ if (!decodeint(value, &ival))
+ output_raw = '?';
+ else
+ output(fp, name, inttoa(ival));
+ break;
+
+ case HA:
+ case NA:
+ if (!decodenetnum(value, &hval))
+ output_raw = '?';
+ else if (fmt == HA)
+ output(fp, name, nntohost(hval));
+ else
+ output(fp, name, numtoa(hval));
+ break;
+
+ case ST:
+ output_raw = '*';
+ break;
+
+ case RF:
+ if (decodenetnum(value, &hval))
+ output(fp, name, nntohost(hval));
+ else if ((int)strlen(value) <= 4)
+ output(fp, name, value);
+ else
+ output_raw = '?';
+ break;
+
+ case LP:
+ if (!decodeuint(value, &uval) || uval > 3)
+ output_raw = '?';
+ else {
+ char b[3];
+ b[0] = b[1] = '0';
+ if (uval & 0x2)
+ b[0] = '1';
+ if (uval & 0x1)
+ b[1] = '1';
+ b[2] = '\0';
+ output(fp, name, b);
+ }
+ break;
+
+ case OC:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else {
+ char b[10];
+
+ (void) sprintf(b, "%03lo", uval);
+ output(fp, name, b);
+ }
+ break;
+
+ case MD:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, uinttoa(uval));
+ break;
+
+ case AR:
+ if (!decodearr(value, &narr, lfparr))
+ output_raw = '?';
+ else
+ outputarr(fp, name, narr, lfparr);
+ break;
+
+ case TST:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, tstflags(uval));
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ "Internal error in cookedprint, %s=%s, fmt %d\n",
+ name, value, fmt);
+ break;
+ }
+
+ }
+ if (output_raw != 0) {
+ char bn[401];
+ char bv[401];
+ int len;
+
+ atoascii(400, name, bn);
+ atoascii(400, value, bv);
+ if (output_raw != '*') {
+ len = strlen(bv);
+ bv[len] = output_raw;
+ bv[len+1] = '\0';
+ }
+ output(fp, bn, bv);
+ }
+ }
+ endoutput(fp);
+}
+
+
+/*
+ * sortassoc - sort associations in the cache into ascending order
+ */
+void
+sortassoc()
+{
+ if (numassoc > 1)
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)assoc_cache, numassoc,
+ sizeof(struct association), assoccmp);
+#else
+ qsort((char *)assoc_cache, numassoc,
+ sizeof(struct association), assoccmp);
+#endif /* sgi || bsdi */
+}
+
+
+/*
+ * assoccmp - compare two associations
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+assoccmp(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const struct association *ass1 = (const struct association *)t1;
+ const struct association *ass2 = (const struct association *)t2;
+#else
+assoccmp(ass1, ass2)
+ struct association *ass1;
+ struct association *ass2;
+{
+#endif /* sgi || bsdi */
+ if (ass1->assid < ass2->assid)
+ return -1;
+ if (ass1->assid > ass2->assid)
+ return 1;
+ return 0;
+}
diff --git a/usr.sbin/xntpd/ntpq/ntpq.h b/usr.sbin/xntpd/ntpq/ntpq.h
new file mode 100644
index 0000000..c233b24
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq.h
@@ -0,0 +1,97 @@
+/*
+ * ntpq.h - definitions of interest to ntpq
+ */
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+
+/*
+ * Maximum number of arguments
+ */
+#define MAXARGS 4
+
+/*
+ * Flags for forming descriptors.
+ */
+#define OPT 0x80 /* this argument is optional, or'd with type */
+
+#define NO 0x0
+#define STR 0x1 /* string argument */
+#define UINT 0x2 /* unsigned integer */
+#define INT 0x3 /* signed integer */
+#define ADD 0x4 /* IP network address */
+
+/*
+ * Arguments are returned in a union
+ */
+typedef union {
+ char *string;
+ long ival;
+ u_long uval;
+ u_long netnum;
+} arg_v;
+
+/*
+ * Structure for passing parsed command line
+ */
+struct parse {
+ char *keyword;
+ arg_v argval[MAXARGS];
+ int nargs;
+};
+
+/*
+ * xntpdc includes a command parser which could charitably be called
+ * crude. The following structure is used to define the command
+ * syntax.
+ */
+struct xcmd {
+ char *keyword; /* command key word */
+ void (*handler) P((struct parse *, FILE *)); /* command handler */
+ u_char arg[MAXARGS]; /* descriptors for arguments */
+ char *desc[MAXARGS]; /* descriptions for arguments */
+ char *comment;
+};
+
+/*
+ * Types of things we may deal with
+ */
+#define TYPE_SYS 1
+#define TYPE_PEER 2
+#define TYPE_CLOCK 3
+
+
+/*
+ * Structure to hold association data
+ */
+struct association {
+ u_short assid;
+ u_short status;
+};
+
+#define MAXASSOC 1024
+
+/*
+ * Structure for translation tables between text format
+ * variable indices and text format.
+ */
+struct ctl_var {
+ u_short code;
+ u_short fmt;
+ char *text;
+};
+
+extern void asciize P((int, char *, FILE *));
+extern int getnetnum P((char *, u_long *, char *));
+extern void sortassoc P((void));
+extern int doquery P((int, int, int, int, char *, u_short *, int *, char **));
+extern char * nntohost P((u_long));
+extern int decodets P((char *, l_fp *));
+extern int decodeuint P((char *, u_long *));
+extern int nextvar P((int *, char **, char **, char **));
+extern int decodetime P((char *, l_fp *));
+extern void printvars P((int, char *, int, int, FILE *));
+extern int decodeint P((char *, long *));
+extern int findvar P((char *, struct ctl_var *));
diff --git a/usr.sbin/xntpd/ntpq/ntpq_ops.c b/usr.sbin/xntpd/ntpq/ntpq_ops.c
new file mode 100644
index 0000000..8e59a71
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq_ops.c
@@ -0,0 +1,1610 @@
+/*
+ * ntpdc_ops.c - subroutines which are called to perform operations by xntpdc
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntpq.h"
+#include "ntp_stdlib.h"
+
+extern char * chosts[];
+extern char currenthost[];
+extern int numhosts;
+int maxhostlen;
+
+/*
+ * Declarations for command handlers in here
+ */
+static int checkassocid P((u_long));
+static char * strsave P((char *));
+static struct varlist *findlistvar P((struct varlist *, char *));
+static void doaddvlist P((struct varlist *, char *));
+static void dormvlist P((struct varlist *, char *));
+static void doclearvlist P((struct varlist *));
+static void makequerydata P((struct varlist *, int *, char *));
+static int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **));
+static void doprintvlist P((struct varlist *, FILE *));
+static void addvars P((struct parse *, FILE *));
+static void rmvars P((struct parse *, FILE *));
+static void clearvars P((struct parse *, FILE *));
+static void showvars P((struct parse *, FILE *));
+static int dolist P((struct varlist *, int, int, int, FILE *));
+static void readlist P((struct parse *, FILE *));
+static void writelist P((struct parse *, FILE *));
+static void readvar P((struct parse *, FILE *));
+static void writevar P((struct parse *, FILE *));
+static void clocklist P((struct parse *, FILE *));
+static void clockvar P((struct parse *, FILE *));
+static int findassidrange P((u_long, u_long, int *, int *));
+static void mreadlist P((struct parse *, FILE *));
+static void mreadvar P((struct parse *, FILE *));
+static int dogetassoc P((FILE *));
+static void printassoc P((int, FILE *));
+static void associations P((struct parse *, FILE *));
+static void lassociations P((struct parse *, FILE *));
+static void passociations P((struct parse *, FILE *));
+static void lpassociations P((struct parse *, FILE *));
+
+#ifdef UNUSED
+static void radiostatus P((struct parse *, FILE *));
+#endif /* UNUSED */
+
+static void pstatus P((struct parse *, FILE *));
+static char * fixup P((int, char *));
+static char * when P((l_fp *, l_fp *, l_fp *));
+static int doprintpeers P((struct varlist *, int, int, int, char *, FILE *));
+static int dogetpeers P((struct varlist *, int, FILE *));
+static void dopeers P((int, FILE *));
+static void peers P((struct parse *, FILE *));
+static void lpeers P((struct parse *, FILE *));
+static void doopeers P((int, FILE *));
+static void opeers P((struct parse *, FILE *));
+static void lopeers P((struct parse *, FILE *));
+
+
+/*
+ * Commands we understand. Ntpdc imports this.
+ */
+struct xcmd opcmds[] = {
+ { "associations", associations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of association ID's and statuses for the server's peers" },
+ { "passociations", passociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations returned by last associations command" },
+ { "lassociations", lassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations including all client information" },
+ { "lpassociations", lpassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+"print last obtained list of associations, including client information" },
+ { "addvars", addvars, { STR, NO, NO, NO },
+ { "name[=value][,...]", "", "", "" },
+ "add variables to the variable list or change their values" },
+ { "rmvars", rmvars, { STR, NO, NO, NO },
+ { "name[,...]", "", "", "" },
+ "remove variables from the variable list" },
+ { "clearvars", clearvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "remove all variables from the variable list" },
+ { "showvars", showvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print variables on the variable list" },
+ { "readlist", readlist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "rl", readlist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "writelist", writelist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "write the system or peer variables included in the variable list" },
+ { "readvar", readvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read system or peer variables" },
+ { "rv", readvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read system or peer variables" },
+ { "writevar", writevar, { UINT, STR, NO, NO },
+ { "assocID", "name=value,[...]", "", "" },
+ "write system or peer variables" },
+ { "mreadlist", mreadlist, { UINT, UINT, NO, NO },
+ { "assocID", "assocID", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mrl", mreadlist, { UINT, UINT, NO, NO },
+ { "assocID", "assocID", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mreadvar", mreadvar, { UINT, UINT, OPT|STR, NO },
+ { "assocID", "assocID", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "mrv", mreadvar, { UINT, UINT, OPT|STR, NO },
+ { "assocID", "assocID", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "clocklist", clocklist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "cl", clocklist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "clockvar", clockvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "cv", clockvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "pstatus", pstatus, { UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "print status information returned for a peer" },
+ { "peers", peers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of the server's peers" },
+ { "lpeers", lpeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of all peers and clients" },
+ { "opeers", opeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print peer list the old way, with dstadr shown rather than refid" },
+ { "lopeers", lopeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of all peers and clients showing dstadr" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Variable list data space
+ */
+#define MAXLIST 64 /* maximum number of variables in list */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+/*
+ * Old CTL_PST defines for version 2.
+ */
+#define OLD_CTL_PST_CONFIG 0x80
+#define OLD_CTL_PST_AUTHENABLE 0x40
+#define OLD_CTL_PST_AUTHENTIC 0x20
+#define OLD_CTL_PST_REACH 0x10
+#define OLD_CTL_PST_SANE 0x08
+#define OLD_CTL_PST_DISP 0x04
+#define OLD_CTL_PST_SEL_REJECT 0
+#define OLD_CTL_PST_SEL_SELCAND 1
+#define OLD_CTL_PST_SEL_SYNCCAND 2
+#define OLD_CTL_PST_SEL_SYSPEER 3
+
+
+char flash2[] = " .+* "; /* flash decode for version 2 */
+char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
+
+struct varlist {
+ char *name;
+ char *value;
+} varlist[MAXLIST] = { { 0, 0 } };
+
+/*
+ * Imported from ntpq.c
+ */
+extern int showhostnames;
+extern int rawmode;
+extern int debug;
+extern struct servent *server_entry;
+extern struct association assoc_cache[];
+extern int numassoc;
+extern u_char pktversion;
+
+/*
+ * For quick string comparisons
+ */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+
+/*
+ * checkassocid - return the association ID, checking to see if it is valid
+ */
+static int
+checkassocid(value)
+ u_long value;
+{
+ if (value == 0 || value >= 65536) {
+ (void) fprintf(stderr, "***Invalid association ID specified\n");
+ return 0;
+ }
+ return (int)value;
+}
+
+
+/*
+ * strsave - save a string
+ * XXX - should be in libntp.a
+ */
+static char *
+strsave(str)
+ char *str;
+{
+ char *cp;
+ u_int len;
+
+ len = strlen(str) + 1;
+ if ((cp = (char *)malloc(len)) == NULL) {
+ (void) fprintf(stderr, "Malloc failed!!\n");
+ exit(1);
+ }
+
+ memmove(cp, str, len);
+ return (cp);
+}
+
+
+/*
+ * findlistvar - look for the named variable in a list and return if found
+ */
+static struct varlist *
+findlistvar(list, name)
+ struct varlist *list;
+ char *name;
+{
+ register struct varlist *vl;
+
+ for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++)
+ if (STREQ(name, vl->name))
+ return vl;
+ if (vl < list + MAXLIST)
+ return vl;
+ return (struct varlist *)0;
+}
+
+
+/*
+ * doaddvlist - add variable(s) to the variable list
+ */
+static void
+doaddvlist(vlist, vars)
+ struct varlist *vlist;
+ char *vars;
+{
+ register struct varlist *vl;
+ int len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ vl = findlistvar(vlist, name);
+ if (vl == 0) {
+ (void) fprintf(stderr, "Variable list full\n");
+ return;
+ }
+
+ if (vl->name == 0) {
+ vl->name = strsave(name);
+ } else if (vl->value != 0) {
+ (void) free(vl->value);
+ vl->value = 0;
+ }
+
+ if (value != 0)
+ vl->value = strsave(value);
+ }
+}
+
+
+/*
+ * dormvlist - remove variable(s) from the variable list
+ */
+static void
+dormvlist(vlist, vars)
+ struct varlist *vlist;
+ char *vars;
+{
+ register struct varlist *vl;
+ int len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ vl = findlistvar(vlist, name);
+ if (vl == 0 || vl->name == 0) {
+ (void) fprintf(stderr, "Variable `%s' not found\n",
+ name);
+ } else {
+ (void) free(vl->name);
+ if (vl->value != 0)
+ (void) free(vl->value);
+ for ( ; (vl+1) < (varlist+MAXLIST)
+ && (vl+1)->name != 0; vl++) {
+ vl->name = (vl+1)->name;
+ vl->value = (vl+1)->value;
+ }
+ vl->name = vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * doclearvlist - clear a variable list
+ */
+static void
+doclearvlist(vlist)
+ struct varlist *vlist;
+{
+ register struct varlist *vl;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ (void) free(vl->name);
+ vl->name = 0;
+ if (vl->value != 0) {
+ (void) free(vl->value);
+ vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * makequerydata - form a data buffer to be included with a query
+ */
+static void
+makequerydata(vlist, datalen, data)
+ struct varlist *vlist;
+ int *datalen;
+ char *data;
+{
+ register struct varlist *vl;
+ register char *cp, *cpend;
+ register int namelen, valuelen;
+ register int totallen;
+
+ cp = data;
+ cpend = data + *datalen;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ namelen = strlen(vl->name);
+ if (vl->value == 0)
+ valuelen = 0;
+ else
+ valuelen = strlen(vl->value);
+ totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
+ if (cp + totallen > cpend)
+ break;
+
+ if (cp != data)
+ *cp++ = ',';
+ memmove(cp, vl->name, namelen);
+ cp += namelen;
+ if (valuelen != 0) {
+ *cp++ = '=';
+ memmove(cp, vl->value, valuelen);
+ cp += valuelen;
+ }
+ }
+ *datalen = cp - data;
+}
+
+
+/*
+ * doquerylist - send a message including variables in a list
+ */
+static int
+doquerylist(vlist, op, associd, auth, rstatus, dsize, datap)
+ struct varlist *vlist;
+ int op;
+ int associd;
+ int auth;
+ u_short *rstatus;
+ int *dsize;
+ char **datap;
+{
+ char data[CTL_MAX_DATA_LEN];
+ int datalen;
+
+ datalen = sizeof(data);
+ makequerydata(vlist, &datalen, data);
+
+ return doquery(op, associd, auth, datalen, data, rstatus,
+ dsize, datap);
+}
+
+
+/*
+ * doprintvlist - print the variables on a list
+ */
+static void
+doprintvlist(vlist, fp)
+ struct varlist *vlist;
+ FILE *fp;
+{
+ register struct varlist *vl;
+
+ if (vlist->name == 0) {
+ (void) fprintf(fp, "No variables on list\n");
+ } else {
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ if (vl->value == 0) {
+ (void) fprintf(fp, "%s\n", vl->name);
+ } else {
+ (void) fprintf(fp, "%s=%s\n",
+ vl->name, vl->value);
+ }
+ }
+ }
+}
+
+
+/*
+ * addvars - add variables to the variable list
+ */
+/*ARGSUSED*/
+static void
+addvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doaddvlist(varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * rmvars - remove variables from the variable list
+ */
+/*ARGSUSED*/
+static void
+rmvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dormvlist(varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * clearvars - clear the variable list
+ */
+/*ARGSUSED*/
+static void
+clearvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doclearvlist(varlist);
+}
+
+
+/*
+ * showvars - show variables on the variable list
+ */
+/*ARGSUSED*/
+static void
+showvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doprintvlist(varlist, fp);
+}
+
+
+/*
+ * dolist - send a request with the given list of variables
+ */
+static int
+dolist(vlist, associd, op, type, fp)
+ struct varlist *vlist;
+ int associd;
+ int op;
+ int type;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (associd == 0)
+ (void) fprintf(fp, "No system%s variables returned\n",
+ (type == TYPE_CLOCK) ? " clock" : "");
+ else
+ (void) fprintf(fp,
+ "No information returned for%s association %u\n",
+ (type == TYPE_CLOCK) ? " clock" : "", associd);
+ return 1;
+ }
+
+ printvars(dsize, datap, (int)rstatus, type, fp);
+ return 1;
+}
+
+
+/*
+ * readlist - send a read variables request with the variables on the list
+ */
+static void
+readlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ (void) dolist(varlist, associd, CTL_OP_READVAR,
+ (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
+}
+
+
+/*
+ * writelist - send a write variables request with the variables on the list
+ */
+static void
+writelist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ res = doquerylist(varlist, CTL_OP_WRITEVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0)
+ (void) fprintf(fp, "done! (no data returned)\n");
+ else
+ printvars(dsize, datap, (int)rstatus,
+ (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
+ return;
+}
+
+
+/*
+ * readvar - send a read variables request with the specified variables
+ */
+static void
+readvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 2)
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ (void) dolist(tmplist, associd, CTL_OP_READVAR,
+ (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * writevar - send a write variables request with the specified variables
+ */
+static void
+writevar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+
+ doclearvlist(tmplist);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0)
+ (void) fprintf(fp, "done! (no data returned)\n");
+ else
+ printvars(dsize, datap, (int)rstatus,
+ (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
+ return;
+}
+
+
+/*
+ * clocklist - send a clock variables request with the variables on the list
+ */
+static void
+clocklist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ (void) dolist(varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+}
+
+
+/*
+ * clockvar - send a clock variables request with the specified variables
+ */
+static void
+clockvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 2)
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ (void) dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * findassidrange - verify a range of association ID's
+ */
+static int
+findassidrange(assid1, assid2, from, to)
+ u_long assid1;
+ u_long assid2;
+ int *from;
+ int *to;
+{
+ register int i;
+ int f, t;
+
+ if (assid1 == 0 || assid1 > 65535) {
+ (void) fprintf(stderr,
+ "***Invalid association ID %lu specified\n", (u_long)assid1);
+ return 0;
+ }
+
+ if (assid2 == 0 || assid2 > 65535) {
+ (void) fprintf(stderr,
+ "***Invalid association ID %lu specified\n", (u_long)assid2);
+ return 0;
+ }
+
+ f = t = -1;
+ for (i = 0; i < numassoc; i++) {
+ if (assoc_cache[i].assid == assid1) {
+ f = i;
+ if (t != -1)
+ break;
+ }
+ if (assoc_cache[i].assid == assid2) {
+ t = i;
+ if (f != -1)
+ break;
+ }
+ }
+
+ if (f == -1 || t == -1) {
+ (void) fprintf(stderr,
+ "***Association ID %lu not found in list\n",
+ (f == -1) ? (u_long)assid1 : (u_long)assid2);
+ return 0;
+ }
+
+ if (f < t) {
+ *from = f;
+ *to = t;
+ } else {
+ *from = t;
+ *to = f;
+ }
+ return 1;
+}
+
+
+
+/*
+ * mreadlist - send a read variables request for multiple associations
+ */
+static void
+mreadlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int from;
+ int to;
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to))
+ return;
+
+ for (i = from; i <= to; i++) {
+ if (i != from)
+ (void) fprintf(fp, "\n");
+ if (!dolist(varlist, (int)assoc_cache[i].assid,
+ CTL_OP_READVAR, TYPE_PEER, fp))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * mreadvar - send a read variables request for multiple associations
+ */
+static void
+mreadvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int from;
+ int to;
+ struct varlist tmplist[MAXLIST];
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to))
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 3)
+ doaddvlist(tmplist, pcmd->argval[2].string);
+
+ for (i = from; i <= to; i++) {
+ if (i != from)
+ (void) fprintf(fp, "\n");
+ if (!dolist(varlist, (int)assoc_cache[i].assid,
+ CTL_OP_READVAR, TYPE_PEER, fp))
+ break;
+ }
+ doclearvlist(tmplist);
+ return;
+}
+
+
+/*
+ * dogetassoc - query the host for its list of associations
+ */
+static int
+dogetassoc(fp)
+ FILE *fp;
+{
+ u_short *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, (char **)&datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ (void) fprintf(fp, "No association ID's returned\n");
+ return 0;
+ }
+
+ if (dsize & 0x3) {
+ (void) fprintf(stderr,
+ "***Server returned %d octets, should be multiple of 4\n",
+ dsize);
+ return 0;
+ }
+
+ numassoc = 0;
+ while (dsize > 0) {
+ assoc_cache[numassoc].assid = ntohs(*datap);
+ datap++;
+ assoc_cache[numassoc].status = ntohs(*datap);
+ datap++;
+ if (++numassoc >= MAXASSOC)
+ break;
+ dsize -= sizeof(u_short) + sizeof(u_short);
+ }
+ sortassoc();
+ return 1;
+}
+
+
+/*
+ * printassoc - print the current list of associations
+ */
+static void
+printassoc(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register char *bp;
+ int i;
+ u_char statval;
+ int event;
+ u_long event_count;
+ char *conf;
+ char *reach;
+ char *auth;
+ char *condition = "";
+ char *last_event;
+ char *cnt;
+ char buf[128];
+
+ if (numassoc == 0) {
+ (void) fprintf(fp, "No association ID's in list\n");
+ return;
+ }
+
+ /*
+ * Output a header
+ */
+ (void) fprintf(fp,
+ "ind assID status conf reach auth condition last_event cnt\n");
+ (void) fprintf(fp,
+ "===========================================================\n");
+ for (i = 0; i < numassoc; i++) {
+ statval = CTL_PEER_STATVAL(assoc_cache[i].status);
+ if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ event = CTL_PEER_EVENT(assoc_cache[i].status);
+ event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
+ if (statval & CTL_PST_CONFIG)
+ conf = "yes";
+ else
+ conf = "no";
+ if (statval & CTL_PST_REACH) {
+ reach = "yes";
+ if (statval & CTL_PST_AUTHENABLE) {
+ if (statval & CTL_PST_AUTHENTIC)
+ auth = "ok ";
+ else
+ auth = "bad";
+ } else
+ auth = "none";
+
+ if (pktversion == NTP_VERSION)
+ switch (statval & 0x7) {
+ case CTL_PST_SEL_REJECT:
+ condition = "insane";
+ break;
+ case CTL_PST_SEL_SANE:
+ condition = "falsetick";
+ break;
+ case CTL_PST_SEL_CORRECT:
+ condition = "eliminate";
+ break;
+ case CTL_PST_SEL_SELCAND:
+ condition = "outlyer";
+ break;
+ case CTL_PST_SEL_SYNCCAND:
+ condition = "synchr.";
+ break;
+ case CTL_PST_SEL_DISTSYSPEER:
+ condition = "dist.peer";
+ break;
+ case CTL_PST_SEL_SYSPEER:
+ condition = "sys.peer";
+ break;
+ case CTL_PST_SEL_PPS:
+ condition = "pps.peer";
+ break;
+ }
+ else
+ switch (statval & 0x3) {
+ case OLD_CTL_PST_SEL_REJECT:
+ if (!(statval & OLD_CTL_PST_SANE))
+ condition = "insane";
+ else if (!(statval & OLD_CTL_PST_DISP))
+ condition = "hi_disp";
+ else
+ condition = "";
+ break;
+ case OLD_CTL_PST_SEL_SELCAND:
+ condition = "sel_cand";
+ break;
+ case OLD_CTL_PST_SEL_SYNCCAND:
+ condition = "sync_cand";
+ break;
+ case OLD_CTL_PST_SEL_SYSPEER:
+ condition = "sys.peer";
+ break;
+ }
+
+ } else {
+ reach = "no";
+ auth = condition = "";
+ }
+
+ switch (PEER_EVENT|event) {
+ case EVNT_PEERIPERR:
+ last_event = "IP error";
+ break;
+ case EVNT_PEERAUTH:
+ last_event = "auth fail";
+ break;
+ case EVNT_UNREACH:
+ last_event = "lost reach";
+ break;
+ case EVNT_REACH:
+ last_event = "reachable";
+ break;
+ case EVNT_PEERCLOCK:
+ last_event = "clock expt";
+ break;
+#if 0
+ case EVNT_PEERSTRAT:
+ last_event = "stratum chg";
+ break;
+#endif
+ default:
+ last_event = "";
+ break;
+ }
+
+ if (event_count != 0)
+ cnt = uinttoa(event_count);
+ else
+ cnt = "";
+ (void) sprintf(buf,
+ "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2s",
+ i+1, assoc_cache[i].assid, assoc_cache[i].status,
+ conf, reach, auth, condition, last_event, cnt);
+ bp = &buf[strlen(buf)];
+ while (bp > buf && *(bp-1) == ' ')
+ *(--bp) = '\0';
+ (void) fprintf(fp, "%s\n", buf);
+ }
+}
+
+
+
+/*
+ * associations - get, record and print a list of associations
+ */
+/*ARGSUSED*/
+static void
+associations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (dogetassoc(fp))
+ printassoc(0, fp);
+}
+
+
+/*
+ * lassociations - get, record and print a long list of associations
+ */
+/*ARGSUSED*/
+static void
+lassociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (dogetassoc(fp))
+ printassoc(1, fp);
+}
+
+
+/*
+ * passociations - print the association list
+ */
+/*ARGSUSED*/
+static void
+passociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ printassoc(0, fp);
+}
+
+
+/*
+ * lpassociations - print the long association list
+ */
+/*ARGSUSED*/
+static void
+lpassociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ printassoc(1, fp);
+}
+
+
+#ifdef UNUSED
+/*
+ * radiostatus - print the radio status returned by the server
+ */
+/*ARGSUSED*/
+static void
+radiostatus(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0) {
+ (void) fprintf(fp, "No radio status string returned\n");
+ return;
+ }
+
+ asciize(dsize, datap, fp);
+}
+#endif /* UNUSED */
+
+/*
+ * pstatus - print peer status returned by the server
+ */
+static void
+pstatus(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+
+ if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ res = doquery(CTL_OP_READSTAT, associd, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0) {
+ (void) fprintf(fp,
+ "No information returned for association %u\n",
+ associd);
+ return;
+ }
+
+ printvars(dsize, datap, (int)rstatus, TYPE_PEER, fp);
+}
+
+
+/*
+ * fixup - fix up a string so we don't get a hanging decimal after it
+ */
+static char *
+fixup(width, str)
+ int width;
+ char *str;
+{
+ if (str[width-1] == '.')
+ str[width-1] = '\0';
+ return str;
+}
+
+
+/*
+ * when - print how long its been since his last packet arrived
+ */
+static char *
+when(ts, rec, reftime)
+ l_fp *ts;
+ l_fp *rec;
+ l_fp *reftime;
+{
+ long diff;
+ l_fp *lasttime;
+ static char buf[20];
+
+ if (rec->l_ui != 0)
+ lasttime = rec;
+ else if (reftime->l_ui != 0)
+ lasttime = reftime;
+ else
+ return "-";
+
+ diff = (long)(ts->l_ui - lasttime->l_ui);
+ if (diff <= 0) {
+ /*
+ * Time warp?
+ */
+ diff = 1;
+ }
+
+ if (diff <= 2048) {
+ (void) sprintf(buf, "%ld", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 300) {
+ (void) sprintf(buf, "%ldm", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 96) {
+ (void) sprintf(buf, "%ldh", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 11) / 24;
+ (void) sprintf(buf, "%ldd", (long int)diff);
+ return buf;
+}
+
+
+
+/*
+ * A list of variables required by the peers command
+ */
+struct varlist opeervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "dstadr", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "dispersion", 0 }, /* 8 */
+ { "rec", 0 }, /* 9 */
+ { "reftime", 0 }, /* 10 */
+ { "srcport", 0 }, /* 11 */
+ { 0, 0 }
+};
+
+struct varlist peervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "refid", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "dispersion", 0 }, /* 8 */
+ { "rec", 0 }, /* 9 */
+ { "reftime", 0 }, /* 10 */
+ { "srcport", 0 }, /* 11 */
+ { 0, 0 }
+};
+
+#define HAVE_SRCADR 0
+#define HAVE_DSTADR 1
+#define HAVE_REFID 1
+#define HAVE_STRATUM 2
+#define HAVE_HPOLL 3
+#define HAVE_PPOLL 4
+#define HAVE_REACH 5
+#define HAVE_DELAY 6
+#define HAVE_OFFSET 7
+#define HAVE_DISPERSION 8
+#define HAVE_REC 9
+#define HAVE_REFTIME 10
+#define HAVE_SRCPORT 11
+#define MAXHAVE 12
+
+/*
+ * Decode an incoming data buffer and print a line in the peer list
+ */
+static int
+doprintpeers(pvl, associd, rstatus, datalen, data, fp)
+ struct varlist *pvl;
+ int associd;
+ int rstatus;
+ int datalen;
+ char *data;
+ FILE *fp;
+{
+ char *name;
+ char *value;
+ int i;
+ int c;
+
+ u_long srcadr;
+ u_long dstadr;
+ u_long srcport;
+ char *dstadr_refid = "0.0.0.0";
+ u_long stratum;
+ long ppoll;
+ long hpoll;
+ u_long reach;
+ l_fp estdelay;
+ l_fp estoffset;
+ l_fp estdisp;
+ l_fp rec;
+ l_fp reftime;
+ l_fp ts;
+ u_char havevar[MAXHAVE];
+ u_long poll;
+ char type = '?';
+ char refid_string[10];
+ extern struct ctl_var peer_var[];
+
+ memset((char *)havevar, 0, sizeof(havevar));
+ gettstamp(&ts);
+
+ while (nextvar(&datalen, &data, &name, &value)) {
+ u_long dummy;
+ i = findvar(name, peer_var);
+ if (i == 0)
+ continue; /* don't know this one */
+ switch (i) {
+ case CP_SRCADR:
+ if (decodenetnum(value, &srcadr))
+ havevar[HAVE_SRCADR] = 1;
+ break;
+ case CP_DSTADR:
+ if (decodenetnum(value, &dummy)) {
+ dummy = ntohl(dummy);
+ type = ((dummy&0xf0000000)==0xe0000000) ? 'm' :
+ ((dummy&0x000000ff)==0x000000ff) ? 'b' :
+ ((dummy&0xffffffff)==0x7f000001) ? 'l' :
+ ((dummy&0xffffffe0)==0x00000000) ? '-' :
+ 'u';
+ }
+ if (pvl == opeervarlist) {
+ if (decodenetnum(value, &dstadr)) {
+ havevar[HAVE_DSTADR] = 1;
+ dstadr_refid = numtoa(dstadr);
+ }
+ }
+ break;
+ case CP_REFID:
+ if (pvl == peervarlist) {
+ havevar[HAVE_REFID] = 1;
+ if (*value == '\0') {
+ dstadr_refid = "0.0.0.0";
+ } else if (decodenetnum(value, &dstadr)) {
+ if (dstadr == 0)
+ dstadr_refid = "0.0.0.0";
+ else
+ dstadr_refid = nntohost(dstadr);
+ } else if ((int)strlen(value) <= 4) {
+ refid_string[0] = '.';
+ (void) strcpy(&refid_string[1], value);
+ i = strlen(refid_string);
+ refid_string[i] = '.';
+ refid_string[i+1] = '\0';
+ dstadr_refid = refid_string;
+ } else {
+ havevar[HAVE_REFID] = 0;
+ }
+ }
+ break;
+ case CP_STRATUM:
+ if (decodeuint(value, &stratum))
+ havevar[HAVE_STRATUM] = 1;
+ break;
+ case CP_HPOLL:
+ if (decodeint(value, &hpoll)) {
+ havevar[HAVE_HPOLL] = 1;
+ if (hpoll < 0)
+ hpoll = NTP_MINPOLL;
+ }
+ break;
+ case CP_PPOLL:
+ if (decodeint(value, &ppoll)) {
+ havevar[HAVE_PPOLL] = 1;
+ if (ppoll < 0)
+ ppoll = NTP_MINPOLL;
+ }
+ break;
+ case CP_REACH:
+ if (decodeuint(value, &reach))
+ havevar[HAVE_REACH] = 1;
+ break;
+ case CP_DELAY:
+ if (decodetime(value, &estdelay))
+ havevar[HAVE_DELAY] = 1;
+ break;
+ case CP_OFFSET:
+ if (decodetime(value, &estoffset))
+ havevar[HAVE_OFFSET] = 1;
+ break;
+ case CP_DISPERSION:
+ if (decodetime(value, &estdisp))
+ havevar[HAVE_DISPERSION] = 1;
+ break;
+ case CP_REC:
+ if (decodets(value, &rec))
+ havevar[HAVE_REC] = 1;
+ break;
+ case CP_SRCPORT:
+ if (decodeuint(value, &srcport))
+ havevar[HAVE_SRCPORT] = 1;
+ break;
+ case CP_REFTIME:
+ havevar[HAVE_REFTIME] = 1;
+ if (!decodets(value, &reftime))
+ L_CLR(&reftime);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Check to see if the srcport is NTP's port. If not this probably
+ * isn't a valid peer association.
+ */
+ if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT)
+ return 1;
+
+ /*
+ * Check to see if we got all of them. If not, return an
+ * error.
+ */
+ for (i = 0; i < MAXHAVE; i++)
+ if (!havevar[i]) {
+ (void) fprintf(stderr,
+ "***Remote host didn't return peer.%s for association %d\n",
+ pvl[i].name, associd);
+ return 0;
+ }
+
+
+ /*
+ * Got everything, format the line
+ */
+ poll = 1<<max(min3(ppoll, hpoll, NTP_MAXPOLL), NTP_MINPOLL);
+ if (pktversion == NTP_VERSION)
+ c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
+ else
+ c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
+ if (numhosts > 1)
+ (void) fprintf(fp, "%-*s ", maxhostlen, currenthost);
+ (void) fprintf(fp,
+ "%c%-15.15s %-15.15s %2ld %c %4.4s %4ld %3lo %7.7s %8.7s %7.7s\n",
+ c, nntohost(srcadr), dstadr_refid, stratum, type,
+ when(&ts, &rec, &reftime), poll, reach,
+ fixup(7, lfptoms(&estdelay, 2)), fixup(8, lfptoms(&estoffset, 3)),
+ fixup(7, lfptoms(&estdisp, 2)));
+ return 1;
+}
+
+#undef HAVE_SRCADR
+#undef HAVE_DSTADR
+#undef HAVE_STRATUM
+#undef HAVE_PPOLL
+#undef HAVE_HPOLL
+#undef HAVE_REACH
+#undef HAVE_ESTDELAY
+#undef HAVE_ESTOFFSET
+#undef HAVE_ESTDISP
+#undef HAVE_REFID
+#undef HAVE_REC
+#undef HAVE_SRCPORT
+#undef HAVE_REFTIME
+#undef MAXHAVE
+
+
+/*
+ * dogetpeers - given an association ID, read and print the spreadsheet
+ * peer variables.
+ */
+static int
+dogetpeers(pvl, associd, fp)
+ struct varlist *pvl;
+ int associd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+#ifdef notdef
+ res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+#else
+ /*
+ * Damn fuzzballs
+ */
+ res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+#endif
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ (void) fprintf(stderr,
+ "***No information returned for association %d\n",
+ associd);
+ return 0;
+ }
+
+
+ return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, fp);
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+static void
+dopeers(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register int i;
+ char fullname[LENHOSTNAME];
+ u_long netnum;
+
+
+ if (!dogetassoc(fp))
+ return;
+
+ for (i = 0; i < numhosts; ++i)
+ { if(getnetnum(chosts[i],&netnum,fullname))
+ if ((int)strlen(fullname) > maxhostlen)
+ maxhostlen = strlen(fullname);
+ }
+ if (numhosts > 1)
+ (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "host");
+ (void) fprintf(fp,
+" remote refid st t when poll reach delay offset disp\n");
+ if (numhosts > 1)
+ for (i = 0; i <= maxhostlen; ++i)
+ (void) fprintf(fp, "=");
+ (void) fprintf(fp,
+"==============================================================================\n");
+
+ for (i = 0; i < numassoc; i++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[i].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ if (!dogetpeers(peervarlist, (int)assoc_cache[i].assid, fp)) {
+ return;
+ }
+ }
+ return;
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+/*ARGSUSED*/
+static void
+peers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(0, fp);
+}
+
+
+/*
+ * lpeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lpeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(1, fp);
+}
+
+
+/*
+ * opeers - print a peer spreadsheet
+ */
+static void
+doopeers(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register int i;
+
+ if (!dogetassoc(fp))
+ return;
+
+ (void) fprintf(fp,
+" remote local st t when poll reach delay offset disp\n");
+ (void) fprintf(fp,
+"===========================================================================\n");
+
+ for (i = 0; i < numassoc; i++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[i].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ if (!dogetpeers(opeervarlist, (int)assoc_cache[i].assid, fp)) {
+ return;
+ }
+ }
+ return;
+}
+
+
+/*
+ * opeers - print a peer spreadsheet the old way
+ */
+/*ARGSUSED*/
+static void
+opeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doopeers(0, fp);
+}
+
+
+/*
+ * lopeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lopeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doopeers(1, fp);
+}
diff --git a/usr.sbin/xntpd/ntptrace/Makefile b/usr.sbin/xntpd/ntptrace/Makefile
new file mode 100644
index 0000000..2632aa4
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id: Makefile,v 1.8 1998/03/07 09:46:10 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= ntptrace
+MAN8= ${.CURDIR}/../doc/ntptrace.8
+CLEANFILES+= .version version.c
+
+SRCS= ntptrace.c version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntptrace
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntptrace/README b/usr.sbin/xntpd/ntptrace/README
new file mode 100644
index 0000000..b976cfd
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/README
@@ -0,0 +1,7 @@
+README file for directory ./ntptrace of the NTP Version 3 distribution
+
+This directory contains the sources for the ntptrace utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
+
diff --git a/usr.sbin/xntpd/ntptrace/ntptrace.c b/usr.sbin/xntpd/ntptrace/ntptrace.c
new file mode 100644
index 0000000..206c738
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/ntptrace.c
@@ -0,0 +1,772 @@
+/*
+ * ntptrace - show the chain from an NTP host leading back to
+ * its source of time
+ *
+ * Jeffrey Mogul DECWRL 13 January 1993
+ *
+ * Inspired by a script written by Glenn Trewitt
+ *
+ * Large portions stolen from ntpdate.c
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(SYS_HPUX)
+#include <utmp.h>
+#endif
+
+#include "ntp_select.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntptrace.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+/*
+ * Debugging flag
+ */
+int debug = 0;
+
+int nonames = 0; /* if set, don't print hostnames */
+
+/*
+ * Program name.
+ */
+char *progname;
+
+/*
+ * Systemwide parameters and flags
+ */
+int sys_retries = 5; /* # of retry attempts per server */
+int sys_timeout = 2; /* timeout time, in seconds */
+struct server **sys_servers; /* the server list */
+int sys_numservers = 0; /* number of servers to poll */
+int sys_maxservers = NTP_MAXSTRATUM+1; /* max number of servers to deal with */
+int sys_version = NTP_OLDVERSION; /* version to poll with */
+
+/*
+ * recvbuf lists
+ */
+struct recvbuf *freelist; /* free buffers */
+struct recvbuf *fulllist; /* buffers with data */
+
+int full_recvbufs; /* number of full ones */
+int free_recvbufs;
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+int fd;
+fd_set fdmask;
+
+/*
+ * Miscellaneous flags
+ */
+int verbose = 0;
+int always_step = 0;
+
+extern int errno;
+
+static void DoTrace P((struct server *));
+static void DoTransmit P((struct server *));
+static int DoReceive P((struct server *));
+static int ReceiveBuf P((struct server *, struct recvbuf *));
+static struct server *addserver P((struct in_addr *));
+static struct server *addservbyname P((char *));
+static void setup_io P((void));
+static void freerecvbuf P((struct recvbuf *));
+static void sendpkt P((struct sockaddr_in *, struct pkt *, int));
+static int getipaddr P((char *, long *));
+static int decodeipaddr P((char *, long *));
+static void printserver P((struct server *, FILE *));
+static void printrefid P((FILE *, struct server *));
+
+/*
+ * Main program. Initialize us and loop waiting for I/O and/or
+ * timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct server *firstserver;
+ int errflg;
+ int c;
+ extern char *ntp_optarg;
+ extern int ntp_optind;
+ extern char *Version;
+
+ errflg = 0;
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, "do:nr:t:v")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ nonames = 1;
+ break;
+ case 'o':
+ sys_version = atoi(ntp_optarg);
+ break;
+ case 'r':
+ sys_retries = atoi(ntp_optarg);
+ if (sys_retries < 1) {
+ (void)fprintf(stderr,
+ "%s: retries (%d) too small\n",
+ progname, sys_retries);
+ errflg++;
+ }
+ break;
+ case 't':
+ sys_timeout = atoi(ntp_optarg);
+ if (sys_timeout < 1) {
+ (void)fprintf(stderr,
+ "%s: timeout (%d) too short\n",
+ progname, sys_timeout);
+ errflg++;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ if (errflg || (argc - ntp_optind) > 1) {
+ (void) fprintf(stderr,
+ "usage: %s [-vnd] [-r retries] [-t timeout] [server]\n",
+ progname);
+ exit(2);
+ }
+
+ sys_servers = (struct server **)
+ emalloc(sys_maxservers * sizeof(struct server *));
+
+ if (debug) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+ if (debug || verbose)
+ syslog(LOG_NOTICE, "%s", Version);
+
+ if ((argc - ntp_optind) == 1)
+ firstserver = addservbyname(argv[ntp_optind]);
+ else
+ firstserver = addservbyname("localhost");
+
+ if (firstserver == NULL) {
+ /* a message has already been printed */
+ exit(2);
+ }
+
+ /*
+ * Initialize the time of day routines and the I/O subsystem
+ */
+ setup_io();
+
+ DoTrace(firstserver);
+
+ exit(0);
+}
+
+static void
+DoTrace(server)
+register struct server *server;
+{
+ int retries = sys_retries;
+
+ if (!verbose) {
+ if (nonames)
+ printf("%s: ", ntoa(&server->srcadr));
+ else
+ printf("%s: ", ntohost(&server->srcadr));
+ fflush(stdout);
+ }
+ while (retries-- > 0) {
+ DoTransmit(server);
+ if (DoReceive(server))
+ return;
+ }
+ if (verbose) {
+ if (nonames)
+ printf("%s:\t*Timeout*\n", ntoa(&server->srcadr));
+ else
+ printf("%s:\t*Timeout*\n", ntohost(&server->srcadr));
+ }
+ else
+ printf("\t*Timeout*\n");
+}
+
+/*
+ * Dotransmit - transmit a packet to the given server
+ */
+static void
+DoTransmit(server)
+register struct server *server;
+{
+ struct pkt xpkt;
+
+ if (debug)
+ printf("DoTransmit(%s)\n", ntoa(&server->srcadr));
+
+ /*
+ * Fill in the packet and let 'er rip.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ sys_version, MODE_CLIENT);
+ xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
+ xpkt.ppoll = NTP_MINPOLL;
+ xpkt.precision = NTPTRACE_PRECISION;
+ xpkt.rootdelay = htonl(NTPTRACE_DISTANCE);
+ xpkt.rootdispersion = htonl(NTPTRACE_DISP);
+ xpkt.refid = htonl(NTPTRACE_REFID);
+ L_CLR(&xpkt.reftime);
+ L_CLR(&xpkt.org);
+ L_CLR(&xpkt.rec);
+
+ /*
+ * just timestamp packet and send it away.
+ */
+ get_systime(&(server->xmt));
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC);
+
+ if (debug)
+ printf("DoTransmit to %s\n", ntoa(&(server->srcadr)));
+}
+
+/*
+ * DoReceive - attempt to receive a packet from a specific server
+ */
+static int
+DoReceive(server)
+register struct server *server;
+{
+ register int n;
+ fd_set fds;
+ struct timeval timeout;
+ l_fp ts;
+ register struct recvbuf *rb;
+ int fromlen;
+ int status;
+
+ /*
+ * Loop until we see the packet we want or until we time out
+ */
+ for (;;) {
+ fds = fdmask;
+ timeout.tv_sec = sys_timeout;
+ timeout.tv_usec = 0;
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &timeout);
+
+ if (n == 0) { /* timed out */
+ if (debug)
+ printf("timeout\n");
+ return(0);
+ }
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return(0);
+ }
+ get_systime(&ts);
+
+ if (free_recvbufs == 0) {
+ syslog(LOG_ERR, "no buffers");
+ exit(1);
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt,
+ sizeof(rb->recv_pkt), 0,
+ (struct sockaddr *)&rb->srcadr, &fromlen);
+ if (rb->recv_length == -1) {
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list.
+ */
+ rb->recv_time = ts;
+ rb->next = fulllist;
+ fulllist = rb;
+ full_recvbufs++;
+
+ status = ReceiveBuf(server, rb);
+
+ freerecvbuf(rb);
+
+ return(status);
+ }
+}
+
+/*
+ * receive - receive and process an incoming frame
+ * Return 1 on success, 0 on failure
+ */
+static int
+ReceiveBuf(server, rbufp)
+ struct server *server;
+ struct recvbuf *rbufp;
+{
+ register struct pkt *rpkt;
+ register s_fp di;
+ l_fp t10, t23;
+ l_fp org;
+ l_fp rec;
+ l_fp ci;
+ struct server *nextserver;
+ struct in_addr nextia;
+
+
+ if (debug) {
+ printf("ReceiveBuf(%s, ", ntoa(&server->srcadr));
+ printf("%s)\n", ntoa(&rbufp->srcadr));
+ }
+
+ /*
+ * Check to see if the packet basically looks like something
+ * intended for us.
+ */
+ if (rbufp->recv_length < LEN_PKT_NOMAC) {
+ if (debug)
+ printf("receive: packet length %d\n",
+ rbufp->recv_length);
+ return(0); /* funny length packet */
+ }
+ if (rbufp->srcadr.sin_addr.s_addr != server->srcadr.sin_addr.s_addr) {
+ if (debug)
+ printf("receive: wrong server\n");
+ return(0); /* funny length packet */
+ }
+
+ rpkt = &(rbufp->recv_pkt);
+
+ if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION) {
+ if (debug)
+ printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode));
+ return(0);
+ }
+ if (PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
+ if (debug)
+ printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode));
+ return(0);
+ }
+
+ if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
+ && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
+ || rpkt->stratum > NTP_MAXSTRATUM) {
+ if (debug)
+ printf("receive: mode %d stratum %d\n",
+ PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
+ return(0);
+ }
+
+ /*
+ * Decode the org timestamp and make sure we're getting a response
+ * to our last request.
+ */
+ NTOHL_FP(&rpkt->org, &org);
+ if (!L_ISEQU(&org, &server->xmt)) {
+ if (debug)
+ printf("receive: pkt.org and peer.xmt differ\n");
+ return(0);
+ }
+
+ /*
+ * Looks good. Record info from the packet.
+ */
+ server->leap = PKT_LEAP(rpkt->li_vn_mode);
+ server->stratum = PKT_TO_STRATUM(rpkt->stratum);
+ server->precision = rpkt->precision;
+ server->rootdelay = ntohl(rpkt->rootdelay);
+ server->rootdispersion = ntohl(rpkt->rootdispersion);
+ server->refid = rpkt->refid;
+ NTOHL_FP(&rpkt->reftime, &server->reftime);
+ NTOHL_FP(&rpkt->rec, &rec);
+ NTOHL_FP(&rpkt->xmt, &server->org);
+
+ /*
+ * Make sure the server is at least somewhat sane. If not, try
+ * again.
+ */
+ if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
+ return(0);
+ }
+
+ /*
+ * Calculate the round trip delay (di) and the clock offset (ci).
+ * We use the equations (reordered from those in the spec):
+ *
+ * d = (t2 - t3) - (t1 - t0)
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ */
+ t10 = server->org; /* pkt.xmt == t1 */
+ L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/
+
+ t23 = rec; /* pkt.rec == t2 */
+ L_SUB(&t23, &org); /* pkt->org == t3 */
+
+ /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */
+ ci = t10;
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+
+ /*
+ * Calculate di in t23 in full precision, then truncate
+ * to an s_fp.
+ */
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+
+ server->offset = ci;
+ server->delay = di;
+
+ printserver(server, stdout);
+
+ /* End of recursion if we reach stratum 1 */
+ if (server->stratum <= 1)
+ return(1);
+
+ nextia.s_addr = server->refid;
+ nextserver = addserver(&nextia);
+ DoTrace(nextserver);
+ return(1);
+}
+
+/* XXX ELIMINATE addserver (almost) identical to ntpdate.c, ntptrace.c */
+/*
+ * addserver - Allocate a new structure for server.
+ * Returns a pointer to that structure.
+ */
+static struct server *
+addserver(iap)
+struct in_addr *iap;
+{
+ register struct server *server;
+ static int toomany = 0;
+
+ if (sys_numservers >= sys_maxservers) {
+ if (!toomany) {
+ toomany = 1;
+ syslog(LOG_ERR,
+ "too many servers (> %d) specified, remainder not used",
+ sys_maxservers);
+ }
+ return(NULL);
+ }
+
+ server = (struct server *)emalloc(sizeof(struct server));
+ memset((char *)server, 0, sizeof(struct server));
+
+ server->srcadr.sin_family = AF_INET;
+ server->srcadr.sin_addr = *iap;
+ server->srcadr.sin_port = htons(NTP_PORT);
+
+ sys_servers[sys_numservers++] = server;
+
+ return(server);
+}
+/*
+ * addservbyname - determine a server's address and allocate a new structure
+ * for it. Returns a pointer to that structure.
+ */
+static struct server *
+addservbyname(serv)
+ char *serv;
+{
+ long ipaddr;
+ struct in_addr ia;
+
+ if (!getipaddr(serv, &ipaddr)) {
+ syslog(LOG_ERR, "can't find host %s\n", serv);
+ return(NULL);
+ }
+
+ ia.s_addr = ipaddr;
+ return(addserver(&ia));
+}
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * setup_io - initialize I/O data and open socket
+ */
+static void
+setup_io()
+{
+ register int i;
+ register struct recvbuf *rb;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ rb = (struct recvbuf *)
+ emalloc((sys_maxservers + 2) * sizeof(struct recvbuf));
+ freelist = 0;
+ for (i = sys_maxservers + 2; i > 0; i--) {
+ rb->next = freelist;
+ freelist = rb;
+ rb++;
+ }
+
+ fulllist = 0;
+ full_recvbufs = 0;
+ free_recvbufs = sys_maxservers + 2;
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+}
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+static void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination
+ */
+static void
+sendpkt(dest, pkt, len)
+ struct sockaddr_in *dest;
+ struct pkt *pkt;
+ int len;
+{
+ int cc;
+
+ cc = sendto(fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ if (errno != EWOULDBLOCK && errno != ENOBUFS)
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+}
+
+/*
+ * getipaddr - given a host name, return its host address
+ */
+static int
+getipaddr(host, num)
+ char *host;
+ long *num;
+{
+ struct hostent *hp;
+
+ if (decodeipaddr(host, num)) {
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(long));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * decodeipaddr - return a host address (this is crude, but careful)
+ */
+static int
+decodeipaddr(num, ipaddr)
+ char *num;
+ long *ipaddr;
+{
+ register char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ char buf[80]; /* will core dump on really stupid stuff */
+
+ cp = num;
+ *ipaddr = 0;
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != '\0')
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ *ipaddr <<= 8;
+ *ipaddr += temp;
+ }
+
+ if (i < 4)
+ return 0;
+ *ipaddr = htonl(*ipaddr);
+ return 1;
+}
+
+
+/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */
+/*
+ * printserver - print detail information for a server
+ */
+static void
+printserver(pp, fp)
+ register struct server *pp;
+ FILE *fp;
+{
+ u_fp synchdist;
+
+ synchdist = pp->rootdispersion + (pp->rootdelay/2);
+
+ if (!verbose) {
+ (void) fprintf(fp, "stratum %d, offset %s, synch distance %s",
+ pp->stratum,
+ lfptoa(&pp->offset, 6),
+ ufptoa(synchdist, 5));
+ if (pp->stratum == 1) {
+ (void) fprintf(fp, ", refid ");
+ printrefid(fp, pp);
+ }
+ (void) fprintf(fp, "\n");
+ return;
+ }
+
+ (void) fprintf(fp, "server %s, port %d\n",
+ ntoa(&pp->srcadr), ntohs(pp->srcadr.sin_port));
+
+ (void) fprintf(fp, "stratum %d, precision %d, leap %c%c\n",
+ pp->stratum, pp->precision,
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0');
+
+ (void) fprintf(fp, "refid ");
+ printrefid(fp, pp);
+
+ (void) fprintf(fp,
+ " delay %s, dispersion %s ",
+ fptoa(pp->delay, 5),
+ ufptoa(pp->dispersion, 5));
+ (void) fprintf(fp, "offset %s\n",
+ lfptoa(&pp->offset, 6));
+ (void) fprintf(fp, "rootdelay %s, rootdispersion %s",
+ fptoa(pp->rootdelay, 5), ufptoa(pp->rootdispersion, 5));
+ (void) fprintf(fp, ", synch dist %s\n",
+ ufptoa(synchdist, 5));
+
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&pp->reftime));
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&pp->org));
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&pp->xmt));
+
+ (void) fprintf(fp, "\n");
+
+}
+
+static void
+printrefid(fp, pp)
+FILE *fp;
+struct server *pp;
+{
+ char junk[5];
+ char *str;
+
+ if (pp->stratum == 1) {
+ junk[4] = 0;
+ memmove(junk, (char *)&pp->refid, 4);
+ str = junk;
+ (void) fprintf(fp, "'%s'", str);
+ } else {
+ if (nonames) {
+ str = numtoa(pp->refid);
+ (void) fprintf(fp, "[%s]", str);
+ }
+ else {
+ str = numtohost(pp->refid);
+ (void) fprintf(fp, "%s", str);
+ }
+ }
+}
+
+#if defined(NEED_VSPRINTF)
+/*
+ * This nugget for pre-tahoe 4.3bsd systems
+ */
+#if !defined(__STDC__) || !__STDC__
+#define const
+#endif
+
+int
+vsprintf(str, fmt, ap)
+ char *str;
+ const char *fmt;
+ va_list ap;
+{
+ FILE f;
+ int len;
+
+ f._flag = _IOWRT+_IOSTRG;
+ f._ptr = str;
+ f._cnt = 32767;
+ len = _doprnt(fmt, ap, &f);
+ *f._ptr = 0;
+ return (len);
+}
+#endif
diff --git a/usr.sbin/xntpd/ntptrace/ntptrace.h b/usr.sbin/xntpd/ntptrace/ntptrace.h
new file mode 100644
index 0000000..65b72fb
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/ntptrace.h
@@ -0,0 +1,36 @@
+/*
+ * ntptrace.h - declarations for the ntptrace program
+ */
+
+/*
+ * The server structure is a much simplified version of the
+ * peer structure, for ntptrace's use. Since we always send
+ * in client mode and expect to receive in server mode, this
+ * leaves only a very limited number of things we need to
+ * remember about the server.
+ */
+struct server {
+ struct sockaddr_in srcadr; /* address of remote host */
+ u_char leap; /* leap indicator */
+ u_char stratum; /* stratum of remote server */
+ s_char precision; /* server's clock precision */
+ u_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ l_fp org; /* peer's originate time stamp */
+ l_fp xmt; /* transmit time stamp */
+ u_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+};
+
+
+/*
+ * Since ntptrace isn't aware of some of the things that normally get
+ * put in an NTP packet, we fix some values.
+ */
+#define NTPTRACE_PRECISION (-6) /* use this precision */
+#define NTPTRACE_DISTANCE FP_SECOND /* distance is 1 sec */
+#define NTPTRACE_DISP FP_SECOND /* so is the dispersion */
+#define NTPTRACE_REFID (0) /* reference ID to use */
diff --git a/usr.sbin/xntpd/parse/Makefile b/usr.sbin/xntpd/parse/Makefile
new file mode 100644
index 0000000..8162377
--- /dev/null
+++ b/usr.sbin/xntpd/parse/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id$
+#
+
+PARSEFLAGS= -DCLOCK_SCHMID -DCLOCK_DCF7000 -DCLOCK_MEINBERG \
+ -DCLOCK_RAWDCF -DCLOCK_TRIMSV6
+
+CFLAGS+= -I${.CURDIR}/../include ${PARSEFLAGS}
+
+SRCS= parse.c parse_conf.c clk_meinberg.c clk_schmid.c clk_rawdcf.c \
+ clk_dcf7000.c clk_trimble.c
+
+NOMAN=
+NOPROFILE=
+LIB= parse
+
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/xntpd/parse/README b/usr.sbin/xntpd/parse/README
new file mode 100644
index 0000000..aa83aa7
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README
@@ -0,0 +1,100 @@
+PARSE reference clock driver:
+
+This directory contains the files making up the parser for
+the parse refclock driver. For reasonably sane clocks this refclock
+drivers allows a refclock implementation by just providing a
+conversion routine and the appropriate NTP parameters. Refclock
+support can run as low a 3k code with the parse refclock driver.
+
+The modules in here are designed to live in two worlds. In userlevel
+as part of the xntp daemon and in kernel land as part of a STREAMS module
+or, if someone gets to it, as part of a line discipline. Currently only
+SunOS4.x/SunOS5.x STREAMS are supported (volunteers for other vendors like HP?).
+This structure means, that refclock_parse can work with or without kernel
+support. Kernelsupport increases accuracy tremendingly. The current restriction
+of the parse driver is that it only supports SYSV type ttys and that kernel
+support is only available for Suns right now.
+
+Three kernel modules are part of this directory. These work only on
+SunOS (SunOS4 and SunOS5 (not fully tested!)).
+
+ SunOS4 (aka Solaris 1.x):
+ parsestreams.o - standard parse module for SunOS 4
+ mparsestreams.o - parse module for SunOS 4 with better
+ clock reading code (assembler)
+
+ Both modules can be loaded via modload <modulename>.
+
+ SunOS5 (aka Solaris 2.x):
+ parse - auto loadable streams module
+ (not fully tested - don't kill me if
+ it kills you machine)
+
+ To install just drop "parse" into /kernel/strmod and
+ start the daemon (SunOS5 will do the rest).
+
+The structure of the parse reference clock driver is as follows:
+
+ xntpd - contains NTP implementation and calls a reference clock
+ 127.127.8.x which is implemented by
+ refclock_parse.c
+ - which contains several refclock decriptions. These are
+ selected by the x part of the refclock address.
+ The lower two bits specify the device to use. Thus the
+ value (x % 4) determines the device to open
+ (/dev/refclock-0 - /dev/refclock-3).
+
+ The kind of clock is selected by bits 2-6. This parameter
+ selects the clock type which deterimines how I/O is done,
+ the tty parameters and the NTP parameters.
+
+ refclock_parse operates on an abstract reference clock
+ that delivers time stamps and stati. Offsets and sychron-
+ isation information is derived from this data and passed
+ on to refclock_receive of xntp which uses that data for
+ syncronisation.
+
+ The abstract reference clock is generated by the parse*
+ routines. They parse the incoming data stream from the
+ clock and convert it to the appropriate time stamps.
+ The data is also mapped int the abstract clock states
+ POWERUP - clock has no valid phase and time code
+ information
+
+ NOSYNC - Time code is not confirmed, phase is probably
+ ok.
+ SYNC - Time code and phase are correct.
+
+ A clock is trusted for a certain time (type parameter) when
+ it leaves the SYNC state. This is derived from the
+ observation that quite a few clocks can still generate good
+ time code information when losing contact to their
+ synchronisation source. When the clock does not reagain
+ synchronisation in that trust period it will be deemed
+ unsynchronised until it regains synchronisation. The same
+ will happen if xntp sees the clock unsynchronised at
+ startup.
+
+ The upper bit of x specifies that all samples delivered
+ from the clock should be used to discipline the NTP
+ loppfilter. For clock with accurate once a second time
+ information this means big improvements for time keeping.
+ A prerequisite for passing on the time stamps to
+ the loopfilter is, that the clock is in synchronised state.
+
+ parse.c These are the general routines to parse the incoming data
+ stream. Usually these routines should not require
+ modification.
+
+ clk_*.c These files hole the conversion code for the time stamps
+ and the description how the time code can be parsed and
+ where the time stamps are to be taken.
+ If you want to add a new clock type this is the file
+ you need to write in addition to mention it in
+ parse_conf.c and setting up the NTP and TTY parameters
+ in refclock_parse.c.
+
+Further information can be found in parse/README.parse and the various source
+files.
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/parse/README.new_clocks b/usr.sbin/xntpd/parse/README.new_clocks
new file mode 100644
index 0000000..5b2d29e
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.new_clocks
@@ -0,0 +1,212 @@
+Here is an attempt to sketch out what you need to do in order to
+add another clock to the parse driver:
+
+Prerequisites:
+- Does the system you want the clock connect to have
+ termio.h or termios.h ? (You need that for the parse driver)
+
+What to do:
+
+Make a conversion module (parse/clk_*.c)
+
+- What ist the time code format ?
+ - find year, month, day, hour, minute, second, status (synchronised or
+ not), possibly time zone information (you need to give the offset to UTC)
+ You will have to convert the data from a string into a struct clocktime:
+ struct clocktime /* clock time broken up from time code */
+ {
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in seconds */
+ time_t utcoffset; /* true utc time instead of date/time */
+ long flags; /* current clock status */
+ };
+
+ Conversion is usually simple and straight forward. For the flags following
+ values can be OR'ed together:
+
+ PARSEB_ANNOUNCE switch time zone warning (informational only)
+ PARSEB_POWERUP no synchronisation - clock confused (must set then)
+ PARSEB_NOSYNC timecode currently not confirmed (must set then)
+ usually on reception error when there is still a
+ chance the the generated time is still ok.
+
+ PARSEB_DST DST in effect (informational only)
+ PARSEB_UTC timecode contains UTC time (informational only)
+ PARSEB_LEAPADD LEAP addition warning (prior to leap happening - must set when imminent)
+ also used for time code that do not encode the
+ direction (as this is currently the default).
+ PARSEB_LEAPDEL LEAP deletion warning (prior to leap happening - must set when imminent)
+ PARSEB_ALTERNATE backup transmitter (informational only)
+ PARSEB_POSITION geographic position available (informational only)
+ PARSEB_LEAPSECOND actual leap second (this time code is the leap
+ second - informational only)
+
+ These are feature flags denoting items that are supported by the clock:
+ PARSEB_S_LEAP supports LEAP - might set PARSEB_LEAP
+ PARSEB_S_ANTENNA supports ANTENNA - might set PARSEB_ALTERNATE
+ PARSEB_S_PPS supports PPS time stamping
+ PARSEB_S_POSITION supports position information (GPS)
+
+ If the utctime field is non zero this value will be take as
+ time code value. This allows for conversion routines that
+ already have the utc time value. The utctime field gives the seconds
+ since Jan 1st 1970, 0:00:00. The useconds field gives the respective
+ usec value. The fields for date and time (down to second resolution)
+ will be ignored.
+
+ Conversion is done in the cvt_* routine in parse/clk_*.c files. look in
+ them for examples. The basic structure is:
+
+ struct clockformat <yourclock>_format = {
+ lots of fields for you to fill out (see below)
+ };
+
+ static cvt_<yourclock>()
+ ...
+ {
+ if (<I do not recognize my time code>) {
+ return CVT_NONE;
+ } else {
+ if (<conversion into clockformat is ok>) {
+ <set all necessary flags>;
+ return CVT_OK;
+ } else {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ }
+
+ The struct clockformat is the interface to the rest of the parse
+ driver - it holds all information necessary for finding the
+ clock message and doing the appropriate time stamping.
+
+struct clockformat
+{
+ u_long (*convert)();
+ /* conversion routine - your routine - cvt_<yourclock> */
+ void (*syncevt)();
+ /* routine for handling RS232 sync events (time stamps) - usually sync_simple */
+ u_long (*syncpps)();
+ /* PPS input routine - usually pps_simple */
+ u_long (*synth)();
+ /* time code synthesizer - usually not used - (long (*)())0 */
+ void *data;
+ /* local parameters - any parameters/data/configuration info your conversion
+ routine might need */
+ char *name;
+ /* clock format name - Name of the time code */
+ unsigned short length;
+ /* maximum length of data packet for your clock format */
+ u_long flags;
+ /* information for the parser what to look for */
+ struct timeval timeout;
+ /* buffer restart after timeout (us) - some clocks preceede new data by
+ a longer period of silence - unsually not used */
+ unsigned char startsym;
+ /* start symbol - character at the beginning of the clock data */
+ unsigned char endsym;
+ /* end symbol - character at the end of the clock data */
+ unsigned char syncsym;
+ /* sync symbol - character that is "on time" - where the time stamp should be taken */
+};
+
+ The flags:
+ F_START use startsym to find the beginning of the clock data
+ F_END use endsym to find the end of the clock data
+ SYNC_TIMEOUT packet restart after timeout in timeout field
+ SYNC_START packet start is sync event (time stamp at paket start)
+ SYNC_END packet end is sync event (time stamp at paket end)
+ SYNC_CHAR special character (syncsym) is sync event
+ SYNC_ONE PPS synchronize on 'ONE' transition
+ SYNC_ZERO PPS synchronize on 'ZERO' transition
+ SYNC_SYNTHESIZE generate intermediate time stamps (very special case!)
+ CVT_FIXEDONLY convert only in fixed configuration - (data format not
+ suitable for auto-configuration)
+
+
+ The above should have given you some hints on how to build a clk_*.c
+ file with the time code conversion. See the examples and pick a clock
+ closest to yours and tweak the code to match your clock.
+
+ In order to make your clk_*.c file usable a reference to the clockformat
+ structure must be put into parse_conf.c.
+
+TTY setup and initialisation/configuration will be done in
+xntpd/refclock_parse.c
+
+- Find out the exact tty settings for your clock (baud rate, parity,
+ stop bits, character size, ...) and note them in terms of
+ termio*.h c_cflag macros.
+
+- in xntpd/refclock_parse.c fill out a new the struct clockinfo element
+ (that allocates a new "IP" address - see comments)
+ (see all the other clocks for example)
+ struct clockinfo
+ {
+ u_long cl_flags; /* operation flags (io modes) */
+ PARSE_F_NOPOLLONLY always do async io - read whenever input comes
+ PARSE_F_POLLONLY never do async io - only read when expecting data
+ PARSE_F_PPSPPS use loopfilter PPS code (CIOGETEV)
+ PARSE_F_PPSONSECOND PPS pulses are on second
+ usually flags stay 0 as they are used only for special setups
+
+ void (*cl_poll)(); /* active poll routine */
+ The routine to call when the clock needs data sent to it in order to
+ get a time code from the clock (e.g. Trimble clock)
+ int (*cl_init)(); /* active poll init routine */
+ The routine to call for very special initializations.
+ void (*cl_end)(); /* active poll end routine */
+ The routine to call to undo any special initialisation (free memory/timers)
+ void *cl_data; /* local data area for "poll" mechanism */
+ local data for polling routines
+ u_fp cl_rootdelay; /* rootdelay */
+ NTP rottdelay estimate (usually 0)
+ u_long cl_basedelay; /* current offset - unsigned l_fp fractional par
+ time (fraction) by which the RS232 time code is delayed from the actual time.
+ t */
+ u_long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional
+ time (fraction) by which the PPS time stamp is delayed (usually 0)
+ part */
+ char *cl_id; /* ID code (usually "DCF") */
+ Refclock id - (max 4 chars)
+ char *cl_description; /* device name */
+ Name of this device.
+ char *cl_format; /* fixed format */
+ If the data format cann not ne detected automatically this is the name
+ as in clk_*.c clockformat.
+ u_char cl_type; /* clock type (ntp control) */
+ Type if clock as in clock status word (ntp control messages) - usually 0
+ u_long cl_maxunsync; /* time to trust oscillator after loosing synch
+ */
+ seconds a clock can be trusted after loosing synchronisation.
+
+ u_long cl_cflag; /* terminal io flags */
+ u_long cl_iflag; /* terminal io flags */
+ u_long cl_oflag; /* terminal io flags */
+ u_long cl_lflag; /* terminal io flags */
+ termio*.h tty modes.
+ } clockinfo[] = {
+ ...,<other clocks>,...
+ { < your parameters> },
+ };
+
+
+Well, this is very sketchy, i know. But I hope it helps a little bit.
+The best way is to look which clock comes closest to your and tweak that
+code.
+Two sorts of clocks are used with parse. Clocks that automatically send
+their time code (once a second) do not need entries in the poll routines because
+they send the data all the time. The second sort are the clocks that need a
+command sent to them in order to reply with a time code (like the Trimble
+clock).
+
+For questions: kardel@informatik.uni-erlangen.de. Please include
+an exact description on how your clock works. (initialisation,
+TTY modes, strings to be sent to it, responses received from the clock).
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/parse/README.parse b/usr.sbin/xntpd/parse/README.parse
new file mode 100644
index 0000000..660973a
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.parse
@@ -0,0 +1,142 @@
+MINI INFO:
+The following info pertains mainly to SunOS4.x in respect to installation.
+Installation for SunOS5.x (Solaris 2.x) is very simple - just drop the parse
+module into /kernel/strmod.
+All others notes about the software structure refer to both environments.
+
+#ifdef ENGLISH
+Installation of a Streams module requires knowledge in kernel generation
+and possession of "superuser" rights.
+
+This directory contains the STREAMS module code for the supported DCF/GPS
+receivers of the "parse" driver.
+The dataformat should be easy to adept for other clocks.
+
+A suitable kernel module can be generated in two ways:
+ 1) loadable driver
+ 2) linking into the kernel
+
+Solution 1 has the advantage that the kernel module is present right at system startup,
+while solution 2 avoids reconfigurating the kernel (except for VDDRV).
+
+Loadable Driver: (Kernel must be configured with VDDRV option like e.g. GENERIC)
+ make -f Makefile.kernel
+
+# make one module for each kernel architecture you intend to use this module for
+
+ make -f Makefile.kernel mparsestreams.o
+# use the above command for a version with increased time stamp precision
+# (available only for sun4c and sun4m architectures (thanks Craig Leres)
+
+Integration into kernel (refer to the Manual for complete instructions)
+ Still possible, but not recommended
+
+if you run into trouble: time@informatik.uni-erlangen.de
+
+Porting to different clock formats:
+The streams module is designed to be able to parse different time code
+packets. The parser is very simple and expects at least a start or end of packet
+character. In order to be able to distinguish time code packets a list
+of several start/end pairs and conversion routines can be defined in the
+clockformats structure. Whenever a packet delimited by any start/end pair is
+detected the conversion routines are called in a RR fashion for converting the
+time code into a clocktime structure. A return code of CVT_OK indicates a
+correct conversion.
+(This routine will be called first on the next conversion attempt). CVT_FAIL
+indicates the the packet format was detected, but the actual conversion failed
+(e.g. illegal time codes). A CVT_NONE indicates that this conversion routine
+did not recognize the packet format.
+See the simpleformat conversion routines for Meinberg clocks for examples.
+It might be possible to parse other periodically sent time codes with a fixed
+format with these simple conversion routines.
+The parser can be found in parse/*.c
+
+The actual STREAMS module is parsestreams.c. It contains some fudge factors.
+These are needed if a PPS hardware signal is sampled via the serial CD input.
+There are some emperically determined valued for sun4c type machine in there.
+Measurements have shown, that for full precision these values have to be
+determined in the actual environment, as line lengths and capacities DO matter.
+So for absolute precision you need a good oscilloscope and the license for
+hardware work.
+WARNING: DO NOT ATTEMPT TO MEASURE IF YOU ARE NOT ABSOLUTELY CERTAIN WHAT YOU
+ARE DOING.
+
+This instructions are distributed in the hope that they will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+I will not be in any case responsible for any damage cause by this
+Software/Instruction.
+
+USE AT YOUR OWN RISK.
+
+#else
+
+Die folgenden Hinweise zur Uebersetzung und Installation besiehen sich auf
+SunOS 4.x (Solaris1.x). Die Installation auf SunOS5.x (Solaris 2.x) gestaltet
+sich erheblich einfacher. Man muss nur die Daten "parse" in dem Verzeichnis
+/kernel/strmod ablegen.
+Alle anderen Hinweise zur Softwarestruktur sind fuer beide Umgebungen gueltig.
+
+Installation eines STREAMS Moduls setzt Kenntnisse in der Kerngenerierung
+und "Superuser"-Rechte vorraus.
+
+Dieses Inhaltsverzeichnis enthaelt das aktuelle Streams Modul fuer Sun.
+
+Man kann dieses Modul auf zwei Weisen in den Kern integrieren:
+ 1) direkt durch Einbinden (neuer Kern)
+ 2) als ladbarer Treiber
+
+Loesung 1 hat den Vorteil, dass das Modul gleich nach Systemstart zur
+Verfuegung steht.
+Loesung 2 besticht dadurch, das man das Modul nachtraeglich laden und
+auch debuggen kann, ohne einen neuen Kern zu booten.
+
+Fuer ein ladbares Modul muss der Kern mit der VDDRV option konfiguriert sein und das
+parsestreams.c muss mit -DVDDRV uebersetzt werden.
+
+Uebersetzung fuer ladbaren Treiber (Kern muss mit VDDRV konfiguriert sein):
+ make -f Makefile.kernel
+ bitte einmal fuer jede Kernelarchitektur, fuer die dieses Modul
+ benoetigt wird durchfuehren.
+
+ make -f Makefile.kernel mparsestreams.o
+ Das obige make erstellt eine Version, die die Rechneruhr besser
+ als SunOS abliest. Nur fur sun4c und sun4m Architekturen verfuegbar
+
+Uebersetzung als .o Modul oder vorherige Einbindung in die Kernbauumgebung:
+ Immer noch moeglich, wird aber nicht mehr empfohlen.
+
+Anpassung an andere Datenformate:
+Das Streamsmodul ist in der Lage verschiedene Datenformate zu erkennen und
+umzusetzen. Der Parser ist einfach gehalten und kann Datenpakete anhand von
+Start und Endekennzeichen unterscheiden. Jedes so erkannte Paket wird einer
+Liste von Konvertierroutinen vorgelegt (clockformats Struktur). Die
+Konvertierroutinen koennen mit drei verschiedenen Rueckgabewerten angeben,
+wie die Konvertierung verlaufen ist. CVT_OK heisst, dass die Konvertierung
+in die clocktime Struktur erfolgreich verlaufen ist. Beim naechsten
+Umsetzungsversuch wird diese Routine als erstes wieder befragt werden
+(Optimierung). CVT_FAIL bedeutet, dass zwar das Format erkannt wurde, aber
+die eigentliche Konvertierung fehlgeschlagen ist (z. B. illegale Feldwerte).
+CVT_NONE heisst, dass das Format dieser Konvertierroutine nicht erkannt wurde.
+Die simpleformat Routinen fuer Meinberg Uhren koennen als Vorlage fuer eigene
+Anpassungen an Uhren mit periodischem Zeittelegramm und festem Format genommen werden.
+Der Parser ist in parse/*.c zu finden.
+
+Das eigentliche STREAMSmodul ist parsestreams.c. Es enthaelt einige
+Korrekturfaktoren, die beim Einsatz von Hardware-PPS Signalen benoetigt werden.
+Einige empirische Werte fuer sun4c Maschinen sind schon vorgegeben. Bei exterm
+hohen Genauigkeitsanforderungen muessen diese Werte aber in der aktuellen
+Installation NEU ermittelt werden, weil die Zeiten unter anderem von
+Leitunglaengen der PPS Leitung abhaengen. Wenn Sie diese Abstimmung
+durchfuehren, benoetigen Sie ein gutes Oszilloskop und die Lizenz fuer
+Hardwarearbeiten.
+
+ACHTUNG: VERSUCHEN SIE NICHT DIESE MESSUNGEN ZU MACHEN, WENN IHNEN DIE
+VORAUSSETZUNGEN DAFUER FEHLEN !
+
+WIR GEBEN KEINE GARANTIEN
+
+Bei Schwierigkeiten email an: time@informatik.uni-erlangen.de
+
+#endif
diff --git a/usr.sbin/xntpd/parse/README.parse_clocks b/usr.sbin/xntpd/parse/README.parse_clocks
new file mode 100644
index 0000000..a3c5a80
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.parse_clocks
@@ -0,0 +1,264 @@
+The parse driver currently supports several clocks with different
+query mechanisms. In order for you to find a sample that might be
+similar to a clock you might want to integrate into parse i'll sum
+up the major features of the clocks (this information is distributed
+in the parse/clk_*.c and xntpd/refclock_parse.c files).
+
+---
+ Meinberg: 127.127.8. 0- 3 (PZF535TCXO)
+ 127.127.8. 4- 7 (PZF535OCXO)
+ 127.127.8. 8-11 (DCFUA31)
+ 127.127.8.28-31 (GPS166)
+ Meinberg: start=<STX>, end=<ETX>, sync on start
+ pattern="\2D: . . ;T: ;U: . . ; \3"
+ pattern="\2 . . ; ; : : ; \3"
+ pattern="\2 . . ; ; : : ; : ; ; . . "
+
+ Meinberg is a german manufacturer of time code receivers. Those clocks
+ have a pretty common output format in the stock version. In order to
+ support NTP Meinberg was so kind to produce some special versions of
+ the firmware for the use with NTP. So, if you are going to use a
+ Meinberg clock please ask whether there is a special Uni Erlangen
+ version.
+
+ General characteristics:
+ Meinberg clocks primarily output pulse per second and a describing
+ ASCII string. This string can be produced in two modes. either upon
+ the reception of a question mark or every second. NTP uses the latter
+ mechanism. The DCF77 variants have a pretty good relationship between
+ RS232 time code and the PPS signal while the GPS receiver has no fixed
+ timeing between the datagram and the pulse (you need to use PPS with
+ GPS!) on DCF77 you might get away without the PPS signal.
+
+ The preferred tty setting for Meinberg is:
+ CFLAG (B9600|CS7|PARENB|CREAD|HUPCL)
+ IFLAG (IGNBRK|IGNPAR|ISTRIP)
+ OFLAG 0
+ LFLAG 0
+
+ The clock is run at datagram once per second.
+ Stock dataformat is:
+
+ <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
+ pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3
+ 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2
+
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+
+ For the university of Erlangen a special format was implemented to support
+ LEAP announcement and anouncement of alternate antenna.
+
+ Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg)
+
+ The use of this software release (or higher) is *ABSOLUTELY*
+ recommended (ask for PZFUERL version as some minor HW fixes have
+ been introduced) due to the LEAP second support and UTC indication.
+ The standard timecode does not indicate when the timecode is in
+ UTC (by front panel configuration) thus we have no chance to find
+ the correct utc offset. For the standard format do not ever use
+ UTC display as this is not detectable in the time code !!!
+
+ <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
+ pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3
+ 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <U> = 'U' UTC time display
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+ <L> = 'A' LEAP second announcement
+ <R> = 'R' alternate antenna
+
+ Meinberg GPS166 receiver
+
+ You must get the Uni-Erlangen firmware for the GPS receiver support
+ to work to full satisfaction !
+
+ <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
+ *
+ 000000000111111111122222222223333333333444444444455555555556666666
+ 123456789012345678901234567890123456789012345678901234567890123456
+ \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03
+ *
+
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <+/->,<00:00> = offset to UTC
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <U> = 'U' UTC time display
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+ <L> = 'A' LEAP second announcement
+ <R> = 'R' alternate antenna (reminiscent of PZF535) usually ' '
+ <L> = 'L' on 23:59:60
+
+
+ For the Meinberg parse look into clock_meinberg.c
+
+---
+ RAWDCF: 127.127.8.20-23 (Conrad receiver module - delay 258ms)
+ 127.127.8.24-27 (FAU receiver - delay 210ms)
+ 127.127.8.40-43 (Boeder receiver - delay 258ms)
+ RAWDCF: end=TIMEOUT>1.5s, sync each char (any char), generate psuedo time
+ codes, fixed format
+
+ direct DCF77 code input
+ In Europe it is relatively easy/cheap the receive the german time code
+ transmitter DCF77. The simplest version to process its signal is to
+ feed the 100/200ms pulse of the demodulated AM signal via a level
+ converter to an RS232 port at 50Baud. parse/clk_rawdcf.c holds all
+ necessary decoding logic for the time code which is transmitted each
+ minute for one minute. A bit of the time code is sent once a second.
+
+ The preferred tty setting is:
+ CFLAG (B50|CS8|CREAD|CLOCAL)
+ IFLAG 0
+ OFLAG 0
+ LFLAG 0
+
+ DCF77 raw time code
+
+ From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ und Berlin, Maerz 1989
+
+ Timecode transmission:
+ AM:
+ time marks are send every second except for the second before the
+ next minute mark
+ time marks consist of a reduction of transmitter power to 25%
+ of the nominal level
+ the falling edge is the time indication (on time)
+ time marks of a 100ms duration constitute a logical 0
+ time marks of a 200ms duration constitute a logical 1
+ FM:
+ see the spec. (basically a (non-)inverted psuedo random phase shift)
+
+ Encoding:
+ Second Contents
+ 0 - 10 AM: free, FM: 0
+ 11 - 14 free
+ 15 R - alternate antenna
+ 16 A1 - expect zone change (1 hour before)
+ 17 - 18 Z1,Z2 - time zone
+ 0 0 illegal
+ 0 1 MEZ (MET)
+ 1 0 MESZ (MED, MET DST)
+ 1 1 illegal
+ 19 A2 - expect leap insertion/deletion (1 hour before)
+ 20 S - start of time code (1)
+ 21 - 24 M1 - BCD (lsb first) Minutes
+ 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ 28 P1 - Minute Parity (even)
+ 29 - 32 H1 - BCD (lsb first) Hours
+ 33 - 34 H10 - BCD (lsb first) 10 Hours
+ 35 P2 - Hour Parity (even)
+ 36 - 39 D1 - BCD (lsb first) Days
+ 40 - 41 D10 - BCD (lsb first) 10 Days
+ 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ 45 - 49 MO - BCD (lsb first) Month
+ 50 MO0 - 10 Months
+ 51 - 53 Y1 - BCD (lsb first) Years
+ 54 - 57 Y10 - BCD (lsb first) 10 Years
+ 58 P3 - Date Parity (even)
+ 59 - usually missing (minute indication), except for leap insertion
+
+---
+ Schmid clock: 127.127.8.16-19
+ Schmid clock: needs poll, binary input, end='\xFC', sync start
+
+ The Schmid clock is a DCF77 receiver that sends a binary
+ time code at the reception of a flag byte. The contents
+ if the flag byte determined the time code format. The
+ binary time code is delimited by the byte 0xFC.
+
+ TTY setup is:
+ CFLAG (B1200|CS8|CREAD|CLOCAL)
+ IFLAG 0
+ OFLAG 0
+ LFLAG 0
+
+ The command to Schmid's DCF77 clock is a single byte; each bit
+ allows the user to select some part of the time string, as follows (the
+ output for the lsb is sent first).
+
+ Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths
+ Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy
+ Bit 2: week day, 1 byte (unused here)
+ Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here)
+ Bit 4: clock status, 1 byte, 0=time invalid,
+ 1=time from crystal backup,
+ 3=time from DCF77
+ Bit 5: transmitter status, 1 byte,
+ bit 0: backup antenna
+ bit 1: time zone change within 1h
+ bit 3,2: TZ 01=MEST, 10=MET
+ bit 4: leap second will be
+ added within one hour
+ bits 5-7: Zero
+ Bit 6: time in backup mode, units of 5 minutes (unused here)
+
+
+---
+ Trimble SV6: 127.127.8.32-35
+ Trimble SV6: needs poll, ascii timecode, start='>', end='<',
+ query='>QTM<', eol='<'
+
+ Trimble SV6 is a GPS receiver with PPS output. It needs to be polled.
+ It also need a special tty mode setup (EOL='<').
+
+ TTY setup is:
+ CFLAG (B4800|CS8|CREAD)
+ IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+ OFLAG (OPOST|ONLCR)
+ LFLAG (ICANON|ECHOK)
+
+ Special flags are:
+ PARSE_F_PPSPPS - use CIOGETEV for PPS time stamping
+ PARSE_F_PPSONSECOND - the time code is not related to
+ the PPS pulse (so use the time code
+ only for the second epoch)
+
+ Timecode
+ 0000000000111111111122222222223333333 / char
+ 0123456789012345678901234567890123456 \ posn
+ >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ ----33445566600112222BB7__-_____--99- Parse
+ >RTM 1 ;* <", Check
+
+---
+ ELV DCF7000: 127.127.8.12-15
+ ELV DCF7000: end='\r', pattern=" - - - - - - - \r"
+
+ The ELV DCF7000 is a cheap DCF77 receiver sending each second
+ a time code (though not very precise!) delimited by '`r'
+
+ Timecode
+ YY-MM-DD-HH-MM-SS-FF\r
+
+ FF&0x1 - DST
+ FF&0x2 - DST switch warning
+ FF&0x4 - unsynchronised
+
diff --git a/usr.sbin/xntpd/parse/clk_dcf7000.c b/usr.sbin/xntpd/parse/clk_dcf7000.c
new file mode 100644
index 0000000..82e7915
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_dcf7000.c
@@ -0,0 +1,150 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_DCF7000)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_dcf7000.c,v 3.12 1994/05/30 10:19:57 kardel Exp
+ *
+ * clk_dcf7000.c,v 3.12 1994/05/30 10:19:57 kardel Exp
+ *
+ * ELV DCF7000 module
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+static struct format dcf7000_fmt =
+{ /* ELV DCF7000 */
+ {
+ { 6, 2}, { 3, 2}, { 0, 2},
+ { 12, 2}, { 15, 2}, { 18, 2},
+ { 9, 2}, { 21, 2},
+ },
+ " - - - - - - - \r",
+ 0
+};
+
+static u_long cvt_dcf7000();
+
+clockformat_t clock_dcf7000 =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_dcf7000, /* ELV DCF77 conversion */
+ syn_simple, /* easy time stamps */
+ (u_long (*)())0, /* no direct PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&dcf7000_fmt, /* conversion configuration */
+ "ELV DCF7000", /* ELV clock */
+ 24, /* string buffer */
+ F_END|SYNC_END, /* END packet delimiter / synchronisation */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\0',
+ '\r',
+ '\0'
+};
+
+/*
+ * cvt_dcf7000
+ *
+ * convert dcf7000 type format
+ */
+static u_long
+cvt_dcf7000(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+ long flags;
+
+ clock->flags = 0;
+ clock->usecond = 0;
+
+ if (Stoi(f, &flags, format->field_offsets[O_FLAGS].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ if (flags & 0x1)
+ clock->utcoffset = -2*60*60;
+ else
+ clock->utcoffset = -1*60*60;
+
+ if (flags & 0x2)
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (flags & 0x4)
+ clock->flags |= PARSEB_NOSYNC;
+ }
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_DCF7000) */
+
+/*
+ * History:
+ *
+ * clk_dcf7000.c,v
+ * Revision 3.12 1994/05/30 10:19:57 kardel
+ * LONG cleanup
+ *
+ * Revision 3.11 1994/02/02 17:45:14 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.6 1993/10/09 15:01:27 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/03 19:10:41 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.4 1993/09/27 21:08:02 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.3 1993/09/26 23:40:20 kardel
+ * new parse driver logic
+ *
+ * Revision 3.2 1993/07/09 11:37:15 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:14 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_meinberg.c b/usr.sbin/xntpd/parse/clk_meinberg.c
new file mode 100644
index 0000000..10389cf
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_meinberg.c
@@ -0,0 +1,473 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_MEINBERG)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_meinberg.c,v 3.15 1994/05/30 10:19:59 kardel Exp
+ *
+ * clk_meinberg.c,v 3.15 1994/05/30 10:19:59 kardel Exp
+ *
+ * Meinberg clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * The Meinberg receiver every second sends a datagram of the following form
+ * (Standard Format)
+ *
+ * <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
+ * pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3
+ * 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ *
+ * For the university of Erlangen a special format was implemented to support
+ * LEAP announcement and anouncement of alternate antenna.
+ *
+ * Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg)
+ *
+ * The use of this software release (or higher) is *ABSOLUTELY*
+ * recommended (ask for PZFUERL version as some minor HW fixes have
+ * been introduced) due to the LEAP second support and UTC indication.
+ * The standard timecode does not indicate when the timecode is in
+ * UTC (by front panel configuration) thus we have no chance to find
+ * the correct utc offset. For the standard format do not ever use
+ * UTC display as this is not detectable in the time code !!!
+ *
+ * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
+ * pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3
+ * 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <U> = 'U' UTC time display
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ * <L> = 'A' LEAP second announcement
+ * <R> = 'R' alternate antenna
+ *
+ * Meinberg GPS166 receiver
+ *
+ * You must get the Uni-Erlangen firmware for the GPS receiver support
+ * to work to full satisfaction !
+ *
+ * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
+ *
+ * 000000000111111111122222222223333333333444444444455555555556666666
+ * 123456789012345678901234567890123456789012345678901234567890123456
+ * \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03
+ *
+ *
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <+/->,<00:00> = offset to UTC
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <U> = 'U' UTC time display
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ * <L> = 'A' LEAP second announcement
+ * <R> = 'R' alternate antenna (reminiscent of PZF535) usually ' '
+ * <L> = 'L' on 23:59:60
+ */
+
+static struct format meinberg_fmt[] =
+{
+ {
+ {
+ { 3, 2}, { 6, 2}, { 9, 2},
+ { 18, 2}, { 21, 2}, { 24, 2},
+ { 14, 1}, { 27, 4}, { 29, 1},
+ },
+ "\2D: . . ;T: ;U: . . ; \3",
+ 0
+ },
+ { /* special extended FAU Erlangen extended format */
+ {
+ { 1, 2}, { 4, 2}, { 7, 2},
+ { 14, 2}, { 17, 2}, { 20, 2},
+ { 11, 1}, { 25, 4}, { 27, 1},
+ },
+ "\2 . . ; ; : : ; \3",
+ MBG_EXTENDED
+ },
+ { /* special extended FAU Erlangen GPS format */
+ {
+ { 1, 2}, { 4, 2}, { 7, 2},
+ { 14, 2}, { 17, 2}, { 20, 2},
+ { 11, 1}, { 32, 8}, { 35, 1},
+ { 25, 2}, { 28, 2}, { 24, 1}
+ },
+ "\2 . . ; ; : : ; : ; ; . . ",
+ 0
+ }
+};
+
+static u_long cvt_meinberg();
+static u_long cvt_mgps();
+
+clockformat_t clock_meinberg[] =
+{
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_meinberg, /* Meinberg conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[0], /* conversion configuration */
+ "Meinberg Standard", /* Meinberg simple format - beware */
+ 32, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ },
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_meinberg, /* Meinberg conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[1], /* conversion configuration */
+ "Meinberg Extended", /* Meinberg enhanced format */
+ 32, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ },
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_mgps, /* Meinberg GPS166 conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[2], /* conversion configuration */
+ "Meinberg GPS Extended", /* Meinberg FAU GPS format */
+ 70, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ }
+};
+
+/*
+ * cvt_meinberg
+ *
+ * convert simple type format
+ */
+static u_long
+cvt_meinberg(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+
+ clock->flags = 0;
+ clock->usecond = 0;
+
+ /*
+ * in the extended timecode format we have also the
+ * indication that the timecode is in UTC
+ * for compatibilty reasons we start at the USUAL
+ * offset (POWERUP flag) and know that the UTC indication
+ * is the character before the powerup flag
+ */
+ if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U'))
+ {
+ /*
+ * timecode is in UTC
+ */
+ clock->utcoffset = 0; /* UTC */
+ clock->flags |= PARSEB_UTC;
+ }
+ else
+ {
+ /*
+ * only calculate UTC offset if MET/MED is in time code
+ * or we have the old time code format, where we do not
+ * know whether it is UTC time or MET/MED
+ * pray that nobody switches to UTC in the standard time code
+ * ROMS !!!!
+ */
+ switch (buffer[format->field_offsets[O_ZONE].offset])
+ {
+ case ' ':
+ clock->utcoffset = -1*60*60; /* MET */
+ break;
+
+ case 'S':
+ clock->utcoffset = -2*60*60; /* MED */
+ break;
+
+ default:
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ }
+
+ /*
+ * gather status flags
+ */
+ if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
+ clock->flags |= PARSEB_DST;
+
+ if (f[0] == '#')
+ clock->flags |= PARSEB_POWERUP;
+
+ if (f[1] == '*')
+ clock->flags |= PARSEB_NOSYNC;
+
+ if (f[3] == '!')
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (format->flags & MBG_EXTENDED)
+ {
+ clock->flags |= PARSEB_S_LEAP;
+ clock->flags |= PARSEB_S_ANTENNA;
+
+ /*
+ * DCF77 does not encode the direction -
+ * so we take the current default -
+ * earth slowing down
+ */
+ if (f[4] == 'A')
+ clock->flags |= PARSEB_LEAPADD;
+
+ if (f[5] == 'R')
+ clock->flags |= PARSEB_ALTERNATE;
+ }
+ return CVT_OK;
+ }
+ }
+}
+
+/*
+ * cvt_mgps
+ *
+ * convert Meinberg GPS format
+ */
+static u_long
+cvt_mgps(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ long h;
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+
+ clock->flags = PARSEB_S_LEAP|PARSEB_S_POSITION;
+
+ clock->usecond = 0;
+
+ /*
+ * calculate UTC offset
+ */
+ if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h,
+ format->field_offsets[O_UTCHOFFSET].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock->utcoffset,
+ format->field_offsets[O_UTCMOFFSET].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ clock->utcoffset += TIMES60(h);
+
+ if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-')
+ {
+ clock->utcoffset = -clock->utcoffset;
+ }
+ }
+
+ /*
+ * gather status flags
+ */
+ if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
+ clock->flags |= PARSEB_DST;
+
+ if ((f[0] == 'U') ||
+ (clock->utcoffset == 0))
+ clock->flags |= PARSEB_UTC;
+
+ /*
+ * no sv's seen - no time & position
+ */
+ if (f[1] == '#')
+ clock->flags |= PARSEB_POWERUP;
+
+ /*
+ * at least one sv seen - time (for last position)
+ */
+ if (f[2] == '*')
+ clock->flags |= PARSEB_NOSYNC;
+ else
+ if (!(clock->flags & PARSEB_POWERUP))
+ clock->flags |= PARSEB_POSITION;
+
+ /*
+ * oncoming zone switch
+ */
+ if (f[4] == '!')
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ /*
+ * oncoming leap second
+ * data format does not (yet) specify whether
+ * to add or to delete a second - thus we
+ * pick the current default
+ */
+ if (f[5] == 'A')
+ clock->flags |= PARSEB_LEAPADD;
+
+ /*
+ * this is the leap second
+ */
+ if (f[7] == 'L')
+ clock->flags |= PARSEB_LEAPSECOND;
+
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_MEINBERG) */
+
+/*
+ * History:
+ *
+ * clk_meinberg.c,v
+ * Revision 3.15 1994/05/30 10:19:59 kardel
+ * LONG cleanup
+ *
+ * Revision 3.14 1994/02/20 13:04:37 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.13 1994/02/02 17:45:21 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.11 1994/01/25 19:05:10 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.10 1994/01/23 17:21:54 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.9 1993/10/30 09:44:38 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.8 1993/10/22 14:27:48 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.7 1993/10/09 15:01:30 kardel
+ * file structure unified
+ *
+ * Revision 3.6 1993/10/03 19:10:43 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/27 21:08:04 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.4 1993/09/26 23:40:22 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/08/18 09:29:32 kardel
+ * GPS format is somewhat variable length - variable length part holds position
+ *
+ * Revision 3.2 1993/07/09 11:37:16 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:17 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_rawdcf.c b/usr.sbin/xntpd/parse/clk_rawdcf.c
new file mode 100644
index 0000000..042e5d3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_rawdcf.c
@@ -0,0 +1,580 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_RAWDCF)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_rawdcf.c,v 3.16 1994/05/31 20:02:40 kardel Exp
+ *
+ * clk_rawdcf.c,v 3.16 1994/05/31 20:02:40 kardel Exp
+ *
+ * Raw DCF77 pulse clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+#ifdef PARSESTREAM
+#include "sys/parsestreams.h"
+#endif
+
+#ifndef PARSEKERNEL
+#include "ntp_stdlib.h"
+#endif
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+static u_long cvt_rawdcf();
+static u_long pps_rawdcf();
+static u_long snt_rawdcf();
+
+clockformat_t clock_rawdcf =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_rawdcf, /* raw dcf input conversion */
+ (void (*)())0, /* no character bound synchronisation */
+ pps_rawdcf, /* examining PPS information */
+ snt_rawdcf, /* synthesize time code from input */
+ (void *)0, /* buffer bit representation */
+ "RAW DCF77 Timecode", /* direct decoding / time synthesis */
+ 61, /* bit buffer */
+ SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE|CVT_FIXEDONLY,
+ /* catch all transitions, buffer restart on timeout, fixed configuration only */
+ 0, /* no private data (currently in input buffer) */
+ { 1, 500000}, /* restart after 1.5 seconds */
+ '\0',
+ '\0',
+ '\0'
+};
+
+static struct dcfparam
+{
+ unsigned char onebits[60];
+ unsigned char zerobits[60];
+} dcfparam =
+{
+ "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */
+ "--------------------s-------p------p----------------------p" /* 'ZERO' representation */
+};
+
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+static u_long ext_bf(buf, idx, zero)
+ register char *buf;
+ register int idx;
+ register char *zero;
+{
+ register u_long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != zero[i]);
+ }
+ return sum;
+}
+
+static unsigned pcheck(buf, idx, zero)
+ register char *buf;
+ register int idx;
+ register char *zero;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != zero[i]);
+
+ return psum;
+}
+
+static u_long convert_rawdcf(buffer, size, dcfparam, clock)
+ register unsigned char *buffer;
+ register int size;
+ register struct dcfparam *dcfparam;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *b = dcfparam->onebits;
+ register unsigned char *c = dcfparam->zerobits;
+ register int i;
+
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%s\"\n", buffer));
+
+ if (size < 57)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size);
+#endif
+ return CVT_NONE;
+ }
+
+ for (i = 0; i < 58; i++)
+ {
+ if ((*s != *b) && (*s != *c))
+ {
+ /*
+ * we only have two types of bytes (ones and zeros)
+ */
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer);
+#endif
+ return CVT_NONE;
+ }
+ b++;
+ c++;
+ s++;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S, dcfparam->zerobits) == 1) &&
+ pcheck(buffer, DCF_P_P1, dcfparam->zerobits) &&
+ pcheck(buffer, DCF_P_P2, dcfparam->zerobits) &&
+ pcheck(buffer, DCF_P_P3, dcfparam->zerobits))
+ {
+ /*
+ * buffer OK
+ */
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n"));
+
+ clock->flags = PARSEB_S_ANTENNA|PARSEB_S_LEAP;
+ clock->utctime= 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10, dcfparam->zerobits);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1, dcfparam->zerobits);
+ clock->hour = ext_bf(buffer, DCF_H10, dcfparam->zerobits);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1, dcfparam->zerobits);
+ clock->day = ext_bf(buffer, DCF_D10, dcfparam->zerobits);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1, dcfparam->zerobits);
+ clock->month = ext_bf(buffer, DCF_MO0, dcfparam->zerobits);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO, dcfparam->zerobits);
+ clock->year = ext_bf(buffer, DCF_Y10, dcfparam->zerobits);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1, dcfparam->zerobits);
+
+ switch (ext_bf(buffer, DCF_Z, dcfparam->zerobits))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -1*60*60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= PARSEB_DST;
+ clock->utcoffset = -2*60*60;
+ break;
+
+ default:
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n"));
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (ext_bf(buffer, DCF_A1, dcfparam->zerobits))
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2, dcfparam->zerobits))
+ clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
+
+ if (ext_bf(buffer, DCF_R, dcfparam->zerobits))
+ clock->flags |= PARSEB_ALTERNATE;
+
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%lx\n",
+ (int)clock->hour, (int)clock->minute, (int)clock->day, (int)clock->month,(int) clock->year,
+ (u_long)clock->flags));
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer);
+#endif
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+/*
+ * raw dcf input routine - needs to fix up 50 baud
+ * characters for 1/0 decision
+ */
+static u_long cvt_rawdcf(buffer, size, param, clock)
+ register unsigned char *buffer;
+ register int size;
+ register void *param;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *e = buffer + size;
+ register unsigned char *b = dcfparam.onebits;
+ register unsigned char *c = dcfparam.zerobits;
+ register unsigned rtc = CVT_NONE;
+ register unsigned int i, lowmax, highmax, cutoff, span;
+#define BITS 9
+ unsigned char histbuf[BITS];
+ /*
+ * the input buffer contains characters with runs of consecutive
+ * bits set. These set bits are an indication of the DCF77 pulse
+ * length. We assume that we receive the pulse at 50 Baud. Thus
+ * a 100ms pulse would generate a 4 bit train (20ms per bit and
+ * start bit)
+ * a 200ms pulse would create all zeroes (and probably a frame error)
+ */
+
+ for (i = 0; i < BITS; i++)
+ {
+ histbuf[i] = 0;
+ }
+
+ cutoff = 0;
+ lowmax = 0;
+
+ while (s < e)
+ {
+ register unsigned int ch = *s ^ 0xFF;
+ /*
+ * these lines are left as an excercise to the reader 8-)
+ */
+ if (!((ch+1) & ch) || !*s)
+ {
+
+ for (i = 0; ch; i++)
+ {
+ ch >>= 1;
+ }
+
+ *s = i;
+ histbuf[i]++;
+ cutoff += i;
+ lowmax++;
+ }
+ else
+ {
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer));
+ *s = ~0;
+ rtc = CVT_FAIL|CVT_BADFMT;
+ }
+ s++;
+ }
+
+ if (lowmax)
+ {
+ cutoff /= lowmax;
+ }
+ else
+ {
+ cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */
+ }
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff));
+
+ lowmax = 0;
+ highmax = 0;
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:"));
+ for (i = 0; i <= cutoff; i++)
+ {
+ lowmax+=histbuf[i] * i;
+ highmax += histbuf[i];
+ parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
+ }
+ parseprintf(DD_RAWDCF, (" <M>"));
+
+ lowmax += highmax / 2;
+
+ if (highmax)
+ {
+ lowmax /= highmax;
+ }
+ else
+ {
+ lowmax = 0;
+ }
+
+ highmax = 0;
+ cutoff = 0;
+
+ for (; i < BITS; i++)
+ {
+ highmax+=histbuf[i] * i;
+ cutoff +=histbuf[i];
+ parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
+ }
+ parseprintf(DD_RAWDCF,("\n"));
+
+ if (cutoff)
+ {
+ highmax /= cutoff;
+ }
+ else
+ {
+ highmax = BITS-1;
+ }
+
+ span = cutoff = lowmax;
+ for (i = lowmax; i <= highmax; i++)
+ {
+ if (histbuf[cutoff] > histbuf[i])
+ {
+ cutoff = i;
+ span = i;
+ }
+ else
+ if (histbuf[cutoff] == histbuf[i])
+ {
+ span = i;
+ }
+ }
+
+ cutoff = (cutoff + span) / 2;
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff));
+
+ s = buffer;
+ while ((s < e) && *c && *b)
+ {
+ if (*s == (unsigned char)~0)
+ {
+ *s = '?';
+ }
+ else
+ {
+ *s = (*s >= cutoff) ? *b : *c;
+ }
+ s++;
+ b++;
+ c++;
+ }
+
+ return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, &dcfparam, clock) : rtc;
+}
+
+/*
+ * pps_rawdcf
+ *
+ * currently a very stupid version - should be extended to decode
+ * also ones and zeros (which is easy)
+ */
+/*ARGSUSED*/
+static u_long pps_rawdcf(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ if (status)
+ {
+ parseio->parse_dtime.parse_ptime = *ptime;
+ parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+
+ return CVT_NONE;
+}
+
+/*ARGSUSED*/
+static u_long snt_rawdcf(parseio, ptime)
+ register parse_t *parseio;
+ register timestamp_t *ptime;
+{
+ clocktime_t clock;
+ u_long cvtrtc;
+ time_t t;
+
+ /*
+ * start at last sample and add second index - gross, may have to be much more careful
+ */
+ if (convert_rawdcf(parseio->parse_ldata, parseio->parse_ldsize - 1, &dcfparam, &clock) == CVT_OK)
+ {
+ if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1)
+ {
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time conversion FAILED\n"));
+ return CVT_FAIL|cvtrtc;
+ }
+ }
+ else
+ {
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: data conversion FAILED\n"));
+ return CVT_NONE;
+ }
+
+ parseio->parse_dtime.parse_stime = *ptime;
+
+ t += parseio->parse_index - 1;
+
+ /*
+ * time stamp
+ */
+#ifdef PARSEKERNEL
+ parseio->parse_dtime.parse_time.tv.tv_sec = t;
+ parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond;
+#else
+ parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
+ TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
+#endif
+
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1));
+
+ return updatetimeinfo(parseio, t, clock.usecond, clock.flags);
+}
+#endif /* defined(PARSE) && defined(CLOCK_RAWDCF) */
+
+/*
+ * History:
+ *
+ * clk_rawdcf.c,v $
+ * Revision 3.16 1994/05/31 20:02:40 kardel
+ * sync on ONE transition
+ *
+ * Revision 3.15 1994/05/30 10:20:01 kardel
+ * LONG cleanup
+ *
+ * Revision 3.14 1994/05/12 12:49:09 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.13 1994/03/10 19:00:43 kardel
+ * clear utctime field to avoid confusion on synthesize time stamps
+ *
+ * Revision 3.12 1994/02/20 13:04:39 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.11 1994/02/02 17:45:23 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.9 1994/01/25 19:05:12 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.8 1994/01/22 11:24:11 kardel
+ * fixed PPS handling
+ *
+ * Revision 3.7 1993/10/30 09:44:41 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.6 1993/10/03 19:10:45 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/27 21:08:07 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.4 1993/09/26 23:40:25 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/09/01 21:44:54 kardel
+ * conditional cleanup
+ *
+ * Revision 3.2 1993/07/09 11:37:18 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:19 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_schmid.c b/usr.sbin/xntpd/parse/clk_schmid.c
new file mode 100644
index 0000000..457a659
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_schmid.c
@@ -0,0 +1,217 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_SCHMID)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_schmid.c,v 3.16 1994/05/30 10:20:03 kardel Exp
+ *
+ * clk_schmid.c,v 3.16 1994/05/30 10:20:03 kardel Exp
+ *
+ * Schmid clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * Description courtesy of Adam W. Feigin et. al (Swisstime iis.ethz.ch)
+ *
+ * The command to Schmid's DCF77 clock is a single byte; each bit
+ * allows the user to select some part of the time string, as follows (the
+ * output for the lsb is sent first).
+ *
+ * Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths
+ * Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy
+ * Bit 2: week day, 1 byte (unused here)
+ * Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here)
+ * Bit 4: clock status, 1 byte, 0=time invalid,
+ * 1=time from crystal backup,
+ * 3=time from DCF77
+ * Bit 5: transmitter status, 1 byte,
+ * bit 0: backup antenna
+ * bit 1: time zone change within 1h
+ * bit 3,2: TZ 01=MEST, 10=MET
+ * bit 4: leap second will be
+ * added within one hour
+ * bits 5-7: Zero
+ * Bit 6: time in backup mode, units of 5 minutes (unused here)
+ *
+ */
+#define WS_TIME 0x01
+#define WS_SIGNAL 0x02
+
+#define WS_ALTERNATE 0x01
+#define WS_ANNOUNCE 0x02
+#define WS_TZ 0x0c
+#define WS_MET 0x08
+#define WS_MEST 0x04
+#define WS_LEAP 0x10
+
+static u_long cvt_schmid();
+
+clockformat_t clock_schmid =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_schmid, /* Schmid conversion */
+ syn_simple, /* easy time stamps */
+ (u_long (*)())0, /* not direct PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)0, /* conversion configuration */
+ "Schmid", /* Schmid receiver */
+ 12, /* binary data buffer */
+ F_END|SYNC_START, /* END packet delimiter / synchronisation */
+ 0, /* no private data (complete messages) */
+ { 0, 0},
+ '\0',
+ (unsigned char)'\375',
+ '\0'
+};
+
+
+static u_long
+cvt_schmid(buffer, size, format, clock)
+ register unsigned char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if ((size != 11) || (buffer[10] != (unsigned char)'\375'))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (buffer[0] > 23 || buffer[1] > 59 || buffer[2] > 59 || buffer[3] > 9) /* Time */
+ {
+ return CVT_FAIL|CVT_BADTIME;
+ }
+ else
+ if (buffer[4] < 1 || buffer[4] > 31 || buffer[5] < 1 || buffer[5] > 12
+ || buffer[6] > 99)
+ {
+ return CVT_FAIL|CVT_BADDATE;
+ }
+ else
+ {
+ clock->hour = buffer[0];
+ clock->minute = buffer[1];
+ clock->second = buffer[2];
+ clock->usecond = buffer[3] * 100000;
+ clock->day = buffer[4];
+ clock->month = buffer[5];
+ clock->year = buffer[6];
+
+ clock->flags = 0;
+
+ switch (buffer[8] & WS_TZ)
+ {
+ case WS_MET:
+ clock->utcoffset = -1*60*60;
+ break;
+
+ case WS_MEST:
+ clock->utcoffset = -2*60*60;
+ clock->flags |= PARSEB_DST;
+ break;
+
+ default:
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (!(buffer[7] & WS_TIME))
+ {
+ clock->flags |= PARSEB_POWERUP;
+ }
+
+ if (!(buffer[7] & WS_SIGNAL))
+ {
+ clock->flags |= PARSEB_NOSYNC;
+ }
+
+ if (buffer[7] & WS_SIGNAL)
+ {
+ if (buffer[8] & WS_ALTERNATE)
+ {
+ clock->flags |= PARSEB_ALTERNATE;
+ }
+
+ if (buffer[8] & WS_ANNOUNCE)
+ {
+ clock->flags |= PARSEB_ANNOUNCE;
+ }
+
+ if (buffer[8] & WS_LEAP)
+ {
+ clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
+ }
+ }
+
+ clock->flags |= PARSEB_S_LEAP|PARSEB_S_ANTENNA;
+
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_SCHMID) */
+
+/*
+ * History:
+ *
+ * clk_schmid.c,v
+ * Revision 3.16 1994/05/30 10:20:03 kardel
+ * LONG cleanup
+ *
+ * Revision 3.15 1994/05/12 12:34:48 kardel
+ * data type cleanup
+ *
+ * Revision 3.14 1994/04/12 14:56:31 kardel
+ * fix declaration
+ *
+ * Revision 3.13 1994/02/20 13:04:41 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.12 1994/02/02 17:45:25 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.10 1994/01/25 19:05:15 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.9 1994/01/23 17:21:56 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.8 1993/11/01 20:00:18 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.7 1993/10/30 09:44:43 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.6 1993/10/09 15:01:32 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/03 19:10:47 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.4 1993/09/27 21:08:09 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.3 1993/09/26 23:40:27 kardel
+ * new parse driver logic
+ *
+ * Revision 3.2 1993/07/09 11:37:19 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:22 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimble.c b/usr.sbin/xntpd/parse/clk_trimble.c
new file mode 100644
index 0000000..4cb96e2
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimble.c
@@ -0,0 +1,140 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMSV6)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp
+ *
+ * Trimble SV6 clock support
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/* 0000000000111111111122222222223333333 / char
+ * 0123456789012345678901234567890123456 \ posn
+ * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ * ----33445566600112222BB7__-_____--99- Parse
+ * >RTM 1 ;* <", Check
+ */
+
+#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \
+ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \
+ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \
+ -1)
+#define O_USEC O_WDAY
+#define O_GPSFIX O_FLAGS
+#define O_CHKSUM O_UTCHOFFSET
+static struct format trimsv6_fmt =
+{ { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */
+ { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */
+ { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */
+ { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */
+ },
+ ">RTM 1 ;* <",
+ 0
+};
+
+static unsigned LONG cvt_trimsv6();
+
+clockformat_t clock_trimsv6 =
+{
+ (unsigned LONG (*)())0, /* XXX?: no input handling */
+ cvt_trimsv6, /* Trimble conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)&trimsv6_fmt, /* conversion configuration */
+ "Trimble SV6",
+ 37, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* XXX?: no private data (complete messages) */
+ { 0, 0},
+ '>',
+ '<',
+ '\0'
+};
+
+static unsigned LONG
+cvt_trimsv6(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ LONG gpsfix;
+ u_char calc_csum = 0;
+ long recv_csum;
+ int i;
+
+ if (!Strok(buffer, format->fixed_string)) return CVT_NONE;
+#define OFFS(x) format->field_offsets[(x)].offset
+#define STOI(x, y) \
+ Stoi(&buffer[OFFS(x)], y, \
+ format->field_offsets[(x)].length)
+ if ( STOI(O_DAY, &clock->day) ||
+ STOI(O_MONTH, &clock->month) ||
+ STOI(O_YEAR, &clock->year) ||
+ STOI(O_HOUR, &clock->hour) ||
+ STOI(O_MIN, &clock->minute) ||
+ STOI(O_SEC, &clock->second) ||
+ STOI(O_USEC, &clock->usecond)||
+ STOI(O_GPSFIX, &gpsfix)
+ ) return CVT_FAIL|CVT_BADFMT;
+
+ clock->usecond *= 1000;
+ /* Check that the checksum is right */
+ for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i];
+ recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) |
+ hexval(buffer[OFFS(O_CHKSUM)+1]);
+ if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME;
+ if (((u_char) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME;
+
+ clock->utcoffset = 0;
+
+ /* What should flags be set to ? */
+ clock->flags = PARSEB_UTC;
+
+ /* if the current GPS fix is 9 (unknown), reject */
+ if (0 > gpsfix || gpsfix > 9) clock->flags |= PARSEB_POWERUP;
+
+ return CVT_OK;
+}
+#endif /* defined(PARSE) && defined(CLOCK_TRIMSV6) */
+
+/*
+ * History:
+ *
+ * clk_trimble.c,v
+ * Revision 3.9 1994/02/02 17:45:27 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.7 1994/01/25 19:05:17 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.6 1993/10/30 09:44:45 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.5 1993/10/09 15:01:35 kardel
+ * file structure unified
+ *
+ * revision 3.4
+ * date: 1993/10/08 14:44:51; author: kardel;
+ * trimble - initial working version
+ *
+ * revision 3.3
+ * date: 1993/10/03 19:10:50; author: kardel;
+ * restructured I/O handling
+ *
+ * revision 3.2
+ * date: 1993/09/27 21:07:17; author: kardel;
+ * Trimble alpha integration
+ *
+ * revision 3.1
+ * date: 1993/09/26 23:40:29; author: kardel;
+ * new parse driver logic
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimtaip.c b/usr.sbin/xntpd/parse/clk_trimtaip.c
new file mode 100644
index 0000000..87538f1
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimtaip.c
@@ -0,0 +1,140 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMTAIP)
+/*
+ * $Header: /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp $
+ *
+ * Trimble SV6 clock support
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/* 0000000000111111111122222222223333333 / char
+ * 0123456789012345678901234567890123456 \ posn
+ * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ * ----33445566600112222BB7__-_____--99- Parse
+ * >RTM 1 ;* <", Check
+ */
+
+#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \
+ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \
+ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \
+ -1)
+#define O_USEC O_WDAY
+#define O_GPSFIX O_FLAGS
+#define O_CHKSUM O_UTCHOFFSET
+static struct format trimsv6_fmt =
+{ { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */
+ { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */
+ { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */
+ { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */
+ },
+ ">RTM 1 ;* <",
+ 0
+};
+
+static unsigned LONG cvt_trimtaip();
+
+clockformat_t clock_trimtaip =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_trimtaip, /* Trimble conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)&trimsv6_fmt, /* conversion configuration */
+ "Trimble SV6/TAIP",
+ 37, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data */
+ { 0, 0},
+ '>',
+ '<',
+ '\0'
+};
+
+static unsigned LONG
+cvt_trimtaip(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ LONG gpsfix;
+ u_char calc_csum = 0;
+ long recv_csum;
+ int i;
+
+ if (!Strok(buffer, format->fixed_string)) return CVT_NONE;
+#define OFFS(x) format->field_offsets[(x)].offset
+#define STOI(x, y) \
+ Stoi(&buffer[OFFS(x)], y, \
+ format->field_offsets[(x)].length)
+ if ( STOI(O_DAY, &clock->day) ||
+ STOI(O_MONTH, &clock->month) ||
+ STOI(O_YEAR, &clock->year) ||
+ STOI(O_HOUR, &clock->hour) ||
+ STOI(O_MIN, &clock->minute) ||
+ STOI(O_SEC, &clock->second) ||
+ STOI(O_USEC, &clock->usecond)||
+ STOI(O_GPSFIX, &gpsfix)
+ ) return CVT_FAIL|CVT_BADFMT;
+
+ clock->usecond *= 1000;
+ /* Check that the checksum is right */
+ for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i];
+ recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) |
+ hexval(buffer[OFFS(O_CHKSUM)+1]);
+ if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME;
+ if (((u_char) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME;
+
+ clock->utcoffset = 0;
+
+ /* What should flags be set to ? */
+ clock->flags = PARSEB_UTC;
+
+ /* if the current GPS fix is 9 (unknown), reject */
+ if (0 > gpsfix || gpsfix > 9) clock->flags |= PARSEB_POWERUP;
+
+ return CVT_OK;
+}
+#endif /* defined(PARSE) && defined(CLOCK_TRIMTAIP) */
+
+/*
+ * History:
+ *
+ * $Log: clk_trimble.c,v $
+ * Revision 3.9 1994/02/02 17:45:27 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.7 1994/01/25 19:05:17 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.6 1993/10/30 09:44:45 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.5 1993/10/09 15:01:35 kardel
+ * file structure unified
+ *
+ * revision 3.4
+ * date: 1993/10/08 14:44:51; author: kardel;
+ * trimble - initial working version
+ *
+ * revision 3.3
+ * date: 1993/10/03 19:10:50; author: kardel;
+ * restructured I/O handling
+ *
+ * revision 3.2
+ * date: 1993/09/27 21:07:17; author: kardel;
+ * Trimble alpha integration
+ *
+ * revision 3.1
+ * date: 1993/09/26 23:40:29; author: kardel;
+ * new parse driver logic
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimtsip.c b/usr.sbin/xntpd/parse/clk_trimtsip.c
new file mode 100644
index 0000000..8b43587
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimtsip.c
@@ -0,0 +1,477 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMTSIP)
+/*
+ * $Header: /home/ncvs/src/usr.sbin/xntpd/parse/clk_trimtsip.c,v 1.2 1995/05/30 03:54:13 rgrimes Exp $
+ *
+ * Trimble TSIP support - CURRENTLY VERY MUCH UNDER CONSTRUCTION
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+
+#include "ntp_syslog.h"
+#include "ntp_types.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * Trimble TSIP parser
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+ *
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+extern int debug;
+
+struct trimble
+{
+ u_char t_in_pkt; /* first DLE received */
+ u_char t_dle; /* subsequent DLE received */
+ u_char t_status; /* last status */
+ u_char t_error; /* last error */
+ u_short t_week; /* GPS week */
+ u_short t_weekleap; /* GPS week of next/last week */
+ u_short t_dayleap; /* day in week */
+ u_short t_gpsutc; /* GPS - UTC offset */
+ u_short t_gpsutcleap; /* offset at next/last leap */
+ u_char t_operable; /* receiver feels OK */
+ u_char t_leap; /* possible leap warning */
+};
+
+static unsigned LONG inp_tsip();
+static unsigned LONG cvt_trimtsip();
+
+struct clockformat clock_trimtsip =
+{
+ inp_tsip, /* Trimble TSIP input handler */
+ cvt_trimtsip, /* Trimble TSIP conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)0, /* no configuration data */
+ "Trimble SV6/TSIP",
+ 128, /* input buffer */
+ CVT_FIXEDONLY, /* we do our own input handling */
+ sizeof(struct trimble), /* no private data */
+ { 0, 0},
+ '\0',
+ '\0',
+ '\0'
+};
+
+#define ADDSECOND 0x01
+#define DELSECOND 0x02
+
+#define DLE 0x10
+#define ETX 0x03
+
+static unsigned LONG inp_tsip(parseio, ch, ctime)
+ register parse_t *parseio;
+ register unsigned char ch;
+ register timestamp_t *ctime;
+{
+ register struct trimble *t = (struct trimble *)parseio->parse_pdata;
+
+ if (!t)
+ return 0; /* local data not allocated - sigh! */
+
+ if (!t->t_in_pkt && ch != DLE) {
+ /* wait for start of packet */
+#ifdef DEBUG
+ if (debug > 2)
+ printf("sv6+ discarding %2.2x\n", ch);
+#endif
+ return 0;
+ }
+
+ switch (ch) {
+ case DLE:
+ if (!t->t_in_pkt) {
+ t->t_dle = 0;
+ t->t_in_pkt = 1;
+ parseio->parse_index = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_dtime.parse_stime = *ctime; /* pick up time stamp at packet start */
+ } else if (t->t_dle) {
+ /* Double DLE -> insert a DLE */
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ } else
+ t->t_dle = 1;
+ break;
+ case ETX:
+ if (t->t_dle) {
+ /* DLE,ETX -> end of packet */
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_data[parseio->parse_index] = '\0';
+ parseio->parse_ldsize = parseio->parse_index+1;
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_ldsize);
+ t->t_in_pkt = t->t_dle = 0;
+ return 1;
+ }
+ /* fall into ... */
+ default:
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ }
+
+ return (parseio->parse_index == parseio->parse_dsize-1); /* buffer full - attempt to parse (likely to fail) */
+}
+
+#define GPSORIGIN 2524953600 /* NTP origin - GPS origin in seconds */
+#define SECSPERWEEK 604800 /* seconds per week - GPS tells us about weeks */
+#define L_UF_SCALE 4294967296.0 /* scale a float fraction to l_uf units */
+
+/*
+ * mapping union for ints, floats, doubles for both input & output to the
+ * receiver
+ *
+ * CAVEAT: must disappear - non portable
+ */
+
+union {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+} uval;
+
+static float getflt P((u_char *));
+static double getdbl P((u_char *));
+static int getint P((u_char *));
+
+/*
+ * cvt_trimtsip
+ *
+ * convert TSIP type format
+ */
+static unsigned LONG
+cvt_trimtsip(buffer, size, format, clock, t)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+ register struct trimble *t;
+{
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+ register u_char cmd;
+
+ if (!t) {
+#ifdef DEBUG
+ if (debug) printf("sv6+ BAD call (t=0)\n");
+#endif
+ return CVT_NONE; /* local data not allocated - sigh! */
+ }
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("sv6+ BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return CVT_NONE;
+ }
+ else
+ {
+ cmd = buffer[1];
+
+#ifdef DEBUG
+ if (debug > 1)
+ switch(cmd)
+ {
+ case 0x41:
+ printf("sv6+ gps time: %f, %d, %f\n",
+ getflt(&mb(0)), getint(&mb(4)), getflt(&mb(6)));
+ break;
+
+ case 0x44:
+ printf("sv6+ sats: %2x, %2d %2d %2d %2d, %.2f\n",
+ mb(0), mb(1), mb(2), mb(3), mb(4), getflt(&mb(5)));
+ break;
+
+ case 0x45:
+ printf("sv6+ software: %d.%d (%d/%d/%d)\n",
+ mb(0)&0xff, mb(1)&0xff, (mb(4)&0xff)+1900, mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case 0x46:
+ printf("sv6+ health: %2x %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x48:
+ printf("sv6+ gps message: '%.22s'\n", &mb(0));
+ break;
+
+ case 0x4b:
+ printf("sv6+ status: %2d %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x4c:
+ printf("sv6+ op params: %2x %.1f %.1f %.1f %.1f\n",
+ mb(0), getflt(&mb(1)), getflt(&mb(5)),
+ getflt(&mb(9)), getflt(&mb(13)));
+ break;
+
+ case 0x4f:
+ printf("sv6+ utc data: %.3e %.3e %d %d %d %d %d\n",
+ getdbl(&mb(0)), getflt(&mb(8)), getint(&mb(18)),
+ getint(&mb(12)), getint(&mb(20)), getint(&mb(22)), getint(&mb(24)));
+ break;
+
+ case 0x54:
+ /*printf("sv6+ bias and rate: %.1fm %.2fm/s at %.1fs\n",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8))); ignore it*/
+ break;
+
+ case 0x55:
+ printf("sv6+ io opts: %2x %2x %2x %2x\n",
+ mb(0), mb(1), mb(2), mb(3));
+ break;
+
+ case 0x8f:
+ {
+#define RTOD (180.0 / 3.1415926535898)
+ double lat = getdbl(&mb(2));
+ double lng = getdbl(&mb(10));
+ printf("sv6+ last fix: %2.2x %d lat %f %c, long %f %c, alt %.2fm\n",
+ mb(1)&0xff, mb(40)&0xff,
+ ((lat < 0) ? (-lat) : (lat))*RTOD, (lat < 0 ? 'S' : 'N'),
+ ((lng < 0) ? (-lng) : (lng))*RTOD, (lng < 0 ? 'W' : 'E'),
+ getdbl(&mb(18)));
+ }
+ break;
+
+ case 0x40:
+ case 0x5b:
+ case 0x6d:
+ /* Ignore */
+ break;
+
+ default:
+ printf("sv6+ cmd ignored: %2x, length: %d\n",
+ cmd, size);
+ break;
+ }
+#endif /* DEBUG */
+
+ switch(cmd)
+ {
+ case 0x41:
+ { /* GPS time */
+ float secs = getflt(&mb(0));
+ int week = getint(&mb(4));
+ int secint;
+ float secfrac;
+ l_fp gpstime, off;
+
+ if (secs <= 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ printf("sv6+ seconds <= 0 (%e), setting POWERUP\n");
+#endif
+ clock->flags = PARSEB_POWERUP;
+ return CVT_OK;
+ }
+
+ /* time OK */
+ secint = secs; /* integer part, hopefully */
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+ secint -= getflt(&mb(6)); /* UTC offset */
+ gpstime.l_ui = week*SECSPERWEEK + secint + GPSORIGIN; /* convert to NTP time */
+ gpstime.l_uf = secfrac*L_UF_SCALE;
+
+ clock->utctime = gpstime.l_ui - JAN_1970;
+ TSFTOTVU(gpstime.l_uf, clock->usecond);
+
+ if (t->t_leap == ADDSECOND)
+ clock->flags |= PARSEB_LEAPADD;
+
+ if (t->t_leap == DELSECOND)
+ clock->flags |= PARSEB_LEAPDEL;
+
+ if (t->t_operable)
+ clock->flags &= ~(PARSEB_NOSYNC|PARSEB_POWERUP);
+ else
+ clock->flags |= PARSEB_NOSYNC;
+ return CVT_OK;
+
+ } /* case 0x41 */
+ break;
+
+ case 0x46:
+ {
+ /* sv6+ health */
+ u_char status = t->t_status = mb(0);
+ u_char error = t->t_error = mb(1);
+
+ if (status == 0 || status == 9 || status == 10 || status == 11)
+ {
+ if (!t->t_operable)
+ syslog(LOG_ERR, "Trimble clock synced");
+ t->t_operable = 1;
+ }
+ else
+ {
+ if (t->t_operable)
+ syslog(LOG_ERR, "Trimble clock unsynced");
+ t->t_operable = 0;
+ }
+ }
+ break;
+
+ case 0x4f:
+ {
+ /* UTC correction data - derive a leap warning */
+ int tls = t->t_gpsutc = getint(&mb(12)); /* current leap correction (GPS-UTC) */
+ int wnlsf = t->t_weekleap = getint(&mb(20)); /* week no of leap correction */
+ int dn = t->t_dayleap = getint(&mb(22)); /* day in week of leap correction */
+ int tlsf = t->t_gpsutcleap = getint(&mb(24)); /* new leap correction */
+ U_LONG now, leaptime;
+
+ t->t_week = getint(&mb(18)); /* current week no */
+
+ /* this stuff hasn't been tested yet... */
+ now = clock->utctime + JAN_1970; /* now in GPS seconds */
+ leaptime = (wnlsf*7 + dn)*86400; /* time of leap in GPS seconds */
+ if ((leaptime > now) && ((leaptime-now) < 86400*28))
+ {
+ /* generate a leap warning */
+ if (tlsf > tls)
+ t->t_leap = ADDSECOND;
+ else
+ t->t_leap = DELSECOND;
+ }
+ else
+ {
+ t->t_leap = 0;
+ }
+ }
+ break;
+
+ default:
+ /* it's validly formed, but we don't care about it! */
+ break;
+ }
+ }
+ return CVT_SKIP;
+}
+
+/*
+ * getflt, getdbl, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ return uval.fv;
+}
+
+static double
+getdbl(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+ return uval.dv;
+}
+
+static int
+getint(bp)
+ u_char *bp;
+{
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ if (uval.bd[2] & 0x80) /* sign-extend */
+ uval.bd[0] = uval.bd[1] = 0xff;
+ else
+ uval.bd[0] = uval.bd[1] = 0;
+ return uval.iv;
+}
+
+#endif /* defined(PARSE) && defined(CLOCK_TRIMTSIP) */
+
+/*
+ * History:
+ *
+ * $Log: clk_trimtsip.c,v $
+ * Revision 1.2 1995/05/30 03:54:13 rgrimes
+ * Remove trailing whitespace.
+ *
+ * Revision 1.1.1.1 1994/09/29 23:01:31 wollman
+ * xntp 3.4e from Dave Mills @ UDel
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/empty.c b/usr.sbin/xntpd/parse/empty.c
new file mode 100644
index 0000000..91b777a
--- /dev/null
+++ b/usr.sbin/xntpd/parse/empty.c
@@ -0,0 +1,7 @@
+/*
+ * Well, some ranlibs, ar's or compilers react funny
+ * if asked to do nothing but build empty valid files
+ * I would have preferred to a no or at least a static
+ * symbol here...
+ */
+char * _____empty__ = "empty .o file";
diff --git a/usr.sbin/xntpd/parse/parse.c b/usr.sbin/xntpd/parse/parse.c
new file mode 100644
index 0000000..11dacd9
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parse.c
@@ -0,0 +1,1309 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
+ *
+ * parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
+ *
+ * Parser module for reference clock
+ *
+ * PARSEKERNEL define switches between two personalities of the module
+ * if PARSEKERNEL is defined this module can be used with dcf77sync.c as
+ * a PARSEKERNEL kernel module. In this case the time stamps will be
+ * a struct timeval.
+ * when PARSEKERNEL is not defined NTP time stamps will be used.
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#if !(defined(lint) || defined(__GNUC__))
+static char rcsid[] = "parse.c,v 3.19 1994/01/25 19:05:20 kardel Exp";
+#endif
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "ntp_machine.h"
+
+#if defined(PARSESTREAM) && (defined(SYS_SUNOS4) || defined(SYS_SOLARIS)) && defined(STREAM)
+/*
+ * Sorry, but in SunOS 4.x AND Solaris 2.x kernels there are no
+ * mem* operations. I don't want them - bcopy, bzero
+ * are fine in the kernel
+ */
+#ifndef NTP_NEED_BOPS
+#define NTP_NEED_BOPS
+#endif
+#else
+#ifndef NTP_NEED_BOPS
+#ifndef bzero
+#define bzero(_X_, _Y_) memset(_X_, 0, _Y_)
+#define bcopy(_X_, _Y_, _Z_) memmove(_Y_, _X_, _Z_)
+#endif
+#endif
+#endif
+
+#include "parse.h"
+
+#include "ntp_stdlib.h"
+
+#ifdef PARSESTREAM
+#include "sys/parsestreams.h"
+#endif
+
+extern clockformat_t *clockformats[];
+extern unsigned short nformats;
+
+static u_long timepacket();
+
+/*
+ * strings support usually not in kernel - duplicated, but what the heck
+ */
+static int
+Strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static int
+Strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+static int
+timedout(parseio, ctime)
+ register parse_t *parseio;
+ register timestamp_t *ctime;
+{
+ struct timeval delta;
+
+#ifdef PARSEKERNEL
+ delta.tv_sec = ctime->tv.tv_sec - parseio->parse_lastchar.tv.tv_sec;
+ delta.tv_usec = ctime->tv.tv_usec - parseio->parse_lastchar.tv.tv_usec;
+ if (delta.tv_usec < 0)
+ {
+ delta.tv_sec -= 1;
+ delta.tv_usec += 1000000;
+ }
+#else
+ extern long tstouslo[];
+ extern long tstousmid[];
+ extern long tstoushi[];
+
+ l_fp delt;
+
+ delt = ctime->fp;
+ L_SUB(&delt, &parseio->parse_lastchar.fp);
+ TSTOTV(&delt, &delta);
+#endif
+
+ if (timercmp(&delta, &parseio->parse_timeout, >))
+ {
+ parseprintf(DD_PARSE, ("parse: timedout: TRUE\n"));
+ return 1;
+ }
+ else
+ {
+ parseprintf(DD_PARSE, ("parse: timedout: FALSE\n"));
+ return 0;
+ }
+}
+
+/*
+ * setup_bitmaps
+ * WARNING: NOT TO BE CALLED CONCURRENTLY WITH
+ * parse_ioread, parse_ioend, parse_ioinit
+ */
+static int
+setup_bitmaps(parseio, low, high)
+ register parse_t *parseio;
+ register unsigned short low;
+ register unsigned short high;
+{
+ register unsigned short i;
+ register int f = 0;
+ register clockformat_t *fmt;
+ register unsigned short index, mask, plen;
+
+ if ((low >= high) ||
+ (high > nformats))
+ {
+ parseprintf(DD_PARSE, ("setup_bitmaps: failed: bounds error (low=%d, high=%d, nformats=%d)\n", low, high, nformats));
+ return 0;
+ }
+
+ bzero(parseio->parse_startsym, sizeof (parseio->parse_startsym));
+ bzero(parseio->parse_endsym, sizeof (parseio->parse_endsym));
+ bzero(parseio->parse_syncsym, sizeof (parseio->parse_syncsym));
+
+ plen = 0;
+
+ parseio->parse_syncflags = 0;
+ parseio->parse_timeout.tv_sec = 0;
+ parseio->parse_timeout.tv_usec = 0;
+
+ /*
+ * gather bitmaps of possible start and end values
+ */
+ for (i=low; i < high; i++)
+ {
+ fmt = clockformats[i];
+
+ if (!(parseio->parse_flags & PARSE_FIXED_FMT) &&
+ (fmt->flags & CVT_FIXEDONLY)) {
+ if (parseio->parse_dsize < fmt->length)
+ parseio->parse_dsize = fmt->length;
+ continue;
+ }
+
+ if (fmt->flags & F_START)
+ {
+ index = fmt->startsym >> 3;
+ mask = 1 << (fmt->startsym & 0x7);
+
+ if (parseio->parse_endsym[index] & mask)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
+#endif
+ return 0;
+ }
+ else
+ {
+ parseio->parse_startsym[index] |= mask;
+ f = 1;
+ }
+ }
+
+ if (fmt->flags & F_END)
+ {
+ index = fmt->endsym >> 3;
+ mask = 1 << (fmt->endsym & 0x7);
+
+ if (parseio->parse_startsym[index] & mask)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
+#endif
+ return 0;
+ }
+ else
+ {
+ parseio->parse_endsym[index] |= mask;
+ f = 1;
+ }
+ }
+
+ if (fmt->flags & SYNC_CHAR)
+ {
+ parseio->parse_syncsym[fmt->syncsym >> 3] |= (1 << (fmt->syncsym & 0x7));
+ }
+
+ parseio->parse_syncflags |= fmt->flags & (SYNC_START|SYNC_END|SYNC_CHAR|SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE);
+
+ if (((fmt->flags & (SYNC_TIMEOUT|CVT_FIXEDONLY)) == (SYNC_TIMEOUT|CVT_FIXEDONLY)) &&
+ ((parseio->parse_timeout.tv_sec || parseio->parse_timeout.tv_usec) ? timercmp(&parseio->parse_timeout, &fmt->timeout, >) : 1))
+ {
+ parseio->parse_timeout = fmt->timeout;
+ }
+
+ if (parseio->parse_dsize < fmt->length)
+ parseio->parse_dsize = fmt->length;
+ }
+
+ if (parseio->parse_pdata)
+ {
+ FREE(parseio->parse_pdata, parseio->parse_plen);
+ parseio->parse_plen = 0;
+ parseio->parse_pdata = (void *)0;
+ }
+
+ if (!f && ((int)(high - low) > 1))
+ {
+ /*
+ * need at least one start or end symbol
+ */
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
+#endif
+ return 0;
+ }
+
+ if ((high - low == 1) && (clockformats[low]->flags & CVT_FIXEDONLY) &&
+ (clockformats[low]->plen))
+ {
+ parseio->parse_plen = clockformats[low]->plen;
+ parseio->parse_pdata = (void *)MALLOC(parseio->parse_plen);
+
+ if (!parseio->parse_pdata)
+ {
+ /*
+ * no memory
+ */
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: no memory for private data\n");
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: no memory for private data\n");
+#endif
+ return 0;
+ }
+ bzero((char *)parseio->parse_pdata, parseio->parse_plen);
+ }
+
+ return 1;
+}
+
+/*ARGSUSED*/
+int
+parse_ioinit(parseio)
+ register parse_t *parseio;
+{
+ parseprintf(DD_PARSE, ("parse_iostart\n"));
+
+ parseio->parse_plen = 0;
+ parseio->parse_pdata = (void *)0;
+
+ if (!setup_bitmaps(parseio, 0, nformats))
+ return 0;
+
+ parseio->parse_data = MALLOC(parseio->parse_dsize * 2 + 2);
+ if (!parseio->parse_data)
+ {
+ parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n"));
+ return 0;
+ }
+
+ /*
+ * leave room for '\0'
+ */
+ parseio->parse_ldata = parseio->parse_data + parseio->parse_dsize + 1;
+ parseio->parse_lformat = 0;
+ parseio->parse_badformat = 0;
+ parseio->parse_ioflags = PARSE_IO_CS7; /* usual unix default */
+ parseio->parse_flags = 0; /* true samples */
+ parseio->parse_index = 0;
+ parseio->parse_ldsize = 0;
+
+ return 1;
+}
+
+/*ARGSUSED*/
+void
+parse_ioend(parseio)
+ register parse_t *parseio;
+{
+ parseprintf(DD_PARSE, ("parse_ioend\n"));
+
+ if (parseio->parse_pdata)
+ FREE(parseio->parse_pdata, parseio->parse_plen);
+
+ if (parseio->parse_data)
+ FREE(parseio->parse_data, parseio->parse_dsize * 2 + 2);
+}
+
+/*ARGSUSED*/
+int
+parse_ioread(parseio, ch, ctime)
+ register parse_t *parseio;
+ register unsigned char ch;
+ register timestamp_t *ctime;
+{
+ register unsigned updated = CVT_NONE;
+ register unsigned short low, high;
+ register unsigned index, mask;
+
+ /*
+ * within STREAMS CSx (x < 8) chars still have the upper bits set
+ * so we normalize the characters by masking unecessary bits off.
+ */
+ switch (parseio->parse_ioflags & PARSE_IO_CSIZE)
+ {
+ case PARSE_IO_CS5:
+ ch &= 0x1F;
+ break;
+
+ case PARSE_IO_CS6:
+ ch &= 0x3F;
+ break;
+
+ case PARSE_IO_CS7:
+ ch &= 0x7F;
+ break;
+
+ case PARSE_IO_CS8:
+ break;
+ }
+
+ parseprintf(DD_PARSE, ("parse_ioread(0x%x, char=0x%x, ..., ...)\n", (unsigned int)parseio, ch & 0xFF));
+
+ if (parseio->parse_flags & PARSE_FIXED_FMT)
+ {
+ if (!clockformats[parseio->parse_lformat]->convert)
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n"));
+ return CVT_NONE;
+ }
+
+ if (clockformats[parseio->parse_lformat]->input)
+ {
+ if (clockformats[parseio->parse_lformat]->input(parseio, ch, ctime))
+ updated = timepacket(parseio); /* got a datagram - process */
+
+ low = high = 0; /* all done - just post processing */
+ }
+ else
+ {
+ low = parseio->parse_lformat;
+ high = low + 1; /* scan just one format */
+ }
+ }
+ else
+ {
+ low = 0;
+ high = nformats; /* scan all non fixed formats */
+ }
+
+ if (low != high)
+ {
+ index = ch >> 3;
+ mask = 1 << (ch & 0x7);
+
+ if ((parseio->parse_syncflags & SYNC_CHAR) &&
+ (parseio->parse_syncsym[index] & mask))
+ {
+ register clockformat_t *fmt;
+ register unsigned short i;
+ /*
+ * got a sync event - call sync routine
+ */
+
+ for (i = low; i < high; i++)
+ {
+ fmt = clockformats[i];
+
+ if ((fmt->flags & SYNC_CHAR) &&
+ (fmt->syncsym == ch))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_CHAR event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_CHAR);
+ }
+ }
+ }
+
+ if ((((parseio->parse_syncflags & SYNC_START) &&
+ (parseio->parse_startsym[index] & mask)) ||
+ (parseio->parse_index == 0)) ||
+ ((parseio->parse_syncflags & SYNC_TIMEOUT) &&
+ timedout(parseio, ctime)))
+ {
+ register unsigned short i;
+ /*
+ * packet start - re-fill buffer
+ */
+ if (parseio->parse_index)
+ {
+ /*
+ * filled buffer - thus not end character found
+ * do processing now
+ */
+ parseio->parse_data[parseio->parse_index] = '\0';
+
+ updated = timepacket(parseio);
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
+ parseio->parse_ldsize = parseio->parse_index+1;
+ if (parseio->parse_syncflags & SYNC_TIMEOUT)
+ parseio->parse_dtime.parse_stime = *ctime;
+ }
+
+ /*
+ * could be a sync event - call sync routine if needed
+ */
+ if (parseio->parse_syncflags & SYNC_START)
+ for (i = low; i < high; i++)
+ {
+ register clockformat_t *fmt = clockformats[i];
+
+ if ((parseio->parse_index == 0) ||
+ ((fmt->flags & SYNC_START) && (fmt->startsym == ch)))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_START event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_START);
+ }
+ }
+ parseio->parse_index = 1;
+ parseio->parse_data[0] = ch;
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer start\n"));
+ }
+ else
+ {
+ register unsigned short i;
+
+ if (parseio->parse_index < parseio->parse_dsize)
+ {
+ /*
+ * collect into buffer
+ */
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer[%d] = 0x%x\n", parseio->parse_index, ch));
+ parseio->parse_data[parseio->parse_index++] = ch;
+ }
+
+ if ((parseio->parse_endsym[index] & mask) ||
+ (parseio->parse_index >= parseio->parse_dsize))
+ {
+ /*
+ * packet end - process buffer
+ */
+ if (parseio->parse_syncflags & SYNC_END)
+ for (i = low; i < high; i++)
+ {
+ register clockformat_t *fmt = clockformats[i];
+
+ if ((fmt->flags & SYNC_END) && (fmt->endsym == ch))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_END event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_END);
+ }
+ }
+ parseio->parse_data[parseio->parse_index] = '\0';
+ updated = timepacket(parseio);
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
+ parseio->parse_ldsize = parseio->parse_index+1;
+ parseio->parse_index = 0;
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer end\n"));
+ }
+ }
+ }
+
+ if ((updated == CVT_NONE) &&
+ (parseio->parse_flags & PARSE_FIXED_FMT) &&
+ (parseio->parse_syncflags & SYNC_SYNTHESIZE) &&
+ ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) &&
+ clockformats[parseio->parse_lformat]->synth)
+ {
+ updated = clockformats[parseio->parse_lformat]->synth(parseio, ctime);
+ }
+
+ /*
+ * remember last character time
+ */
+ parseio->parse_lastchar = *ctime;
+
+#ifdef DEBUG
+ if ((updated & CVT_MASK) != CVT_NONE)
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated));
+ }
+#endif
+
+ parseio->parse_dtime.parse_status = updated;
+
+ return (updated & CVT_MASK) != CVT_NONE;
+}
+
+/*
+ * parse_iopps
+ *
+ * take status line indication and derive synchronisation information
+ * from it.
+ * It can also be used to decode a serial serial data format (such as the
+ * ONE, ZERO, MINUTE sync data stream from DCF77)
+ */
+/*ARGSUSED*/
+int
+parse_iopps(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ register unsigned updated = CVT_NONE;
+
+ /*
+ * PPS pulse information will only be delivered to ONE clock format
+ * this is either the last successful conversion module with a ppssync
+ * routine, or a fixed format with a ppssync routine
+ */
+ parseprintf(DD_PARSE, ("parse_iopps: STATUS %s\n", (status == SYNC_ONE) ? "ONE" : "ZERO"));
+
+ if (((parseio->parse_flags & PARSE_FIXED_FMT) ||
+ ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK)) &&
+ clockformats[parseio->parse_lformat]->syncpps &&
+ (status & clockformats[parseio->parse_lformat]->flags))
+ {
+ updated = clockformats[parseio->parse_lformat]->syncpps(parseio, status == SYNC_ONE, ptime);
+ parseprintf(DD_PARSE, ("parse_iopps: updated = 0x%x\n", updated));
+ }
+ else
+ {
+ parseprintf(DD_PARSE, ("parse_iopps: STATUS dropped\n"));
+ }
+
+ return (updated & CVT_MASK) != CVT_NONE;
+}
+
+/*
+ * parse_iodone
+ *
+ * clean up internal status for new round
+ */
+/*ARGSUSED*/
+void
+parse_iodone(parseio)
+ register parse_t *parseio;
+{
+ /*
+ * we need to clean up certain flags for the next round
+ */
+ parseprintf(DD_PARSE, ("parse_iodone: DONE\n"));
+ parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */
+}
+
+/*---------- conversion implementation --------------------*/
+
+/*
+ * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH)
+ */
+#define dysize(x) (((x) % 4) ? 365 : \
+ (((x) % 100) ? 366 : \
+ (((x) % 400) ? 365 : 366)))
+
+time_t
+parse_to_unixtime(clock, cvtrtc)
+ register clocktime_t *clock;
+ register u_long *cvtrtc;
+{
+#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); }
+ static int days_of_month[] =
+ {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ register int i;
+ time_t t;
+
+ if (clock->utctime)
+ return clock->utctime; /* if the conversion routine gets it right away - why not */
+
+ if (clock->year < 100)
+ clock->year += 1900;
+
+ if (clock->year < 1970)
+ clock->year += 100; /* XXX this will do it till <2070 */
+
+ if (clock->year < 0)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1;
+ }
+
+ /*
+ * sorry, slow section here - but it's not time critical anyway
+ */
+ t = (clock->year - 1970) * 365;
+ t += (clock->year >> 2) - (1970 >> 2);
+ t -= clock->year / 100 - 1970 / 100;
+ t += clock->year / 400 - 1970 / 400;
+
+ /* month */
+ if (clock->month <= 0 || clock->month > 12)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad month */
+ }
+ /* adjust leap year */
+ if (clock->month < 3 && dysize(clock->year) == 366)
+ t--;
+
+ for (i = 1; i < clock->month; i++)
+ {
+ t += days_of_month[i];
+ }
+ /* day */
+ if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ?
+ clock->day > 29 : clock->day > days_of_month[clock->month]))
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad day */
+ }
+
+ t += clock->day - 1;
+ /* hour */
+ if (clock->hour < 0 || clock->hour >= 24)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad hour */
+ }
+
+ t = TIMES24(t) + clock->hour;
+
+ /* min */
+ if (clock->minute < 0 || clock->minute > 59)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad min */
+ }
+
+ t = TIMES60(t) + clock->minute;
+ /* sec */
+
+ if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad sec */
+ }
+
+ t = TIMES60(t) + clock->second;
+
+ t += clock->utcoffset; /* warp to UTC */
+
+ /* done */
+
+ clock->utctime = t; /* documentray only */
+
+ return t;
+}
+
+/*--------------- format conversion -----------------------------------*/
+
+int
+Stoi(s, zp, cnt)
+ char *s;
+ long *zp;
+ int cnt;
+{
+ char *b = s;
+ int f,z,v;
+ char c;
+
+ f=z=v=0;
+
+ while(*s == ' ')
+ s++;
+
+ if (*s == '-')
+ {
+ s++;
+ v = 1;
+ }
+ else
+ if (*s == '+')
+ s++;
+
+ for(;;)
+ {
+ c = *s++;
+ if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt)))
+ {
+ if (f == 0)
+ {
+ return(-1);
+ }
+ if (v)
+ z = -z;
+ *zp = z;
+ return(0);
+ }
+ z = (z << 3) + (z << 1) + ( c - '0' );
+ f=1;
+ }
+}
+
+
+int
+Strok(s, m)
+ char *s;
+ char *m;
+{
+ if (!s || !m)
+ return 0;
+
+ while(*s && *m)
+ {
+ if ((*m == ' ') ? 1 : (*s == *m))
+ {
+ s++;
+ m++;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ return !*m;
+}
+
+u_long
+updatetimeinfo(parseio, t, usec, flags)
+ register parse_t *parseio;
+ register time_t t;
+ register u_long usec;
+ register u_long flags;
+{
+ register long usecoff;
+ register long mean;
+ long delta[PARSE_DELTA];
+
+#ifdef PARSEKERNEL
+ usecoff = (t - parseio->parse_dtime.parse_stime.tv.tv_sec) * 1000000
+ - parseio->parse_dtime.parse_stime.tv.tv_usec + usec;
+#else
+ extern long tstouslo[];
+ extern long tstousmid[];
+ extern long tstoushi[];
+
+ TSFTOTVU(parseio->parse_dtime.parse_stime.fp.l_uf, usecoff);
+ usecoff = -usecoff;
+ usecoff += (t - parseio->parse_dtime.parse_stime.fp.l_ui + JAN_1970) * 1000000
+ + usec;
+#endif
+
+ /*
+ * filtering (median) if requested
+ */
+ if (parseio->parse_flags & PARSE_STAT_FILTER)
+ {
+ register int n, i, s, k;
+
+ parseio->parse_delta[parseio->parse_dindex] = usecoff;
+
+ parseio->parse_dindex = (parseio->parse_dindex + 1) % PARSE_DELTA;
+
+ /*
+ * sort always - thus every sample gets its data
+ */
+ bcopy((caddr_t)parseio->parse_delta, (caddr_t)delta, sizeof(delta));
+
+ for (s = 0; s < PARSE_DELTA; s++)
+ for (k = s+1; k < PARSE_DELTA; k++)
+ { /* Yes - it's slow sort */
+ if (delta[s] > delta[k])
+ {
+ register long tmp;
+
+ tmp = delta[k];
+ delta[k] = delta[s];
+ delta[s] = tmp;
+ }
+ }
+
+ i = 0;
+ n = PARSE_DELTA;
+
+ /*
+ * you know this median loop if you have read the other code
+ */
+ while ((n - i) > 8)
+ {
+ register long top = delta[n-1];
+ register long mid = delta[(n+i)>>1];
+ register long low = delta[i];
+
+ if ((top - mid) > (mid - low))
+ {
+ /*
+ * cut off high end
+ */
+ n--;
+ }
+ else
+ {
+ /*
+ * cut off low end
+ */
+ i++;
+ }
+ }
+
+ parseio->parse_dtime.parse_usecdisp = delta[n-1] - delta[i];
+
+ if (parseio->parse_flags & PARSE_STAT_AVG)
+ {
+ /*
+ * take the average of the median samples as this clock
+ * is a little bumpy
+ */
+ mean = 0;
+
+ while (i < n)
+ {
+ mean += delta[i++];
+ }
+
+ mean >>= 3;
+ }
+ else
+ {
+ mean = delta[(n+i)>>1];
+ }
+
+ parseio->parse_dtime.parse_usecerror = mean;
+ }
+ else
+ {
+ parseio->parse_dtime.parse_usecerror = usecoff;
+ parseio->parse_dtime.parse_usecdisp = 0;
+ }
+
+ parseprintf(DD_PARSE,("parse: updatetimeinfo: T=%x+%d usec, useccoff=%d, usecerror=%d, usecdisp=%d\n",
+ (int)t, (int)usec, (int)usecoff, (int)parseio->parse_dtime.parse_usecerror,
+ (int)parseio->parse_dtime.parse_usecdisp));
+
+
+#ifdef PARSEKERNEL
+ {
+ int s = splhigh();
+#endif
+
+ parseio->parse_lstate = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE;
+
+ parseio->parse_dtime.parse_state = parseio->parse_lstate;
+
+#ifdef PARSEKERNEL
+ (void)splx(s);
+ }
+#endif
+
+ return CVT_OK; /* everything fine and dandy... */
+}
+
+
+/*
+ * syn_simple
+ *
+ * handle a sync time stamp
+ */
+/*ARGSUSED*/
+void
+syn_simple(parseio, ts, format, why)
+ register parse_t *parseio;
+ register timestamp_t *ts;
+ register struct format *format;
+ register u_long why;
+{
+ parseio->parse_dtime.parse_stime = *ts;
+}
+
+/*
+ * pps_simple
+ *
+ * handle a pps time stamp
+ */
+/*ARGSUSED*/
+u_long
+pps_simple(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ parseio->parse_dtime.parse_ptime = *ptime;
+ parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+
+ return CVT_NONE;
+}
+
+/*
+ * timepacket
+ *
+ * process a data packet
+ */
+static u_long
+timepacket(parseio)
+ register parse_t *parseio;
+{
+ register int k;
+ register unsigned short format;
+ register time_t t;
+ register u_long cvtsum = 0;/* accumulated CVT_FAIL errors */
+ u_long cvtrtc; /* current conversion result */
+ clocktime_t clock;
+
+ bzero(&clock, sizeof clock);
+ format = parseio->parse_lformat;
+
+ k = 0;
+
+ if (parseio->parse_flags & PARSE_FIXED_FMT)
+ {
+ switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) : CVT_NONE) & CVT_MASK)
+ {
+ case CVT_FAIL:
+ parseio->parse_badformat++;
+ cvtsum = cvtrtc & ~CVT_MASK;
+
+ /*
+ * may be too often ... but is nice to know when it happens
+ */
+#ifdef PARSEKERNEL
+ printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#endif
+ break;
+
+ case CVT_NONE:
+ /*
+ * too bad - pretend bad format
+ */
+ parseio->parse_badformat++;
+ cvtsum = CVT_BADFMT;
+ break;
+
+ case CVT_OK:
+ k = 1;
+ break;
+
+ case CVT_SKIP:
+ k = 2;
+ break;
+
+ default:
+ /* shouldn't happen */
+#ifdef PARSEKERNEL
+ printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#endif
+ return CVT_FAIL|cvtrtc;
+ }
+ }
+ else
+ {
+ /*
+ * find correct conversion routine
+ * and convert time packet
+ * RR search starting at last successful conversion routine
+ */
+
+ if (nformats) /* very careful ... */
+ {
+ do
+ {
+ switch ((cvtrtc = (clockformats[format]->convert && !(clockformats[format]->flags & CVT_FIXEDONLY)) ?
+ clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) :
+ CVT_NONE) & CVT_MASK)
+ {
+ case CVT_FAIL:
+ parseio->parse_badformat++;
+ cvtsum |= cvtrtc & ~CVT_MASK;
+
+ /*
+ * may be too often ... but is nice to know when it happens
+ */
+#ifdef PARSEKERNEL
+ printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#endif
+ /*FALLTHROUGH*/
+ case CVT_NONE:
+ format++;
+ break;
+
+ case CVT_OK:
+ k = 1;
+ break;
+
+ default:
+ /* shouldn't happen */
+#ifdef PARSEKERNEL
+ printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#endif
+ return CVT_BADFMT;
+ }
+ if (format >= nformats)
+ format = 0;
+ }
+ while (!k && (format != parseio->parse_lformat));
+ }
+ }
+
+ if (!k)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: time format \"%s\" not convertable\n", parseio->parse_data);
+#else
+ syslog(LOG_WARNING, "parse: time format \"%s\" not convertable\n", parseio->parse_data);
+#endif
+ return CVT_FAIL|cvtsum;
+ }
+
+ if (k == 2) return CVT_OK;
+
+ if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: bad time format \"%s\"\n", parseio->parse_data);
+#else
+ syslog(LOG_WARNING,"parse: bad time format \"%s\"\n", parseio->parse_data);
+#endif
+ return CVT_FAIL|cvtrtc;
+ }
+
+ parseio->parse_lformat = format;
+
+ /*
+ * time stamp
+ */
+#ifdef PARSEKERNEL
+ parseio->parse_dtime.parse_time.tv.tv_sec = t;
+ parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond;
+#else
+ parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
+ TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
+#endif
+
+ parseio->parse_dtime.parse_format = format;
+
+ return updatetimeinfo(parseio, t, clock.usecond, clock.flags);
+}
+
+
+/*
+ * control operations
+ */
+/*ARGSUSED*/
+int
+parse_getstat(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ dct->parsestatus.flags = parse->parse_flags & PARSE_STAT_FLAGS;
+ return 1;
+}
+
+
+/*ARGSUSED*/
+int
+parse_setstat(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ parse->parse_flags = (parse->parse_flags & ~PARSE_STAT_FLAGS) | dct->parsestatus.flags;
+ return 1;
+}
+
+
+/*ARGSUSED*/
+int
+parse_timecode(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ dct->parsegettc.parse_state = parse->parse_lstate;
+ dct->parsegettc.parse_format = parse->parse_lformat;
+ /*
+ * move out current bad packet count
+ * user program is expected to sum these up
+ * this is not a problem, as "parse" module are
+ * exclusive open only
+ */
+ dct->parsegettc.parse_badformat = parse->parse_badformat;
+ parse->parse_badformat = 0;
+
+ if (parse->parse_ldsize <= PARSE_TCMAX)
+ {
+ dct->parsegettc.parse_count = parse->parse_ldsize;
+ bcopy(parse->parse_ldata, dct->parsegettc.parse_buffer, dct->parsegettc.parse_count);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/*ARGSUSED*/
+int
+parse_setfmt(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ if (dct->parseformat.parse_count <= PARSE_TCMAX)
+ {
+ if (dct->parseformat.parse_count)
+ {
+ register unsigned short i;
+
+ for (i = 0; i < nformats; i++)
+ {
+ if (!Strcmp(dct->parseformat.parse_buffer, clockformats[i]->name))
+ {
+ parse->parse_lformat = i;
+ parse->parse_flags |= PARSE_FIXED_FMT; /* set fixed format indication */
+ return setup_bitmaps(parse, i, i+1);
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ parse->parse_flags &= ~PARSE_FIXED_FMT; /* clear fixed format indication */
+ return setup_bitmaps(parse, 0, nformats);
+ }
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+int
+parse_getfmt(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ if (dct->parseformat.parse_format < nformats &&
+ Strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX)
+ {
+ dct->parseformat.parse_count = Strlen(clockformats[dct->parseformat.parse_format]->name)+1;
+ bcopy(clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_buffer, dct->parseformat.parse_count);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+int
+parse_setcs(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ parse->parse_ioflags &= ~PARSE_IO_CSIZE;
+ parse->parse_ioflags |= dct->parsesetcs.parse_cs & PARSE_IO_CSIZE;
+ return 1;
+}
+
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * parse.c,v
+ * Revision 3.27 1994/06/01 08:18:33 kardel
+ * more debug info
+ *
+ * Revision 3.26 1994/05/30 10:20:07 kardel
+ * LONG cleanup
+ *
+ * Revision 3.25 1994/05/12 12:49:12 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.24 1994/03/27 15:01:36 kardel
+ * reorder include file to cope with PTX
+ *
+ * Revision 3.23 1994/03/25 13:09:02 kardel
+ * considering FIXEDONLY entries only in FIXEDONLY mode
+ *
+ * Revision 3.22 1994/02/25 12:34:49 kardel
+ * allow for converter generated utc times
+ *
+ * Revision 3.21 1994/02/02 17:45:30 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.19 1994/01/25 19:05:20 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.18 1994/01/23 17:21:59 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.17 1993/11/11 11:20:29 kardel
+ * declaration fixes
+ *
+ * Revision 3.16 1993/11/06 22:26:07 duwe
+ * Linux cleanup after config change
+ *
+ * Revision 3.15 1993/11/04 11:14:18 kardel
+ * ansi/K&R traps
+ *
+ * Revision 3.14 1993/11/04 10:03:28 kardel
+ * disarmed ansiism
+ *
+ * Revision 3.13 1993/11/01 20:14:13 kardel
+ * useless comparision removed
+ *
+ * Revision 3.12 1993/11/01 20:00:22 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.11 1993/10/30 09:41:25 kardel
+ * minor optimizations
+ *
+ * Revision 3.10 1993/10/22 14:27:51 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.9 1993/10/05 23:15:09 kardel
+ * more STREAM protection
+ *
+ * Revision 3.8 1993/09/27 21:08:00 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.7 1993/09/26 23:40:16 kardel
+ * new parse driver logic
+ *
+ * Revision 3.6 1993/09/07 10:12:46 kardel
+ * September 7th reconcilation - 3.2 (alpha)
+ *
+ * Revision 3.5 1993/09/01 21:44:48 kardel
+ * conditional cleanup
+ *
+ * Revision 3.4 1993/08/27 00:29:39 kardel
+ * compilation cleanup
+ *
+ * Revision 3.3 1993/08/24 22:27:13 kardel
+ * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad
+ *
+ * Revision 3.2 1993/07/09 11:37:11 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:08 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parse_conf.c b/usr.sbin/xntpd/parse/parse_conf.c
new file mode 100644
index 0000000..cc931e3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parse_conf.c
@@ -0,0 +1,133 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp
+ *
+ * parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp
+ *
+ * Parser configuration module for reference clocks
+ *
+ * STREAM define switches between two personalities of the module
+ * if STREAM is defined this module can be used with dcf77sync.c as
+ * a STREAMS kernel module. In this case the time stamps will be
+ * a struct timeval.
+ * when STREAM is not defined NTP time stamps will be used.
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+#ifdef CLOCK_SCHMID
+extern clockformat_t clock_schmid;
+#endif
+
+#ifdef CLOCK_DCF7000
+extern clockformat_t clock_dcf7000;
+#endif
+
+#ifdef CLOCK_MEINBERG
+extern clockformat_t clock_meinberg[];
+#endif
+
+#ifdef CLOCK_RAWDCF
+extern clockformat_t clock_rawdcf;
+#endif
+
+#ifdef CLOCK_TRIMTAIP
+extern clockformat_t clock_trimtaip;
+#endif
+
+#ifdef CLOCK_TRIMTSIP
+extern clockformat_t clock_trimtsip;
+#endif
+
+/*
+ * format definitions
+ */
+clockformat_t *clockformats[] =
+{
+#ifdef CLOCK_MEINBERG
+ &clock_meinberg[0],
+ &clock_meinberg[1],
+ &clock_meinberg[2],
+#endif
+#ifdef CLOCK_DCF7000
+ &clock_dcf7000,
+#endif
+#ifdef CLOCK_SCHMID
+ &clock_schmid,
+#endif
+#ifdef CLOCK_RAWDCF
+ &clock_rawdcf,
+#endif
+#ifdef CLOCK_TRIMTAIP
+ &clock_trimtaip,
+#endif
+#ifdef CLOCK_TRIMTSIP
+ &clock_trimtsip,
+#endif
+0};
+
+unsigned short nformats = sizeof(clockformats) / sizeof(clockformats[0]) - 1;
+#endif /* REFCLOCK PARSE */
+
+/*
+ * History:
+ *
+ * parse_conf.c,v
+ * Revision 3.15 1994/02/02 17:45:32 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.13 1994/01/25 19:05:23 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.12 1994/01/23 17:22:02 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.11 1993/11/01 20:00:24 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.10 1993/10/09 15:01:37 kardel
+ * file structure unified
+ *
+ * Revision 3.9 1993/09/26 23:40:19 kardel
+ * new parse driver logic
+ *
+ * Revision 3.8 1993/09/02 23:20:57 kardel
+ * dragon extiction
+ *
+ * Revision 3.7 1993/09/01 21:44:52 kardel
+ * conditional cleanup
+ *
+ * Revision 3.6 1993/09/01 11:25:09 kardel
+ * patch accident 8-(
+ *
+ * Revision 3.5 1993/08/31 22:31:14 kardel
+ * SINIX-M SysVR4 integration
+ *
+ * Revision 3.4 1993/08/27 00:29:42 kardel
+ * compilation cleanup
+ *
+ * Revision 3.3 1993/07/14 09:04:45 kardel
+ * only when REFCLOCK && PARSE is defined
+ *
+ * Revision 3.2 1993/07/09 11:37:13 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:11 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parsesolaris.c b/usr.sbin/xntpd/parse/parsesolaris.c
new file mode 100644
index 0000000..a67f0bf
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parsesolaris.c
@@ -0,0 +1,1249 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp
+ *
+ * parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp
+ *
+ * STREAMS module for reference clocks
+ * (SunOS5.x - not fully tested - buyer beware ! - OS KILLERS may still be
+ * lurking in the code!)
+ *
+ * Copyright (c) 1993,1994
+ * derived work from parsestreams.c ((c) 1991-1993, Frank Kardel) and
+ * dcf77sync.c((c) Frank Kardel)
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp";
+#endif
+
+/*
+ * Well, the man spec says we have to do this junk - the
+ * header files tell a different story (i like that one more)
+ */
+#define SAFE_WR(q) (((q)->q_flag & QREADR) ? WR((q)) : (q))
+#define SAFE_RD(q) (((q)->q_flag & QREADR) ? (q) : RD((q)))
+
+/*
+ * needed to cope with Solaris 2.3 header file chaos
+ */
+#include <sys/types.h>
+/*
+ * the Solaris 2.2 include list
+ */
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/termios.h>
+#include <sys/stream.h>
+#include <sys/strtty.h>
+#include <sys/stropts.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cpu.h>
+
+#define STREAM /* that's what we are here for */
+
+#define HAVE_NO_NICE /* for the NTP headerfiles */
+#include "ntp_fp.h"
+#include "parse.h"
+#include "sys/parsestreams.h"
+
+static unsigned int parsebusy = 0;
+
+/*--------------- loadable driver section -----------------------------*/
+
+static struct streamtab parseinfo;
+
+static struct fmodsw fmod_templ =
+{
+ "parse", /* module name */
+ &parseinfo, /* module information */
+ D_NEW|D_MP|D_MTQPAIR, /* exclusive for q pair */
+ /* lock ptr */
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod =
+{
+ &mod_strmodops, /* a STREAMS module */
+ "PARSE - NTP reference", /* name this baby - keep room for revision number */
+ &fmod_templ
+};
+
+static struct modlinkage modlinkage =
+{
+ MODREV_1,
+ &modlstrmod,
+ NULL
+};
+
+/*
+ * strings support usually not in kernel
+ */
+static int Strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static void Strncpy(t, s, c)
+ register char *t;
+ register char *s;
+ register int c;
+{
+ if (s && t)
+ {
+ while ((c-- > 0) && (*t++ = *s++))
+ ;
+ }
+}
+
+int Strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+/*
+ * module management routines
+ */
+/*ARGSUSED*/
+int _init(void)
+{
+ static char revision[] = "3.16";
+ char *s, *S, *t;
+
+ /*
+ * copy RCS revision into Drv_name
+ *
+ * are we forcing RCS here to do things it was not built for ?
+ */
+ s = revision;
+ if (*s == '$')
+ {
+ /*
+ * skip "$Revision: "
+ * if present. - not necessary on a -kv co (cvs export)
+ */
+ while (*s && (*s != ' '))
+ {
+ s++;
+ }
+ if (*s == ' ') s++;
+ }
+
+ t = modlstrmod.strmod_linkinfo;
+ while (*t && (*t != ' '))
+ {
+ t++;
+ }
+ if (*t == ' ') t++;
+
+ S = s;
+ while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
+ {
+ S++;
+ }
+
+ if (*s && *t && (S > s))
+ {
+ if (Strlen(t) >= (S - s))
+ {
+ (void) Strncpy(t, s, S - s);
+ }
+ }
+ return (mod_install(&modlinkage));
+}
+
+/*ARGSUSED*/
+int _info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*ARGSUSED*/
+int _fini(void)
+{
+ if (parsebusy > 0)
+ {
+ printf("_fini[%s]: STREAMS module has still %d instances active.\n", modlstrmod.strmod_linkinfo, parsebusy);
+ return (EBUSY);
+ }
+ else
+ return (mod_remove(&modlinkage));
+}
+
+/*--------------- stream module definition ----------------------------*/
+
+static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc();
+
+static struct module_info driverinfo =
+{
+ 0, /* module ID number */
+ fmod_templ.f_name, /* module name - why repeated here ? compat ?*/
+ 0, /* minimum accepted packet size */
+ INFPSZ, /* maximum accepted packet size */
+ 1, /* high water mark - flow control */
+ 0 /* low water mark - flow control */
+};
+
+static struct qinit rinit = /* read queue definition */
+{
+ parserput, /* put procedure */
+ parsersvc, /* service procedure */
+ parseopen, /* open procedure */
+ parseclose, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct qinit winit = /* write queue definition */
+{
+ parsewput, /* put procedure */
+ NULL, /* service procedure */
+ NULL, /* open procedure */
+ NULL, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct streamtab parseinfo = /* stream info element for parse driver */
+{
+ &rinit, /* read queue */
+ &winit, /* write queue */
+ NULL, /* read mux */
+ NULL /* write mux */
+};
+
+/*--------------- driver data structures ----------------------------*/
+
+/*
+ * we usually have an inverted signal - but you
+ * can change this to suit your needs
+ */
+int cd_invert = 1; /* invert status of CD line - PPS support via CD input */
+
+int parsedebug = ~0;
+
+extern void uniqtime();
+
+/*--------------- module implementation -----------------------------*/
+
+#define TIMEVAL_USADD(_X_, _US_) do {\
+ (_X_)->tv_usec += (_US_);\
+ if ((_X_)->tv_usec >= 1000000)\
+ {\
+ (_X_)->tv_sec++;\
+ (_X_)->tv_usec -= 1000000;\
+ }\
+ } while (0)
+
+#if defined(sun4c) && defined(DEBUG_CD)
+#include <sun4c/cpu.h>
+#include <sun4c/auxio.h>
+#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0)
+#else
+#define SET_LED(_X_)
+#endif
+
+static int init_linemon();
+static void close_linemon();
+
+/*
+ * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT
+ * timing constants
+ *
+ * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING
+ * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime()
+ * YOURSELF.
+ *
+ * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE
+ * OF HW MANIPULATION !
+ *
+ * you need an oscilloscope and the permission for HW work
+ * in order to figure out these timing constants/variables
+ */
+
+static unsigned long xsdelay = 10; /* assume an SS2 */
+static unsigned long stdelay = 350;
+
+struct delays
+{
+ unsigned char mask; /* what to check for */
+ unsigned char type; /* what to match */
+ unsigned long xsdelay; /* external status direct delay in us */
+ unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */
+} isr_delays[] =
+{
+ /*
+ * WARNING: must still be measured - currently taken from Craig Leres ppsdev
+ */
+#ifdef sun4c
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350},
+#endif
+#ifdef sun4m
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250},
+#endif
+ {0,}
+};
+
+void setup_delays()
+{
+ register int i;
+
+ if (cputype & OBP_ARCH)
+ {
+ printf("parse: WARNING: PPS kernel fudge factors no yet determinable (no dev tree walk yet) - assuming SS2 (Sun4/75)\n", cputype);
+ return;
+ }
+
+ for (i = 0; isr_delays[i].mask; i++)
+ {
+ if ((cputype & isr_delays[i].mask) == isr_delays[i].type)
+ {
+ xsdelay = isr_delays[i].xsdelay;
+ stdelay = isr_delays[i].stdelay;
+ return;
+ }
+ }
+ printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cputype);
+}
+
+#define M_PARSE 0x0001
+#define M_NOPARSE 0x0002
+
+static int
+setup_stream(queue_t *q, int mode)
+{
+ register mblk_t *mp;
+
+ parseprintf(DD_OPEN,("parse: SETUP_STREAM - setting up stream for q=%x\n", q));
+
+ mp = allocb(sizeof(struct stroptions), BPRI_MED);
+ if (mp)
+ {
+ struct stroptions *str = (struct stroptions *)mp->b_wptr;
+
+ str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT;
+ str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
+ str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
+ str->so_lowat = 0;
+ mp->b_datap->db_type = M_SETOPTS;
+ mp->b_wptr += sizeof(struct stroptions);
+ if (!q)
+ panic("NULL q - strange");
+ putnext(q, mp);
+ return putctl1(SAFE_WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
+ MC_SERVICEDEF);
+ }
+ else
+ {
+ parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n"));
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
+{
+ register mblk_t *mp;
+ register parsestream_t *parse;
+ static int notice = 0;
+
+ parseprintf(DD_OPEN,("parse: OPEN - q=%x\n", q));
+
+ if (sflag != MODOPEN)
+ { /* open only for modules */
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n"));
+ return EIO;
+ }
+
+ if (q->q_ptr != (caddr_t)NULL)
+ {
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n"));
+ return EBUSY;
+ }
+
+ parsebusy++;
+
+ q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t), KM_SLEEP);
+ if (q->q_ptr == (caddr_t)0)
+ {
+ return ENOMEM;
+ }
+
+ parseprintf(DD_OPEN,("parse: OPEN - parse area q=%x, q->q_ptr=%x\n", q, q->q_ptr));
+ SAFE_WR(q)->q_ptr = q->q_ptr;
+ parseprintf(DD_OPEN,("parse: OPEN - WQ parse area q=%x, q->q_ptr=%x\n", SAFE_WR(q), SAFE_WR(q)->q_ptr));
+
+ parse = (parsestream_t *) q->q_ptr;
+ bzero((caddr_t)parse, sizeof(*parse));
+ parse->parse_queue = q;
+ parse->parse_status = PARSE_ENABLE;
+ parse->parse_ppsclockev.tv.tv_sec = 0;
+ parse->parse_ppsclockev.tv.tv_usec = 0;
+ parse->parse_ppsclockev.serial = 0;
+
+ qprocson(q);
+
+ parseprintf(DD_OPEN,("parse: OPEN - initializing io subsystem q=%x\n", q));
+
+ if (!parse_ioinit(&parse->parse_io))
+ {
+ /*
+ * ok guys - beat it
+ */
+ qprocsoff(q);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ parsebusy--;
+
+ return EIO;
+ }
+
+ parseprintf(DD_OPEN,("parse: OPEN - initializing stream q=%x\n", q));
+
+ if (setup_stream(q, M_PARSE))
+ {
+ (void) init_linemon(q); /* hook up PPS ISR routines if possible */
+ setup_delays();
+ parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n"));
+
+ /*
+ * I know that you know the delete key, but you didn't write this
+ * code, did you ? - So, keep the message in here.
+ */
+ if (!notice)
+ {
+ printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", modlstrmod.strmod_linkinfo);
+ notice = 1;
+ }
+
+ return 0;
+ }
+ else
+ {
+ qprocsoff(q);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ parsebusy--;
+
+ return EIO;
+ }
+}
+
+/*ARGSUSED*/
+static int parseclose(queue_t *q, int flags)
+{
+ register parsestream_t *parse = (parsestream_t *)q->q_ptr;
+ register unsigned long s;
+
+ parseprintf(DD_CLOSE,("parse: CLOSE\n"));
+
+ qprocsoff(q);
+
+ s = splhigh();
+
+ if (parse->parse_dqueue)
+ close_linemon(parse->parse_dqueue, q);
+ parse->parse_dqueue = (queue_t *)0;
+
+ (void) splx(s);
+
+ parse_ioend(&parse->parse_io);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ q->q_ptr = (caddr_t)NULL;
+ SAFE_WR(q)->q_ptr = (caddr_t)NULL;
+
+ parsebusy--;
+}
+
+/*
+ * move unrecognized stuff upward
+ */
+static parsersvc(queue_t *q)
+{
+ mblk_t *mp;
+
+ while (mp = getq(q))
+ {
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - putnext\n"));
+ }
+ else
+ {
+ putbq(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n"));
+ break;
+ }
+ }
+}
+
+/*
+ * do ioctls and
+ * send stuff down - dont care about
+ * flow control
+ */
+static int parsewput(queue_t *q, mblk_t *mp)
+{
+ register int ok = 1;
+ register mblk_t *datap;
+ register struct iocblk *iocp;
+ parsestream_t *parse = (parsestream_t *)q->q_ptr;
+
+ parseprintf(DD_WPUT,("parse: parsewput\n"));
+
+ switch (mp->b_datap->db_type)
+ {
+ default:
+ putnext(q, mp);
+ break;
+
+ case M_IOCTL:
+ iocp = (struct iocblk *)mp->b_rptr;
+ switch (iocp->ioc_cmd)
+ {
+ default:
+ parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n"));
+ putnext(q, mp);
+ break;
+
+ case CIOGETEV:
+ /*
+ * taken from Craig Leres ppsclock module (and modified)
+ */
+ datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
+ if (datap == NULL || mp->b_cont)
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
+ if (datap != NULL)
+ freeb(datap);
+ qreply(q, mp);
+ break;
+ }
+
+ mp->b_cont = datap;
+ *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev;
+ datap->b_wptr +=
+ sizeof(struct ppsclockev) / sizeof(*datap->b_wptr);
+ mp->b_datap->db_type = M_IOCACK;
+ iocp->ioc_count = sizeof(struct ppsclockev);
+ qreply(q, mp);
+ break;
+
+ case PARSEIOC_ENABLE:
+ case PARSEIOC_DISABLE:
+ {
+ parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) |
+ (iocp->ioc_cmd == PARSEIOC_ENABLE) ?
+ PARSE_ENABLE : 0;
+ if (!setup_stream(SAFE_RD(q), (parse->parse_status & PARSE_ENABLE) ?
+ M_PARSE : M_NOPARSE))
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCACK;
+ }
+ qreply(q, mp);
+ break;
+ }
+
+ case PARSEIOC_SETSTAT:
+ case PARSEIOC_GETSTAT:
+ case PARSEIOC_TIMECODE:
+ case PARSEIOC_SETFMT:
+ case PARSEIOC_GETFMT:
+ case PARSEIOC_SETCS:
+ if (iocp->ioc_count == sizeof(parsectl_t))
+ {
+ parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr;
+
+ switch (iocp->ioc_cmd)
+ {
+ case PARSEIOC_GETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n"));
+ ok = parse_getstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n"));
+ ok = parse_setstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_TIMECODE:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n"));
+ ok = parse_timecode(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n"));
+ ok = parse_setfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_GETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n"));
+ ok = parse_getfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETCS:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n"));
+ ok = parse_setcs(dct, &parse->parse_io);
+ break;
+ }
+ mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"));
+ qreply(q, mp);
+ break;
+ }
+ }
+}
+
+/*
+ * read characters from streams buffers
+ */
+static unsigned long rdchar(mblk_t **mp)
+{
+ while (*mp != (mblk_t *)NULL)
+ {
+ if ((*mp)->b_wptr - (*mp)->b_rptr)
+ {
+ return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++));
+ }
+ else
+ {
+ register mblk_t *mmp = *mp;
+
+ *mp = (*mp)->b_cont;
+ freeb(mmp);
+ }
+ }
+ return ~0;
+}
+
+/*
+ * convert incoming data
+ */
+static int parserput(queue_t *q, mblk_t *imp)
+{
+ register unsigned char type;
+ mblk_t *mp = imp;
+
+ switch (type = mp->b_datap->db_type)
+ {
+ default:
+ /*
+ * anything we don't know will be put on queue
+ * the service routine will move it to the next one
+ */
+ parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type));
+
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ break;
+
+ case M_BREAK:
+ case M_DATA:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ register mblk_t *nmp;
+ register unsigned long ch;
+ timestamp_t ctime;
+
+ /*
+ * get time on packet delivery
+ */
+ uniqtime(&ctime.tv);
+
+ if (!(parse->parse_status & PARSE_ENABLE))
+ {
+ parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ }
+ else
+ {
+#if 0
+ parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK"));
+#endif
+ if (type == M_DATA)
+ {
+ /*
+ * parse packet looking for start an end characters
+ */
+ while (mp != (mblk_t *)NULL)
+ {
+ ch = rdchar(&mp);
+ if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ }
+ }
+ else
+ {
+ if (parse_ioread(&parse->parse_io, (char)0, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ freemsg(mp);
+ }
+ break;
+ }
+ }
+
+ /*
+ * CD PPS support for non direct ISR hack
+ */
+ case M_HANGUP:
+ case M_UNHANGUP:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ timestamp_t ctime;
+ register mblk_t *nmp;
+ register int status = cd_invert ^ (type == M_HANGUP);
+
+ SET_LED(status);
+
+ uniqtime(&ctime.tv);
+
+ TIMEVAL_USADD(&ctime.tv, stdelay);
+
+ parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
+
+ if ((parse->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime))
+ {
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ freemsg(mp);
+ }
+ else
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+
+ if (status)
+ {
+ parse->parse_ppsclockev.tv = ctime.tv;
+ ++(parse->parse_ppsclockev.serial);
+ }
+ }
+ }
+}
+
+static int init_zs_linemon(); /* handle line monitor for "zs" driver */
+static void close_zs_linemon();
+static void zs_xsisr(); /* zs external status interupt handler */
+
+/*-------------------- CD isr status monitor ---------------*/
+
+static int init_linemon(queue_t *q)
+{
+ register queue_t *dq;
+
+ dq = SAFE_WR(q);
+ /*
+ * we ARE doing very bad things down here (basically stealing ISR
+ * hooks)
+ *
+ * so we chase down the STREAMS stack searching for the driver
+ * and if this is a known driver we insert our ISR routine for
+ * status changes in to the ExternalStatus handling hook
+ */
+ while (dq->q_next)
+ {
+ dq = dq->q_next; /* skip down to driver */
+ }
+
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (dq->q_qinfo && dq->q_qinfo->qi_minfo)
+ {
+ register char *dname = dq->q_qinfo->qi_minfo->mi_idname;
+
+ parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname));
+
+#ifdef sun
+ if (dname && !Strcmp(dname, "zs"))
+ {
+ return init_zs_linemon(dq, q);
+ }
+ else
+#endif
+ {
+ parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname));
+ return 0;
+ }
+ }
+ parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n"));
+ return 0;
+}
+
+static void close_linemon(queue_t *q, queue_t *my_q)
+{
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ register char *dname = q->q_qinfo->qi_minfo->mi_idname;
+
+#ifdef sun
+ if (dname && !Strcmp(dname, "zs"))
+ {
+ close_zs_linemon(q, my_q);
+ return;
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname));
+#endif
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n"));
+}
+
+#ifdef sun
+#include <sys/tty.h>
+#include <sys/zsdev.h>
+#include <sys/ser_async.h>
+#include <sys/ser_zscc.h>
+
+/*
+ * there should be some docs telling how to get to
+ * sz:zs_usec_delay and zs:initzsops()
+ */
+#define zs_usec_delay 5
+
+struct savedzsops
+{
+ struct zsops zsops;
+ struct zsops *oldzsops;
+};
+
+static struct zsops *emergencyzs;
+
+static int init_zs_linemon(queue_t *q, queue_t *my_q)
+{
+ register struct zscom *zs;
+ register struct savedzsops *szs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+ /*
+ * we expect the zsaline pointer in the q_data pointer
+ * from there on we insert our on EXTERNAL/STATUS ISR routine
+ * into the interrupt path, before the standard handler
+ */
+ zs = ((struct asyncline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return 0;
+ }
+ else
+ {
+ unsigned long s;
+
+ /*
+ * we do a direct replacement, in case others fiddle also
+ * if somebody else grabs our hook and we disconnect
+ * we are in DEEP trouble - panic is likely to be next, sorry
+ */
+ szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops), KM_SLEEP);
+
+ if (szs == (struct savedzsops *)0)
+ {
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n"));
+
+ return 0;
+ }
+ else
+ {
+ parsestream->parse_data = (void *)szs;
+
+ mutex_enter(zs->zs_excl);
+
+ parsestream->parse_dqueue = q; /* remember driver */
+
+ szs->zsops = *zs->zs_ops;
+ szs->zsops.zsop_xsint = (void (*)())zs_xsisr; /* place our bastard */
+ szs->oldzsops = zs->zs_ops;
+ emergencyzs = zs->zs_ops;
+
+ zs->zs_ops = &szs->zsops; /* hook it up */
+ /*
+ * XXX: this is usually done via zsopinit()
+ * - have yet to find a way to call that routine
+ */
+ zs->zs_xsint = (void (*)())zs_xsisr;
+
+ mutex_exit(zs->zs_excl);
+
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n"));
+
+ return 1;
+ }
+ }
+}
+
+/*
+ * unregister our ISR routine - must call under splhigh()
+ */
+static void close_zs_linemon(queue_t *q, queue_t *my_q)
+{
+ register struct zscom *zs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+
+ zs = ((struct asyncline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return;
+ }
+ else
+ {
+ register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
+
+ mutex_enter(zs->zs_excl);
+
+ zs->zs_ops = szs->oldzsops; /* reset to previous handler functions */
+ /*
+ * XXX: revert xsint (usually done via zsopinit() - have still to find
+ * a way to call that bugger
+ */
+ zs->zs_xsint = zs->zs_ops->zsop_xsint;
+
+ mutex_exit(zs->zs_excl);
+
+ kmem_free((caddr_t)szs, sizeof (struct savedzsops));
+
+ parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n"));
+ return;
+ }
+}
+
+#define ZSRR0_IGNORE (ZSRR0_CD|ZSRR0_SYNC|ZSRR0_CTS)
+
+#define MAXDEPTH 50 /* maximum allowed stream crawl */
+
+/*
+ * take external status interrupt (only CD interests us)
+ */
+static void zs_xsisr(struct zscom *zs)
+{
+ register struct asyncline *za = (struct asyncline *)zs->zs_priv;
+ register queue_t *q;
+ register unsigned char zsstatus;
+ register int loopcheck;
+ register unsigned char cdstate;
+ register char *dname;
+
+ /*
+ * pick up current state
+ */
+ zsstatus = SCC_READ0();
+
+ if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD))
+ {
+ timestamp_t cdevent;
+ register int status;
+
+ /*
+ * CONDITIONAL external measurement support
+ */
+ SET_LED(cdstate); /*
+ * inconsistent with upper SET_LED, but this
+ * is for oscilloscope business anyway and we
+ * are just interested in edge delays in the
+ * lower us range
+ */
+
+ /*
+ * time stamp
+ */
+ uniqtime(&cdevent.tv);
+
+ TIMEVAL_USADD(&cdevent.tv, xsdelay);
+
+ q = za->za_ttycommon.t_readq;
+
+ /*
+ * logical state
+ */
+ status = cd_invert ? cdstate == 0 : cdstate != 0;
+
+ /*
+ * ok - now the hard part - find ourself
+ */
+ loopcheck = MAXDEPTH;
+
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+
+ if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+
+ if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent))
+ {
+ /*
+ * XXX - currently we do not pass up the message, as
+ * we should.
+ * for a correct behaviour wee need to block out
+ * processing until parse_iodone has been posted via
+ * a softcall-ed routine which does the message pass-up
+ * right now PPS information relies on input being
+ * received
+ */
+ parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io);
+ }
+
+ if (status)
+ {
+ ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
+ ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial);
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname));
+ break;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - CD event");
+ }
+ }
+
+ /*
+ * only pretend that CD and ignored transistion (SYNC,CTS)
+ * have been handled
+ */
+ za->za_rr0 = (za->za_rr0 & ~ZSRR0_IGNORE) | (zsstatus & ZSRR0_IGNORE);
+
+ if (((za->za_rr0 ^ zsstatus) & ~ZSRR0_IGNORE) == 0)
+ {
+ /*
+ * all done - kill status indication and return
+ */
+ SCC_WRITE0(ZSWR0_RESET_STATUS); /* might kill other conditions here */
+ return;
+ }
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event 0x%x for \"%s\"\n",
+ (za->za_rr0 ^ zsstatus) & ~ZSRR0_CD,dname));
+ /*
+ * we are now gathered here to process some unusual external status
+ * interrupts.
+ * any CD events have also been handled and shouldn't be processed
+ * by the original routine (unless we have a VERY busy port pin)
+ * some initializations are done here, which could have been done before for
+ * both code paths but have been avioded for minimum path length to
+ * the uniq_time routine
+ */
+ dname = (char *) 0;
+ q = za->za_ttycommon.t_readq;
+
+ loopcheck = MAXDEPTH;
+
+ /*
+ * the real thing for everything else ...
+ */
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+ if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ register void (*zsisr)();
+
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+ if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint)
+ zsisr(zs);
+ else
+ panic("zs_xsisr: unable to locate original ISR");
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname));
+ /*
+ * now back to our program ...
+ */
+ return;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
+ }
+ }
+
+ /*
+ * last resort - shouldn't even come here as it indicates
+ * corrupted TTY structures
+ */
+ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-");
+
+ if (emergencyzs && emergencyzs->zsop_xsint)
+ emergencyzs->zsop_xsint(zs);
+ else
+ panic("zs_xsisr: no emergency ISR handler");
+}
+#endif /* sun */
+
+/*
+ * History:
+ *
+ * parsesolaris.c,v
+ * Revision 3.16 1994/05/30 09:57:40 kardel
+ * kmem_alloc checking
+ *
+ * Revision 3.15 1994/02/15 22:20:51 kardel
+ * rcsid fixed
+ *
+ * Revision 3.14 1994/02/15 22:06:04 kardel
+ * added qprocsx & flags for MT capability
+ *
+ * Revision 3.13 1994/02/13 19:16:47 kardel
+ * updated verbose Copyright message
+ *
+ * Revision 3.12 1994/02/02 17:45:35 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.9 1994/01/25 19:05:26 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.8 1994/01/23 17:22:04 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.7 1993/12/15 18:24:41 kardel
+ * Now also ignoring state changes on ZSRR0_{SYNC,CTS} to avoid zs driver bugs (Solaris 2.3)
+ *
+ * Revision 3.6 1993/12/15 12:48:53 kardel
+ * fixed message loss on M_*HANHUP messages
+ *
+ * Revision 3.5 1993/12/14 21:05:12 kardel
+ * PPS working now for SunOS 5.x zs external status hook
+ *
+ * Revision 3.4 1993/11/13 11:13:17 kardel
+ * Solaris 2.3 additional includes
+ *
+ * Revision 3.3 1993/11/11 11:20:33 kardel
+ * declaration fixes
+ *
+ * Revision 3.2 1993/11/05 15:40:25 kardel
+ * shut up nice feature detection
+ *
+ * Revision 3.1 1993/11/01 20:00:29 kardel
+ * parse Solaris support (initial version)
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parsestreams.c b/usr.sbin/xntpd/parse/parsestreams.c
new file mode 100644
index 0000000..990f2b3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parsestreams.c
@@ -0,0 +1,1363 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp
+ *
+ * parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp
+ *
+ * STREAMS module for reference clocks
+ * (SunOS4.x)
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp";
+#endif
+
+#include "sys/types.h"
+#include "sys/conf.h"
+#include "sys/buf.h"
+#include "sys/param.h"
+#include "sys/sysmacros.h"
+#include "sys/errno.h"
+#include "sys/time.h"
+#include "sundev/mbvar.h"
+#include "sun/autoconf.h"
+#include "sys/stream.h"
+#include "sys/stropts.h"
+#include "sys/dir.h"
+#include "sys/signal.h"
+#include "sys/termios.h"
+#include "sys/termio.h"
+#include "sys/ttold.h"
+#include "sys/user.h"
+#include "sys/errno.h"
+#include "sys/tty.h"
+#include "machine/cpu.h"
+
+#ifdef VDDRV
+#include "sun/vddrv.h"
+#endif
+
+/*
+ * no protypes here !
+ */
+#define P(x) ()
+
+/*
+ * use microtime instead of uniqtime if advised to
+ */
+#ifdef MICROTIME
+#define uniqtime microtime
+#endif
+
+#define HAVE_NO_NICE /* for the NTP headerfiles */
+#include "ntp_fp.h"
+#include "parse.h"
+#include "sys/parsestreams.h"
+
+#ifdef VDDRV
+static unsigned int parsebusy = 0;
+
+/*--------------- loadable driver section -----------------------------*/
+
+extern struct streamtab parseinfo;
+
+struct vdldrv parsesync_vd =
+{
+ VDMAGIC_PSEUDO, /* nothing like a real driver - a STREAMS module */
+ "PARSE ", /* name this baby - keep room for revision number */
+};
+
+/*
+ * strings support usually not in kernel
+ */
+static int strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static void strncpy(t, s, c)
+ register char *t;
+ register char *s;
+ register int c;
+{
+ if (s && t)
+ {
+ while ((c-- > 0) && (*t++ = *s++))
+ ;
+ }
+}
+
+static int strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+static int strncmp(s, t, n)
+ register char *s;
+ register char *t;
+ register int n;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (n-- && !(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+/*
+ * driver init routine
+ * since no mechanism gets us into and out of the fmodsw, we have to
+ * do it ourselves
+ */
+/*ARGSUSED*/
+int xxxinit(fc, vdp, vdi, vds)
+ unsigned int fc;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ extern struct fmodsw fmodsw[];
+ extern int fmodcnt;
+
+ struct fmodsw *fm = fmodsw;
+ struct fmodsw *fmend = &fmodsw[fmodcnt];
+ struct fmodsw *ifm = (struct fmodsw *)0;
+ char *mname = parseinfo.st_rdinit->qi_minfo->mi_idname;
+
+ switch (fc)
+ {
+ case VDLOAD:
+ vdp->vdd_vdtab = (struct vdlinkage *)&parsesync_vd;
+ /*
+ * now, jog along fmodsw scanning for an empty slot
+ * and deposit our name there
+ */
+ while (fm <= fmend)
+ {
+ if (!strncmp(fm->f_name, mname, FMNAMESZ))
+ {
+ printf("vddrinit[%s]: STREAMS module already loaded.\n", mname);
+ return(EBUSY);
+ }
+ else
+ if ((ifm == (struct fmodsw *)0) &&
+ (fm->f_name[0] == '\0') && (fm->f_str == (struct streamtab *)0))
+ {
+ /*
+ * got one - so move in
+ */
+ ifm = fm;
+ break;
+ }
+ fm++;
+ }
+
+ if (ifm == (struct fmodsw *)0)
+ {
+ printf("vddrinit[%s]: no slot free for STREAMS module\n", mname);
+ return (ENOSPC);
+ }
+ else
+ {
+ static char revision[] = "3.22";
+ char *s, *S, *t;
+
+ strncpy(ifm->f_name, mname, FMNAMESZ);
+ ifm->f_name[FMNAMESZ] = '\0';
+ ifm->f_str = &parseinfo;
+ /*
+ * copy RCS revision into Drv_name
+ *
+ * are we forcing RCS here to do things it was not built for ?
+ */
+ s = revision;
+ if (*s == '$')
+ {
+ /*
+ * skip "$Revision: "
+ * if present. - not necessary on a -kv co (cvs export)
+ */
+ while (*s && (*s != ' '))
+ {
+ s++;
+ }
+ if (*s == ' ') s++;
+ }
+
+ t = parsesync_vd.Drv_name;
+ while (*t && (*t != ' '))
+ {
+ t++;
+ }
+ if (*t == ' ') t++;
+
+ S = s;
+ while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
+ {
+ S++;
+ }
+
+ if (*s && *t && (S > s))
+ {
+ if (strlen(t) >= (S - s))
+ {
+ (void) strncpy(t, s, S - s);
+ }
+ }
+ return (0);
+ }
+ break;
+
+ case VDUNLOAD:
+ if (parsebusy > 0)
+ {
+ printf("vddrinit[%s]: STREAMS module has still %d instances active.\n", mname, parsebusy);
+ return (EBUSY);
+ }
+ else
+ {
+ while (fm <= fmend)
+ {
+ if (!strncmp(fm->f_name, mname, FMNAMESZ))
+ {
+ /*
+ * got it - kill entry
+ */
+ fm->f_name[0] = '\0';
+ fm->f_str = (struct streamtab *)0;
+ fm++;
+
+ break;
+ }
+ fm++;
+ }
+ if (fm > fmend)
+ {
+ printf("vddrinit[%s]: cannot find entry for STREAMS module\n", mname);
+ return (ENXIO);
+ }
+ else
+ return (0);
+ }
+
+
+ case VDSTAT:
+ return (0);
+
+ default:
+ return (EIO);
+
+ }
+ return EIO;
+}
+
+#endif
+
+/*--------------- stream module definition ----------------------------*/
+
+static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc();
+
+static struct module_info driverinfo =
+{
+ 0, /* module ID number */
+ "parse", /* module name */
+ 0, /* minimum accepted packet size */
+ INFPSZ, /* maximum accepted packet size */
+ 1, /* high water mark - flow control */
+ 0 /* low water mark - flow control */
+};
+
+static struct qinit rinit = /* read queue definition */
+{
+ parserput, /* put procedure */
+ parsersvc, /* service procedure */
+ parseopen, /* open procedure */
+ parseclose, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct qinit winit = /* write queue definition */
+{
+ parsewput, /* put procedure */
+ NULL, /* service procedure */
+ NULL, /* open procedure */
+ NULL, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+struct streamtab parseinfo = /* stream info element for dpr driver */
+{
+ &rinit, /* read queue */
+ &winit, /* write queue */
+ NULL, /* read mux */
+ NULL, /* write mux */
+ NULL /* module auto push */
+};
+
+/*--------------- driver data structures ----------------------------*/
+
+/*
+ * we usually have an inverted signal - but you
+ * can change this to suit your needs
+ */
+int cd_invert = 1; /* invert status of CD line - PPS support via CD input */
+
+int parsedebug = ~0;
+
+extern void uniqtime();
+
+/*--------------- module implementation -----------------------------*/
+
+#define TIMEVAL_USADD(_X_, _US_) {\
+ (_X_)->tv_usec += (_US_);\
+ if ((_X_)->tv_usec >= 1000000)\
+ {\
+ (_X_)->tv_sec++;\
+ (_X_)->tv_usec -= 1000000;\
+ }\
+ } while (0)
+
+#if defined(sun4c) && defined(DEBUG_CD)
+#include <sun4c/cpu.h>
+#include <sun4c/auxio.h>
+#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0)
+#else
+#define SET_LED(_X_)
+#endif
+
+static int init_linemon();
+static void close_linemon();
+
+/*
+ * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT
+ * timing constants
+ *
+ * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING
+ * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime()
+ * YOURSELF.
+ *
+ * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE
+ * OF HW MANIPULATION !
+ *
+ * you need an oscilloscope and the permission for HW work
+ * in order to figure out these timing constants/variables
+ */
+#ifdef sun
+static unsigned long xsdelay = 10; /* assume an SS2 */
+static unsigned long stdelay = 350;
+
+struct delays
+{
+ unsigned char mask; /* what to check for */
+ unsigned char type; /* what to match */
+ unsigned long xsdelay; /* external status direct delay in us */
+ unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */
+} isr_delays[] =
+{
+ /*
+ * WARNING: must still be measured - currently taken from Craig Leres ppsdev
+ */
+#ifdef sun4c
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350},
+#endif
+#ifdef sun4m
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250},
+#endif
+ {0,}
+};
+
+void setup_delays()
+{
+ register int i;
+
+ for (i = 0; isr_delays[i].mask; i++)
+ {
+ if ((cpu & isr_delays[i].mask) == isr_delays[i].type)
+ {
+ xsdelay = isr_delays[i].xsdelay;
+ stdelay = isr_delays[i].stdelay;
+ return;
+ }
+ }
+ printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cpu);
+}
+#else
+#define setup_delays() /* empty - no need for clobbering kernel with this */
+static unsigned long xsdelay = 0; /* assume nothing */
+static unsigned long stdelay = 0;
+#endif
+
+#define M_PARSE 0x0001
+#define M_NOPARSE 0x0002
+
+static int
+setup_stream(q, mode)
+ queue_t *q;
+ int mode;
+{
+ mblk_t *mp;
+
+ mp = allocb(sizeof(struct stroptions), BPRI_MED);
+ if (mp)
+ {
+ struct stroptions *str = (struct stroptions *)mp->b_rptr;
+
+ str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT;
+ str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
+ str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
+ str->so_lowat = 0;
+ mp->b_datap->db_type = M_SETOPTS;
+ mp->b_wptr += sizeof(struct stroptions);
+ putnext(q, mp);
+ return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
+ MC_SERVICEDEF);
+ }
+ else
+ {
+ parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n"));
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+static int parseopen(q, dev, flag, sflag)
+ queue_t *q;
+ dev_t dev;
+ int flag;
+ int sflag;
+{
+ register mblk_t *mp;
+ register parsestream_t *parse;
+ static int notice = 0;
+
+ parseprintf(DD_OPEN,("parse: OPEN\n"));
+
+ if (sflag != MODOPEN)
+ { /* open only for modules */
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n"));
+ return OPENFAIL;
+ }
+
+ if (q->q_ptr != (caddr_t)NULL)
+ {
+ u.u_error = EBUSY;
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n"));
+ return OPENFAIL;
+ }
+
+#ifdef VDDRV
+ parsebusy++;
+#endif
+
+ q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t));
+ if (q->q_ptr == (caddr_t)0)
+ {
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - no memory\n"));
+ return OPENFAIL;
+ }
+ WR(q)->q_ptr = q->q_ptr;
+
+ parse = (parsestream_t *) q->q_ptr;
+ bzero((caddr_t)parse, sizeof(*parse));
+ parse->parse_queue = q;
+ parse->parse_status = PARSE_ENABLE;
+ parse->parse_ppsclockev.tv.tv_sec = 0;
+ parse->parse_ppsclockev.tv.tv_usec = 0;
+ parse->parse_ppsclockev.serial = 0;
+
+ if (!parse_ioinit(&parse->parse_io))
+ {
+ /*
+ * ok guys - beat it
+ */
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+#ifdef VDDRV
+ parsebusy--;
+#endif
+ return OPENFAIL;
+ }
+
+ if (setup_stream(q, M_PARSE))
+ {
+ (void) init_linemon(q); /* hook up PPS ISR routines if possible */
+ setup_delays();
+ parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n"));
+
+ /*
+ * I know that you know the delete key, but you didn't write this
+ * code, did you ? - So, keep the message in here.
+ */
+ if (!notice)
+ {
+ printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", parsesync_vd.Drv_name);
+ notice = 1;
+ }
+
+ return 1;
+ }
+ else
+ {
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+#ifdef VDDRV
+ parsebusy--;
+#endif
+ return OPENFAIL;
+ }
+}
+
+/*ARGSUSED*/
+static int parseclose(q, flags)
+ queue_t *q;
+ int flags;
+{
+ register parsestream_t *parse = (parsestream_t *)q->q_ptr;
+ register unsigned long s;
+
+ parseprintf(DD_CLOSE,("parse: CLOSE\n"));
+
+ s = splhigh();
+
+ if (parse->parse_dqueue)
+ close_linemon(parse->parse_dqueue, q);
+ parse->parse_dqueue = (queue_t *)0;
+
+ (void) splx(s);
+
+ parse_ioend(&parse->parse_io);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ q->q_ptr = (caddr_t)NULL;
+ WR(q)->q_ptr = (caddr_t)NULL;
+
+#ifdef VDDRV
+ parsebusy--;
+#endif
+}
+
+/*
+ * move unrecognized stuff upward
+ */
+static parsersvc(q)
+ queue_t *q;
+{
+ mblk_t *mp;
+
+ while (mp = getq(q))
+ {
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - putnext\n"));
+ }
+ else
+ {
+ putbq(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n"));
+ break;
+ }
+ }
+}
+
+/*
+ * do ioctls and
+ * send stuff down - dont care about
+ * flow control
+ */
+static int parsewput(q, mp)
+ queue_t *q;
+ register mblk_t *mp;
+{
+ register int ok = 1;
+ register mblk_t *datap;
+ register struct iocblk *iocp;
+ parsestream_t *parse = (parsestream_t *)q->q_ptr;
+
+ parseprintf(DD_WPUT,("parse: parsewput\n"));
+
+ switch (mp->b_datap->db_type)
+ {
+ default:
+ putnext(q, mp);
+ break;
+
+ case M_IOCTL:
+ iocp = (struct iocblk *)mp->b_rptr;
+ switch (iocp->ioc_cmd)
+ {
+ default:
+ parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n"));
+ putnext(q, mp);
+ break;
+
+ case CIOGETEV:
+ /*
+ * taken from Craig Leres ppsclock module (and modified)
+ */
+ datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
+ if (datap == NULL || mp->b_cont)
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
+ if (datap != NULL)
+ freeb(datap);
+ qreply(q, mp);
+ break;
+ }
+
+ mp->b_cont = datap;
+ *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev;
+ datap->b_wptr +=
+ sizeof(struct ppsclockev) / sizeof(*datap->b_wptr);
+ mp->b_datap->db_type = M_IOCACK;
+ iocp->ioc_count = sizeof(struct ppsclockev);
+ qreply(q, mp);
+ break;
+
+ case PARSEIOC_ENABLE:
+ case PARSEIOC_DISABLE:
+ {
+ parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) |
+ (iocp->ioc_cmd == PARSEIOC_ENABLE) ?
+ PARSE_ENABLE : 0;
+ if (!setup_stream(RD(q), (parse->parse_status & PARSE_ENABLE) ?
+ M_PARSE : M_NOPARSE))
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCACK;
+ }
+ qreply(q, mp);
+ break;
+ }
+
+ case PARSEIOC_SETSTAT:
+ case PARSEIOC_GETSTAT:
+ case PARSEIOC_TIMECODE:
+ case PARSEIOC_SETFMT:
+ case PARSEIOC_GETFMT:
+ case PARSEIOC_SETCS:
+ if (iocp->ioc_count == sizeof(parsectl_t))
+ {
+ parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr;
+
+ switch (iocp->ioc_cmd)
+ {
+ case PARSEIOC_GETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n"));
+ ok = parse_getstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n"));
+ ok = parse_setstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_TIMECODE:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n"));
+ ok = parse_timecode(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n"));
+ ok = parse_setfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_GETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n"));
+ ok = parse_getfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETCS:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n"));
+ ok = parse_setcs(dct, &parse->parse_io);
+ break;
+ }
+ mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"));
+ qreply(q, mp);
+ break;
+ }
+ }
+}
+
+/*
+ * read characters from streams buffers
+ */
+static unsigned long rdchar(mp)
+ register mblk_t **mp;
+{
+ while (*mp != (mblk_t *)NULL)
+ {
+ if ((*mp)->b_wptr - (*mp)->b_rptr)
+ {
+ return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++));
+ }
+ else
+ {
+ register mblk_t *mmp = *mp;
+
+ *mp = (*mp)->b_cont;
+ freeb(mmp);
+ }
+ }
+ return ~0;
+}
+
+/*
+ * convert incoming data
+ */
+static int parserput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ unsigned char type;
+
+ switch (type = mp->b_datap->db_type)
+ {
+ default:
+ /*
+ * anything we don't know will be put on queue
+ * the service routine will move it to the next one
+ */
+ parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ break;
+
+ case M_BREAK:
+ case M_DATA:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ register mblk_t *nmp;
+ register unsigned long ch;
+ timestamp_t ctime;
+
+ /*
+ * get time on packet delivery
+ */
+ uniqtime(&ctime.tv);
+
+ if (!(parse->parse_status & PARSE_ENABLE))
+ {
+ parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ }
+ else
+ {
+ parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK"));
+
+ if (type == M_DATA)
+ {
+ /*
+ * parse packet looking for start an end characters
+ */
+ while (mp != (mblk_t *)NULL)
+ {
+ ch = rdchar(&mp);
+ if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ }
+ }
+ else
+ {
+ if (parse_ioread(&parse->parse_io, (char)0, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ freemsg(mp);
+ }
+ break;
+ }
+ }
+
+ /*
+ * CD PPS support for non direct ISR hack
+ */
+ case M_HANGUP:
+ case M_UNHANGUP:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ timestamp_t ctime;
+ register mblk_t *nmp;
+ register int status = cd_invert ^ (type == M_HANGUP);
+
+ SET_LED(status);
+
+ uniqtime(&ctime.tv);
+
+ TIMEVAL_USADD(&ctime.tv, stdelay);
+
+ parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
+
+ if ((parse->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime))
+ {
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ freemsg(mp);
+ }
+ else
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+
+ if (status)
+ {
+ parse->parse_ppsclockev.tv = ctime.tv;
+ ++(parse->parse_ppsclockev.serial);
+ }
+ }
+ }
+}
+
+static int init_zs_linemon(); /* handle line monitor for "zs" driver */
+static void close_zs_linemon();
+static void zs_xsisr(); /* zs external status interupt handler */
+
+/*-------------------- CD isr status monitor ---------------*/
+
+static int init_linemon(q)
+ register queue_t *q;
+{
+ register queue_t *dq;
+
+ dq = WR(q);
+ /*
+ * we ARE doing very bad things down here (basically stealing ISR
+ * hooks)
+ *
+ * so we chase down the STREAMS stack searching for the driver
+ * and if this is a known driver we insert our ISR routine for
+ * status changes in to the ExternalStatus handling hook
+ */
+ while (dq->q_next)
+ {
+ dq = dq->q_next; /* skip down to driver */
+ }
+
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (dq->q_qinfo && dq->q_qinfo->qi_minfo)
+ {
+ register char *dname = dq->q_qinfo->qi_minfo->mi_idname;
+
+ parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname));
+
+#ifdef sun
+ if (dname && !strcmp(dname, "zs"))
+ {
+ return init_zs_linemon(dq, q);
+ }
+ else
+#endif
+ {
+ parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname));
+ return 0;
+ }
+ }
+ parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n"));
+ return 0;
+}
+
+static void close_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ register char *dname = q->q_qinfo->qi_minfo->mi_idname;
+
+#ifdef sun
+ if (dname && !strcmp(dname, "zs"))
+ {
+ close_zs_linemon(q, my_q);
+ return;
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname));
+#endif
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n"));
+}
+
+#ifdef sun
+#include <sundev/zsreg.h>
+#include <sundev/zscom.h>
+#include <sundev/zsvar.h>
+
+struct savedzsops
+{
+ struct zsops zsops;
+ struct zsops *oldzsops;
+};
+
+struct zsops *emergencyzs;
+
+static int init_zs_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ register struct zscom *zs;
+ register struct savedzsops *szs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+ /*
+ * we expect the zsaline pointer in the q_data pointer
+ * from there on we insert our on EXTERNAL/STATUS ISR routine
+ * into the interrupt path, before the standard handler
+ */
+ zs = ((struct zsaline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return 0;
+ }
+ else
+ {
+ unsigned long s;
+
+ /*
+ * we do a direct replacement, in case others fiddle also
+ * if somebody else grabs our hook and we disconnect
+ * we are in DEEP trouble - panic is likely to be next, sorry
+ */
+ szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops));
+
+ if (szs == (struct savedzsops *)0)
+ {
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n"));
+
+ return 0;
+ }
+ else
+ {
+ parsestream->parse_data = (void *)szs;
+
+ s = splhigh();
+
+ parsestream->parse_dqueue = q; /* remember driver */
+
+ szs->zsops = *zs->zs_ops;
+ szs->zsops.zsop_xsint = (int (*)())zs_xsisr; /* place our bastard */
+ szs->oldzsops = zs->zs_ops;
+ emergencyzs = zs->zs_ops;
+
+ zsopinit(zs, &szs->zsops); /* hook it up */
+
+ (void) splx(s);
+
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n"));
+
+ return 1;
+ }
+ }
+}
+
+/*
+ * unregister our ISR routine - must call under splhigh()
+ */
+static void close_zs_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ register struct zscom *zs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+
+ zs = ((struct zsaline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return;
+ }
+ else
+ {
+ register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
+
+ zsopinit(zs, szs->oldzsops); /* reset to previous handler functions */
+
+ kmem_free((caddr_t)szs, sizeof (struct savedzsops));
+
+ parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n"));
+ return;
+ }
+}
+
+#define MAXDEPTH 50 /* maximum allowed stream crawl */
+
+#ifdef PPS_SYNC
+extern hardpps();
+extern struct timeval time;
+#endif
+
+/*
+ * take external status interrupt (only CD interests us)
+ */
+static void zs_xsisr(zs)
+ register struct zscom *zs;
+{
+ register struct zsaline *za = (struct zsaline *)zs->zs_priv;
+ register struct zscc_device *zsaddr = zs->zs_addr;
+ register queue_t *q;
+ register unsigned char zsstatus;
+ register int loopcheck;
+ register char *dname;
+#ifdef PPS_SYNC
+ register int s;
+ register long usec;
+#endif
+
+ /*
+ * pick up current state
+ */
+ zsstatus = zsaddr->zscc_control;
+
+ if ((za->za_rr0 ^ zsstatus) & (ZSRR0_CD|ZSRR0_SYNC))
+ {
+ timestamp_t cdevent;
+ register int status;
+
+ /*
+ * CONDITIONAL external measurement support
+ */
+ SET_LED(zsstatus & (ZSRR0_CD|ZSRR0_SYNC)); /*
+ * inconsistent with upper SET_LED, but this
+ * is for oscilloscope business anyway and we
+ * are just interested in edge delays in the
+ * lower us range
+ */
+#ifdef PPS_SYNC
+ s = splclock();
+ usec = time.tv_usec;
+#endif
+ /*
+ * time stamp
+ */
+ uniqtime(&cdevent.tv);
+
+#ifdef PPS_SYNC
+ splx(s);
+#endif
+
+ /*
+ * logical state
+ */
+ status = cd_invert ? (zsstatus & ZSRR0_SYNC) == 0 : (zsstatus & ZSRR0_SYNC) != 0;
+
+#ifdef PPS_SYNC
+ if (status)
+ {
+ usec = cdevent.tv.tv_usec - usec;
+ if (usec < 0)
+ usec += 1000000;
+
+ hardpps(&cdevent.tv, usec);
+ }
+#endif
+
+ TIMEVAL_USADD(&cdevent.tv, xsdelay);
+
+ q = za->za_ttycommon.t_readq;
+
+ /*
+ * ok - now the hard part - find ourself
+ */
+ loopcheck = MAXDEPTH;
+
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+
+ if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+
+ if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent))
+ {
+ /*
+ * XXX - currently we do not pass up the message, as
+ * we should.
+ * for a correct behaviour wee need to block out
+ * processing until parse_iodone has been posted via
+ * a softcall-ed routine which does the message pass-up
+ * right now PPS information relies on input being
+ * received
+ */
+ parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io);
+ }
+
+ if (status)
+ {
+ ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
+ ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial);
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname));
+ break;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - CD event");
+ }
+ }
+
+ /*
+ * only pretend that CD has been handled
+ */
+ za->za_rr0 = za->za_rr0 & ~(ZSRR0_CD|ZSRR0_SYNC) | zsstatus & (ZSRR0_CD|ZSRR0_SYNC);
+ ZSDELAY(2);
+
+ if (!((za->za_rr0 ^ zsstatus) & ~(ZSRR0_CD|ZSRR0_SYNC)))
+ {
+ /*
+ * all done - kill status indication and return
+ */
+ zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */
+ return;
+ }
+ }
+
+ /*
+ * we are now gathered here to process some unusual external status
+ * interrupts.
+ * any CD events have also been handled and shouldn't be processed
+ * by the original routine (unless we have a VERY busy port pin)
+ * some initializations are done here, which could have been done before for
+ * both code paths but have been avioded for minimum path length to
+ * the uniq_time routine
+ */
+ dname = (char *) 0;
+ q = za->za_ttycommon.t_readq;
+
+ loopcheck = MAXDEPTH;
+
+ /*
+ * the real thing for everything else ...
+ */
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+ if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ register int (*zsisr)();
+
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+ if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint)
+ (void)zsisr(zs);
+ else
+ panic("zs_xsisr: unable to locate original ISR");
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname));
+ /*
+ * now back to our program ...
+ */
+ return;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
+ }
+ }
+
+ /*
+ * last resort - shouldn't even come here as it indicates
+ * corrupted TTY structures
+ */
+ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-");
+
+ if (emergencyzs && emergencyzs->zsop_xsint)
+ emergencyzs->zsop_xsint(zs);
+ else
+ panic("zs_xsisr: no emergency ISR handler");
+}
+#endif /* sun */
+
+/*
+ * History:
+ *
+ * parsestreams.c,v
+ * Revision 3.22 1994/06/01 10:41:16 kardel
+ * CD seems to happen on ZSRR0_SYNC
+ *
+ * Revision 3.21 1994/06/01 08:18:57 kardel
+ * look at CD only
+ *
+ * Revision 3.20 1994/05/30 09:57:43 kardel
+ * kmem_alloc checking
+ *
+ * Revision 3.19 1994/02/24 16:33:54 kardel
+ * CD events can olso be posted on sync flag
+ *
+ * Revision 3.18 1994/02/24 14:12:58 kardel
+ * initial PPS_SYNC support version
+ *
+ * Revision 3.17 1994/02/20 15:18:02 kardel
+ * rcs id cleanup
+ *
+ * Revision 3.16 1994/02/15 22:39:50 kardel
+ * memory leak on open failure closed
+ *
+ * Revision 3.15 1994/02/13 19:16:50 kardel
+ * updated verbose Copyright message
+ *
+ * Revision 3.14 1994/02/02 17:45:38 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.12 1994/01/25 19:05:30 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.11 1994/01/23 17:22:07 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.10 1993/12/15 12:48:58 kardel
+ * fixed message loss on M_*HANHUP messages
+ *
+ * Revision 3.9 1993/11/05 15:34:55 kardel
+ * shut up nice feature detection
+ *
+ * Revision 3.8 1993/10/22 14:27:56 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.7 1993/10/10 18:13:53 kardel
+ * Makefile reorganisation, file relocation
+ *
+ * Revision 3.6 1993/10/09 15:01:18 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/04 07:59:31 kardel
+ * Well, at least we should know that a the tv_usec field should be in the range 0..999999
+ *
+ * Revision 3.4 1993/09/26 23:41:33 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/09/11 00:38:34 kardel
+ * LINEMON must also cover M_[UN]HANGUP handling
+ *
+ * Revision 3.2 1993/07/06 10:02:56 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/util/Makefile b/usr.sbin/xntpd/parse/util/Makefile
new file mode 100644
index 0000000..851e8df
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/Makefile
@@ -0,0 +1,24 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../../include
+CFLAGS+= -DNTP_POSIX_SOURCE -DUSE_PROTOTYPES
+CFLAGS+= -DSYS_FREEBSD -DBOEDER -DHAVE_TERMIOS -DHAVE_BSD_NICE
+
+.if exists(${.OBJDIR}/../../lib)
+LDADD+= -L${.OBJDIR}/../../lib
+DPADD+= -L${.OBJDIR}/../../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../../lib
+DPADD+= -L${.CURDIR}/../../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+BINDIR= /usr/sbin
+PROG= dcfd
+
+SRCS= dcfd.c
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/parse/util/Makefile.tmpl b/usr.sbin/xntpd/parse/util/Makefile.tmpl
new file mode 100644
index 0000000..aa0b262
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/Makefile.tmpl
@@ -0,0 +1,49 @@
+#
+# /src/NTP/REPOSITORY/v3/parse/util/Makefile.tmpl,v 3.12 1994/01/25 19:05:39 kardel Exp
+#
+COMPILER= cc
+DEFS=
+DEFS_OPT=
+DEFS_LOCAL=
+CLOCKDEFS=
+INCL=
+COPTS= -O
+INSTALL= install
+BINDIR=
+#
+CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL) -I../../include
+CC= $(COMPILER)
+TOP=../../
+#
+EXECS=parsetest testdcf dcfd
+
+all:
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DSTREAM/ && /-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" parsetest"; } \
+ }' | \
+ sh
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" testdcf"; } \
+ }' | \
+ sh
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" dcfd"; } \
+ }' | \
+ sh
+
+clean:
+ -@rm -f $(EXECS) *.o
+
+distclean: clean
+ -@rm -f *.orig *.rej .version Makefile
+
+install:
+ @echo "--- DCF77 utilities should be installed manually"
+ @#[ -f testdcf ] && $(INSTALL) -c -m 0755 testdcf $(BINDIR) || true
+ @#[ -f dcfd ] && $(INSTALL) -c -m 0755 dcfd $(BINDIR) || true
diff --git a/usr.sbin/xntpd/parse/util/README b/usr.sbin/xntpd/parse/util/README
new file mode 100644
index 0000000..e1c80d4
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/README
@@ -0,0 +1,19 @@
+This directory contains some DCF77 related programs.
+They have not yet fully been ported to other architectures then Sun with
+SunOS 4.x. So if you want to try them you are on your own - a little
+porting may be necessary.
+
+parsetest: simple parse streams module test
+testdcf: simple DCF77 raw impulse test program via 50Baud RS232
+dcfd: simple DCF77 raw impulse receiver with NTP loopfilter
+ mechanics for synchronisation (allows DCF77 synchronisation
+ without network code in a nutshell)
+
+Frank Kardel
+
+----------------
+
+1995-03-20 Dcfd has been ported to FreeBSD 2.0, it works with a
+ Boeder Receiver connected to a 50Baud RS232.
+
+Vincenzo Capuano
diff --git a/usr.sbin/xntpd/parse/util/dcfd.c b/usr.sbin/xntpd/parse/util/dcfd.c
new file mode 100644
index 0000000..0acfa72
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/dcfd.c
@@ -0,0 +1,1643 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/dcfd.c,v 3.18 1994/05/12 12:49:23 kardel Exp
+ *
+ * dcfd.c,v 3.18 1994/05/12 12:49:23 kardel Exp
+ *
+ * Ported to FreeBSD 2.0 1995/03/20 by Vincenzo Capuano
+ *
+ * DCF77 100/200ms pulse synchronisation daemon program (via 50Baud serial line)
+ *
+ * Features:
+ * DCF77 decoding
+ * NTP loopfilter logic for local clock
+ * interactive display for debugging
+ *
+ * Lacks:
+ * Leap second handling (at that level you should switch to xntp3 - really!)
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * This program may not be sold or used for profit without prior
+ * written consent of the author.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/errno.h>
+#include <syslog.h>
+
+/*
+ * NTP compilation environment
+ */
+#ifdef USE_PROTOTYPES
+#include "ntp_stdlib.h"
+#include <signal.h>
+#include <stdio.h>
+#endif
+
+#ifdef SYS_LINUX
+#include "ntp_timex.h"
+#endif
+
+/*
+ * select which terminal handling to use (currently only SysV variants)
+ */
+#if defined(HAVE_TERMIOS) || defined(STREAM)
+#include <termios.h>
+#define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+#endif
+
+#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifndef TTY_GETATTR
+MUST DEFINE ONE OF "HAVE_TERMIOS" or "HAVE_TERMIO"
+#endif
+
+#ifndef dysize
+#define dysize(_x_) (((_x_) % 4) ? 365 : (((_x_) % 100) ? 366 : ((_x_) % 400 ? 365 : 366 )))
+#endif
+
+#define timernormalize(_a_) \
+ if ((_a_)->tv_usec >= 1000000) \
+ { \
+ (_a_)->tv_sec += (_a_)->tv_usec / 1000000; \
+ (_a_)->tv_usec = (_a_)->tv_usec % 1000000; \
+ } \
+ if ((_a_)->tv_usec < 0) \
+ { \
+ (_a_)->tv_sec -= 1 + -(_a_)->tv_usec / 1000000; \
+ (_a_)->tv_usec = 1000000 - (-(_a_)->tv_usec % 1000000); \
+ }
+
+#define timeradd(_a_, _b_) \
+ (_a_)->tv_sec += (_b_)->tv_sec; \
+ (_a_)->tv_usec += (_b_)->tv_usec; \
+ timernormalize((_a_))
+
+#define timersub(_a_, _b_) \
+ (_a_)->tv_sec -= (_b_)->tv_sec; \
+ (_a_)->tv_usec -= (_b_)->tv_usec; \
+ timernormalize((_a_))
+
+/*
+ * debug macros
+ */
+#define PRINTF if (interactive) printf
+#define LPRINTF if (interactive && loop_filter_debug) printf
+
+#ifdef DEBUG
+#define dprintf(_x_) PRINTF _x_
+#else
+#define dprintf(_x_)
+#endif
+
+extern int errno;
+
+/*
+ * display received data (avoids also detaching from tty)
+ */
+static int interactive = 0;
+
+/*
+ * display loopfilter (clock control) variables
+ */
+static int loop_filter_debug = 0;
+
+/*
+ * do not set/adjust system time
+ */
+static int no_set = 0;
+
+/*
+ * time that passes between start of DCF impulse and time stamping (fine
+ * adjustment) in microseconds (receiver/OS dependent)
+ */
+#define DEFAULT_DELAY 230000 /* rough estimate */
+
+/*
+ * The two states we can be in - eithe we receive nothing
+ * usable or we have the correct time
+ */
+#define NO_SYNC 0x01
+#define SYNC 0x02
+
+static int sync_state = NO_SYNC;
+static time_t last_sync;
+
+static unsigned long ticks = 0;
+
+static char pat[] = "-\\|/";
+
+#define LINES (24-2) /* error lines after which the two headlines are repeated */
+
+#define MAX_UNSYNC (10*60) /* allow synchronisation loss for 10 minutes */
+#define NOTICE_INTERVAL (20*60) /* mention missing synchronisation every 20 minutes */
+
+/*
+ * clock adjustment PLL - see NTP protocol spec (RFC1305) for details
+ */
+
+#define USECSCALE 10
+#define TIMECONSTANT 2
+#define ADJINTERVAL 0
+#define FREQ_WEIGHT 18
+#define PHASE_WEIGHT 7
+#define MAX_DRIFT 0x3FFFFFFF
+
+#define R_SHIFT(_X_, _Y_) (((_X_) < 0) ? -(-(_X_) >> (_Y_)) : ((_X_) >> (_Y_)))
+
+static struct timeval max_adj_offset = { 0, 128000 };
+
+static long clock_adjust = 0; /* current adjustment value (usec * 2^USECSCALE) */
+static long drift_comp = 0; /* accumulated drift value (usec / ADJINTERVAL) */
+static long adjustments = 0;
+static char skip_adjust = 1; /* discard first adjustment (bad samples) */
+
+/*
+ * DCF77 state flags
+ */
+#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */
+#define DCFB_DST 0x0002 /* DST in effect */
+#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */
+#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */
+
+struct clocktime /* clock time broken up from time code */
+{
+ long wday; /* Day of week: 1: Monday - 7: Sunday */
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in minutes */
+ long flags; /* current clock status (DCF77 state flags) */
+};
+
+typedef struct clocktime clocktime_t;
+
+/*
+ * (usually) quick constant multiplications
+ */
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1)) /* *8 + *2 */
+#define TIMES24(_X_) (((_X_) << 4) + ((_X_) << 3)) /* *16 + *8 */
+#define TIMES60(_X_) ((((_X_) << 4) - (_X_)) << 2) /* *(16 - 1) *4 */
+/*
+ * generic abs() function
+ */
+#define abs(_x_) (((_x_) < 0) ? -(_x_) : (_x_))
+
+/*
+ * conversion related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+#define CVT_BADDATE 0x00000020 /* invalid date */
+#define CVT_BADTIME 0x00000040 /* invalid time */
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+/*-----------------------------------------------------------------------
+ * conversion table to map DCF77 bit stream into data fields.
+ * Encoding:
+ * Each field of the DCF77 code is described with two adjacent entries in
+ * this table. The first entry specifies the offset into the DCF77 data stream
+ * while the length is given as the difference between the start index and
+ * the start index of the following field.
+ */
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+/*-----------------------------------------------------------------------
+ * symbolic names for the fields of DCF77 describes in "rawdcfcode".
+ * see comment above for the structure of the DCF77 data
+ */
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+/*-----------------------------------------------------------------------
+ * parity field table (same encoding as rawdcfcode)
+ * This table describes the sections of the DCF77 code that are
+ * parity protected
+ */
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+/*-----------------------------------------------------------------------
+ * offsets for parity field descriptions
+ */
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+/*-----------------------------------------------------------------------
+ * legal values for time zone information
+ */
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+/*-----------------------------------------------------------------------
+ * symbolic representation if the DCF77 data stream
+ */
+static struct dcfparam
+{
+ unsigned char onebits[60];
+ unsigned char zerobits[60];
+} dcfparam =
+{
+ "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */
+ "--------------------s-------p------p----------------------p" /* 'ZERO' representation */
+};
+
+/*-----------------------------------------------------------------------
+ * extract a bitfield from DCF77 datastream
+ * All numeric field are LSB first.
+ * buf holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * idx holds the index to the field description in rawdcfcode
+ */
+static unsigned long ext_bf(buf, idx)
+ register unsigned char *buf;
+ register int idx;
+{
+ register unsigned long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != dcfparam.zerobits[i]);
+ }
+ return sum;
+}
+
+/*-----------------------------------------------------------------------
+ * check even parity integrity for a bitfield
+ *
+ * buf holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * idx holds the index to the field description in partab
+ */
+static unsigned pcheck(buf, idx)
+ register unsigned char *buf;
+ register int idx;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != dcfparam.zerobits[i]);
+
+ return psum;
+}
+
+/*-----------------------------------------------------------------------
+ * convert a DCF77 data buffer into wall clock time + flags
+ *
+ * buffer holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * size describes the length of DCF77 information in bits (represented
+ * as chars in symbolic notation
+ * clock points to a wall clock time description of the DCF77 data (result)
+ */
+static unsigned long convert_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ if (size < 57)
+ {
+ PRINTF("%-30s", "*** INCOMPLETE");
+ return CVT_NONE;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S) == 1) &&
+ pcheck(buffer, DCF_P_P1) &&
+ pcheck(buffer, DCF_P_P2) &&
+ pcheck(buffer, DCF_P_P3))
+ {
+ /*
+ * buffer OK - extract all fields and build wall clock time from them
+ */
+
+ clock->flags = 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1);
+ clock->hour = ext_bf(buffer, DCF_H10);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1);
+ clock->day = ext_bf(buffer, DCF_D10);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1);
+ clock->month = ext_bf(buffer, DCF_MO0);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO);
+ clock->year = ext_bf(buffer, DCF_Y10);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1);
+ clock->wday = ext_bf(buffer, DCF_DW);
+
+ /*
+ * determine offset to UTC by examining the time zone
+ */
+ switch (ext_bf(buffer, DCF_Z))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= DCFB_DST;
+ clock->utcoffset = -120;
+ break;
+
+ default:
+ PRINTF("%-30s", "*** BAD TIME ZONE");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ /*
+ * extract various warnings from DCF77
+ */
+ if (ext_bf(buffer, DCF_A1))
+ clock->flags |= DCFB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2))
+ clock->flags |= DCFB_LEAP;
+
+ if (ext_bf(buffer, DCF_R))
+ clock->flags |= DCFB_ALTERNATE;
+
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+ PRINTF("%-30s", "*** BAD FORMAT (invalid/parity)");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * raw dcf input routine - fix up 50 baud
+ * characters for 1/0 decision
+ */
+static unsigned long cvt_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *e = buffer + size;
+ register unsigned char *b = dcfparam.onebits;
+ register unsigned char *c = dcfparam.zerobits;
+ register unsigned rtc = CVT_NONE;
+ register unsigned int i, lowmax, highmax, cutoff, span;
+#define BITS 9
+ unsigned char histbuf[BITS];
+ /*
+ * the input buffer contains characters with runs of consecutive
+ * bits set. These set bits are an indication of the DCF77 pulse
+ * length. We assume that we receive the pulse at 50 Baud. Thus
+ * a 100ms pulse would generate a 4 bit train (20ms per bit and
+ * start bit)
+ * a 200ms pulse would create all zeroes (and probably a frame error)
+ *
+ * The basic idea is that on corret reception we must have two
+ * maxima in the pulse length distribution histogram. (one for
+ * the zero representing pulses and one for the one representing
+ * pulses)
+ * There will always be ones in the datastream, thus we have to see
+ * two maxima.
+ * The best point to cut for a 1/0 decision is the minimum between those
+ * between the maxima. The following code tries to find this cutoff point.
+ */
+
+ /*
+ * clear histogram buffer
+ */
+ for (i = 0; i < BITS; i++)
+ {
+ histbuf[i] = 0;
+ }
+
+ cutoff = 0;
+ lowmax = 0;
+
+ /*
+ * convert sequences of set bits into bits counts updating
+ * the histogram alongway
+ */
+ while (s < e)
+ {
+ register unsigned int ch = *s ^ 0xFF;
+ /*
+ * check integrity and update histogramm
+ */
+ if (!((ch+1) & ch) || !*s)
+ {
+ /*
+ * character ok
+ */
+ for (i = 0; ch; i++)
+ {
+ ch >>= 1;
+ }
+
+ *s = i;
+ histbuf[i]++;
+ cutoff += i;
+ lowmax++;
+ }
+ else
+ {
+ /*
+ * invalid character (no consecutive bit sequence)
+ */
+ dprintf(("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer));
+ *s = ~0;
+ rtc = CVT_FAIL|CVT_BADFMT;
+ }
+ s++;
+ }
+
+ /*
+ * first cutoff estimate (average bit count - must be between both
+ * maxima)
+ */
+ if (lowmax)
+ {
+ cutoff /= lowmax;
+ }
+ else
+ {
+ cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */
+ }
+
+ dprintf(("parse: cvt_rawdcf: average bit count: %d\n", cutoff));
+
+ lowmax = 0; /* weighted sum */
+ highmax = 0; /* bitcount */
+
+ /*
+ * collect weighted sum of lower bits (left of initial guess)
+ */
+ dprintf(("parse: cvt_rawdcf: histogram:"));
+ for (i = 0; i <= cutoff; i++)
+ {
+ lowmax += histbuf[i] * i;
+ highmax += histbuf[i];
+ dprintf((" %d", histbuf[i]));
+ }
+ dprintf((" <M>"));
+
+ /*
+ * round up
+ */
+ lowmax += highmax / 2;
+
+ /*
+ * calculate lower bit maximum (weighted sum / bit count)
+ *
+ * avoid divide by zero
+ */
+ if (highmax)
+ {
+ lowmax /= highmax;
+ }
+ else
+ {
+ lowmax = 0;
+ }
+
+ highmax = 0; /* weighted sum of upper bits counts */
+ cutoff = 0; /* bitcount */
+
+ /*
+ * collect weighted sum of lower bits (right of initial guess)
+ */
+ for (; i < BITS; i++)
+ {
+ highmax+=histbuf[i] * i;
+ cutoff +=histbuf[i];
+ dprintf((" %d", histbuf[i]));
+ }
+ dprintf(("\n"));
+
+ /*
+ * determine upper maximum (weighted sum / bit count)
+ */
+ if (cutoff)
+ {
+ highmax /= cutoff;
+ }
+ else
+ {
+ highmax = BITS-1;
+ }
+
+ /*
+ * following now holds:
+ * lowmax <= cutoff(initial guess) <= highmax
+ * best cutoff is the minimum nearest to higher bits
+ */
+
+ /*
+ * find the minimum between lowmax and highmax (detecting
+ * possibly a minimum span)
+ */
+ span = cutoff = lowmax;
+ for (i = lowmax; i <= highmax; i++)
+ {
+ if (histbuf[cutoff] > histbuf[i])
+ {
+ /*
+ * got a new minimum move beginning of minimum (cutoff) and
+ * end of minimum (span) there
+ */
+ cutoff = span = i;
+ }
+ else
+ if (histbuf[cutoff] == histbuf[i])
+ {
+ /*
+ * minimum not better yet - but it spans more than
+ * one bit value - follow it
+ */
+ span = i;
+ }
+ }
+
+ /*
+ * cutoff point for 1/0 decision is the middle of the minimum section
+ * in the histogram
+ */
+ cutoff = (cutoff + span) / 2;
+
+ dprintf(("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff));
+
+ /*
+ * convert the bit counts to symbolic 1/0 information for data conversion
+ */
+ s = buffer;
+ while ((s < e) && *c && *b)
+ {
+ if (*s == (unsigned char)~0)
+ {
+ /*
+ * invalid character
+ */
+ *s = '?';
+ }
+ else
+ {
+ /*
+ * symbolic 1/0 representation
+ */
+ *s = (*s >= cutoff) ? *b : *c;
+ }
+ s++;
+ b++;
+ c++;
+ }
+
+ /*
+ * if everything went well so far return the result of the symbolic
+ * conversion routine else just the accumulated errors
+ */
+ if (rtc != CVT_NONE)
+ {
+ PRINTF("%-30s", "*** BAD DATA");
+ }
+
+ return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, clock) : rtc;
+}
+
+/*-----------------------------------------------------------------------
+ * convert a wall clock time description of DCF77 to a Unix time (seconds
+ * since 1.1. 1970 UTC)
+ */
+time_t
+dcf_to_unixtime(clock, cvtrtc)
+ register clocktime_t *clock;
+ register unsigned long *cvtrtc;
+{
+#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); }
+ static int days_of_month[] =
+ {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ register int i;
+ time_t t;
+
+ /*
+ * map 2 digit years to 19xx (DCF77 is a 20th century item)
+ */
+ if (clock->year < 100)
+ clock->year += 1900;
+
+ /*
+ * assume that we convert timecode within the unix/UTC epoch -
+ * prolonges validity of 2 digit years
+ */
+ if (clock->year < 1994)
+ clock->year += 100; /* XXX this will do it till <2094 */
+
+ /*
+ * must have been a really negative year code - drop it
+ */
+ if (clock->year < 0)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1;
+ }
+
+ /*
+ * sorry, slow section here - but it's not time critical anyway
+ */
+
+ /*
+ * calculate days since 1970 (watching leap years)
+ */
+ t = (clock->year - 1970) * 365;
+ t += (clock->year >> 2) - (1970 >> 2);
+ t -= clock->year / 100 - 1970 / 100;
+ t += clock->year / 400 - 1970 / 400;
+
+ /* month */
+ if (clock->month <= 0 || clock->month > 12)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad month */
+ }
+ /* adjust current leap year */
+ if (clock->month >= 3 && dysize(clock->year) == 366)
+ t++;
+
+ /*
+ * collect days from months excluding the current one
+ */
+ for (i = 1; i < clock->month; i++)
+ {
+ t += days_of_month[i];
+ }
+ /* day */
+ if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ?
+ clock->day > 29 : clock->day > days_of_month[clock->month]))
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad day */
+ }
+
+ /*
+ * collect days from date excluding the current one
+ */
+ t += clock->day - 1;
+
+ /* hour */
+ if (clock->hour < 0 || clock->hour >= 24)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad hour */
+ }
+
+ /*
+ * calculate hours from 1. 1. 1970
+ */
+ t = TIMES24(t) + clock->hour;
+
+ /* min */
+ if (clock->minute < 0 || clock->minute > 59)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad min */
+ }
+
+ /*
+ * calculate minutes from 1. 1. 1970
+ */
+ t = TIMES60(t) + clock->minute;
+ /* sec */
+
+ /*
+ * calculate UTC in minutes
+ */
+ t += clock->utcoffset;
+
+ if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad sec */
+ }
+
+ /*
+ * calculate UTC in seconds - phew !
+ */
+ t = TIMES60(t) + clock->second;
+ /* done */
+ return t;
+}
+
+/*-----------------------------------------------------------------------
+ * cheap half baked 1/0 decision - for interactive operation only
+ */
+static char type(c)
+unsigned char c;
+{
+ c ^= 0xFF;
+ return (c > 0xF);
+}
+
+/*-----------------------------------------------------------------------
+ * week day representation
+ */
+static char *wday[8] =
+{
+ "??",
+ "Mo",
+ "Tu",
+ "We",
+ "Th",
+ "Fr",
+ "Sa",
+ "Su"
+};
+
+/*-----------------------------------------------------------------------
+ * generate a string representation for a timeval
+ */
+static char * pr_timeval(val)
+ struct timeval *val;
+{
+ static char buf[20];
+
+ if (val->tv_sec == 0)
+ sprintf(buf, "%c0.%06ld", (val->tv_usec < 0) ? '-' : '+', (long int)abs(val->tv_usec));
+ else
+ sprintf(buf, "%ld.%06ld", (long int)val->tv_sec, (long int)abs(val->tv_usec));
+ return buf;
+}
+
+/*-----------------------------------------------------------------------
+ * correct the current time by an offset by setting the time rigorously
+ */
+static void set_time(offset)
+ struct timeval *offset;
+{
+ struct timeval the_time;
+
+ if (no_set)
+ return;
+
+ LPRINTF("set_time: %s ", pr_timeval(offset));
+ syslog(LOG_NOTICE, "setting time (offset %s)", pr_timeval(offset));
+
+ if (gettimeofday(&the_time, 0L) == -1)
+ {
+ perror("gettimeofday()");
+ }
+ else
+ {
+ timeradd(&the_time, offset);
+ if (settimeofday(&the_time, 0L) == -1)
+ {
+ perror("settimeofday()");
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * slew the time by a given offset
+ */
+static void adj_time(offset)
+ register long offset;
+{
+ struct timeval time_offset;
+
+ if (no_set)
+ return;
+
+ time_offset.tv_sec = offset / 1000000;
+ time_offset.tv_usec = offset % 1000000;
+
+ LPRINTF("adj_time: %ld us ", (long int)offset);
+ if (adjtime(&time_offset, 0L) == -1)
+ perror("adjtime()");
+}
+
+/*-----------------------------------------------------------------------
+ * read in a possibly previously written drift value
+ */
+static void read_drift(drift_file)
+ char *drift_file;
+{
+ FILE *df;
+
+ df = fopen(drift_file, "r");
+ if (df != NULL)
+ {
+ int idrift, fdrift;
+
+ fscanf(df, "%4d.%03d", &idrift, &fdrift);
+ fclose(df);
+ LPRINTF("read_drift: %d.%03d ppm ", idrift, fdrift);
+
+ drift_comp = idrift << USECSCALE;
+ fdrift = (fdrift << USECSCALE) / 1000;
+ drift_comp += fdrift & (1<<USECSCALE);
+ LPRINTF("read_drift: drift_comp %ld ", (long int)drift_comp);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * write out the current drift value
+ */
+static void update_drift(drift_file, offset, reftime)
+ char *drift_file;
+ long offset;
+ time_t reftime;
+{
+ FILE *df;
+
+ df = fopen(drift_file, "w");
+ if (df != NULL)
+ {
+ int idrift = R_SHIFT(drift_comp, USECSCALE);
+ int fdrift = drift_comp & ((1<<USECSCALE)-1);
+
+ LPRINTF("update_drift: drift_comp %ld ", (long int)drift_comp);
+ fdrift = (fdrift * 1000) / (1<<USECSCALE);
+ fprintf(df, "%4d.%03d %c%ld.%06ld %.24s\n", idrift, fdrift,
+ (offset < 0) ? '-' : '+', (long int)(abs(offset) / 1000000),
+ (long int)(abs(offset) % 1000000), asctime(localtime(&reftime)));
+ fclose(df);
+ LPRINTF("update_drift: %d.%03d ppm ", idrift, fdrift);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * process adjustments derived from the DCF77 observation
+ * (controls clock PLL)
+ */
+static void adjust_clock(offset, drift_file, reftime)
+ struct timeval *offset;
+ char *drift_file;
+ time_t reftime;
+{
+ struct timeval toffset;
+ register long usecoffset;
+ int tmp;
+
+ if (no_set)
+ return;
+
+ if (skip_adjust)
+ {
+ skip_adjust = 0;
+ return;
+ }
+
+ toffset = *offset;
+ toffset.tv_sec = abs(toffset.tv_sec);
+ toffset.tv_usec = abs(toffset.tv_usec);
+ if (timercmp(&toffset, &max_adj_offset, >))
+ {
+ /*
+ * hopeless - set the clock - and clear the timing
+ */
+ set_time(offset);
+ clock_adjust = 0;
+ skip_adjust = 1;
+ return;
+ }
+
+ usecoffset = offset->tv_sec * 1000000 + offset->tv_usec;
+
+ clock_adjust = R_SHIFT(usecoffset, TIMECONSTANT); /* adjustment to make for next period */
+
+ tmp = 0;
+ while (adjustments > (1 << tmp))
+ tmp++;
+ adjustments = 0;
+ if (tmp > FREQ_WEIGHT)
+ tmp = FREQ_WEIGHT;
+
+ drift_comp += R_SHIFT(usecoffset << USECSCALE, TIMECONSTANT+TIMECONSTANT+FREQ_WEIGHT-tmp);
+
+ if (drift_comp > MAX_DRIFT) /* clamp into interval */
+ drift_comp = MAX_DRIFT;
+ else
+ if (drift_comp < -MAX_DRIFT)
+ drift_comp = -MAX_DRIFT;
+
+ update_drift(drift_file, usecoffset, reftime);
+ LPRINTF("clock_adjust: %s, clock_adjust %ld, drift_comp %ld(%ld) ",
+ pr_timeval(offset),(long int) R_SHIFT(clock_adjust, USECSCALE),
+ (long int)R_SHIFT(drift_comp, USECSCALE), (long int)drift_comp);
+}
+
+/*-----------------------------------------------------------------------
+ * adjust the clock by a small mount to simulate frequency correction
+ */
+static void periodic_adjust()
+{
+ register long adjustment;
+
+ adjustments++;
+
+ adjustment = R_SHIFT(clock_adjust, PHASE_WEIGHT);
+
+ clock_adjust -= adjustment;
+
+ adjustment += R_SHIFT(drift_comp, USECSCALE+ADJINTERVAL);
+
+ adj_time(adjustment);
+}
+
+/*-----------------------------------------------------------------------
+ * control synchronisation status (warnings) and do periodic adjusts
+ * (frequency control simulation)
+ */
+static void tick()
+{
+ static unsigned long last_notice = 0;
+
+#ifndef SV_ONSTACK
+ (void)signal(SIGALRM, tick);
+#endif
+
+ periodic_adjust();
+
+ ticks += 1<<ADJINTERVAL;
+
+ if ((ticks - last_sync) > MAX_UNSYNC)
+ {
+ /*
+ * not getting time for a while
+ */
+ if (sync_state == SYNC)
+ {
+ /*
+ * completely lost information
+ */
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (timeout)");
+ last_notice = ticks;
+ }
+ else
+ /*
+ * in NO_SYNC state - look whether its time to speak up again
+ */
+ if ((ticks - last_notice) > NOTICE_INTERVAL)
+ {
+ syslog(LOG_NOTICE, "still not synchronized to DCF77 - check receiver/signal");
+ last_notice = ticks;
+ }
+ }
+
+#ifndef ITIMER_REAL
+ (void) alarm(1<<ADJINTERVAL);
+#endif
+}
+
+/*-----------------------------------------------------------------------
+ * break association from terminal to avoid catching terminal
+ * or process group related signals (-> daemon operation)
+ */
+static void detach()
+{
+ int s;
+
+ if (fork())
+ exit(0);
+
+ for (s = 0; s < 3; s++)
+ (void) close(s);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+#if defined(NTP_POSIX_SOURCE) || defined(_POSIX_)
+ (void) setsid();
+#else /* _POSIX_ */
+#ifndef BSD
+ (void) setpgrp();
+#else /* BSD */
+ (void) setpgrp(0, getpid());
+#endif /* BSD */
+#endif /* _POSIX_ */
+#if defined(hpux)
+ if (fork())
+ exit(0);
+#endif /* hpux */
+}
+
+/*-----------------------------------------------------------------------
+ * list possible arguments and options
+ */
+static void usage(program)
+ char *program;
+{
+ fprintf(stderr, "usage: %s [-f] [-l] [-t] [-i] [-o] [-d <drift_file>] <device>\n", program);
+ fprintf(stderr, "\t-n do not change time\n");
+ fprintf(stderr, "\t-i interactive\n");
+ fprintf(stderr, "\t-t trace (print all datagrams)\n");
+ fprintf(stderr, "\t-f print all databits (includes PTB private data)\n");
+ fprintf(stderr, "\t-l print loop filter debug information\n");
+ fprintf(stderr, "\t-o print offet average for current minute\n");
+ fprintf(stderr, "\t-d <drift_file> specify alternate drift file\n");
+ fprintf(stderr, "\t-D <input delay>specify delay from input edge to processing in micro seconds\n");
+}
+
+/*-----------------------------------------------------------------------
+ * main loop - argument interpreter / setup / main loop
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ unsigned char c;
+ char **a = argv;
+ int ac = argc;
+ char *file = NULL;
+ char *drift_file = "/etc/dcfd.drift";
+ int fd;
+ int offset = 15;
+ int offsets = 0;
+ int delay = DEFAULT_DELAY; /* average delay from input edge to time stamping */
+ int trace = 0;
+ int errs = 0;
+
+ /*
+ * process arguments
+ */
+ while (--ac)
+ {
+ char *arg = *++a;
+ if (*arg == '-')
+ while ((c = *++arg))
+ switch (c)
+ {
+ case 't':
+ trace = 1;
+ interactive = 1;
+ break;
+
+ case 'f':
+ offset = 0;
+ interactive = 1;
+ break;
+
+ case 'l':
+ loop_filter_debug = 1;
+ offsets = 1;
+ interactive = 1;
+ break;
+
+ case 'n':
+ no_set = 1;
+ break;
+
+ case 'o':
+ offsets = 1;
+ interactive = 1;
+ break;
+
+ case 'i':
+ interactive = 1;
+ break;
+
+ case 'D':
+ if (ac > 1)
+ {
+ delay = atoi(*++a);
+ ac--;
+ }
+ else
+ {
+ fprintf(stderr, "%s: -D requires integer argument\n", argv[0]);
+ errs=1;
+ }
+ break;
+
+ case 'd':
+ if (ac > 1)
+ {
+ drift_file = *++a;
+ ac--;
+ }
+ else
+ {
+ fprintf(stderr, "%s: -d requires file name argument\n", argv[0]);
+ errs=1;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
+ errs=1;
+ break;
+ }
+ else
+ if (file == NULL)
+ file = arg;
+ else
+ {
+ fprintf(stderr, "%s: device specified twice\n", argv[0]);
+ errs=1;
+ }
+ }
+
+ if (errs)
+ {
+ usage(argv[0]);
+ exit(1);
+ }
+ else
+ if (file == NULL)
+ {
+ fprintf(stderr, "%s: device not specified\n", argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+
+ errs = LINES+1;
+
+ /*
+ * get access to DCF77 tty port
+ */
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ fd = open(file, O_RDONLY | O_NONBLOCK);
+#else
+ fd = open(file, O_RDONLY);
+#endif
+ if (fd == -1)
+ {
+ perror(file);
+ exit(1);
+ }
+ else
+ {
+ int i, rrc;
+ struct timeval t, tt, tlast;
+ struct timeval timeout;
+ struct timeval phase;
+ struct timeval time_offset;
+ char pbuf[61]; /* printable version */
+ char buf[61]; /* raw data */
+ clocktime_t clock; /* wall clock time */
+ time_t utc_time = 0;
+ time_t last_utc_time = 0;
+ long usecerror = 0;
+ long lasterror = 0;
+#if defined(HAVE_TERMIOS) || defined(STREAM)
+ struct termios term;
+#endif
+#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS)
+ struct termio term;
+#endif
+ int rtc = CVT_NONE;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000;
+
+ phase.tv_sec = 0;
+ phase.tv_usec = delay;
+
+ /*
+ * setup TTY (50 Baud, Read, 8Bit, No Hangup, 1 character IO)
+ */
+ if (TTY_GETATTR(fd, &term) == -1)
+ {
+ perror("tcgetattr");
+ exit(1);
+ }
+
+ memset(term.c_cc, 0, sizeof(term.c_cc));
+ term.c_cc[VMIN] = 1;
+#if defined(SYS_FREEBSD)
+ term.c_cflag = CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = 0;
+#else
+ term.c_cflag = B50|CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = IGNPAR;
+#endif
+ term.c_oflag = 0;
+ term.c_lflag = 0;
+
+#if defined(SYS_FREEBSD)
+ if (cfsetspeed(&term, B50) == -1)
+ perror("cfsetspeed");
+#endif
+ if (TTY_SETATTR(fd, &term) == -1)
+ {
+ perror("tcsetattr");
+ exit(1);
+ }
+
+ /*
+ * loose terminal if in daemon operation
+ */
+ if (!interactive)
+ detach();
+
+ /*
+ * get syslog() initialized
+ */
+#ifdef LOG_DAEMON
+ openlog("dcfd", LOG_PID, LOG_DAEMON);
+#else
+ openlog("dcfd", LOG_PID);
+#endif
+
+ /*
+ * setup periodic operations (state control / frequency control)
+ */
+#ifdef SV_ONSTACK
+ {
+ struct sigvec vec;
+
+ vec.sv_handler = tick;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+
+ if (sigvec(SIGALRM, &vec, (struct sigvec *)0) == -1)
+ {
+ syslog(LOG_ERR, "sigvec(SIGALRM): %m");
+ exit(1);
+ }
+ }
+#else
+ (void) signal(SIGALRM, tick);
+#endif
+
+#ifdef ITIMER_REAL
+ {
+ struct itimerval it;
+
+ it.it_interval.tv_sec = 1<<ADJINTERVAL;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 1<<ADJINTERVAL;
+ it.it_value.tv_usec = 0;
+
+ if (setitimer(ITIMER_REAL, &it, (struct itimerval *)0) == -1)
+ {
+ syslog(LOG_ERR, "setitimer: %m");
+ exit(1);
+ }
+ }
+#else
+ (void) alarm(1<<ADJINTERVAL);
+#endif
+#if defined(SYS_FREEBSD) && defined(CONRAD)
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ perror("F_SETFL");
+
+ {
+ int i;
+
+ if (ioctl(fd, TIOCMGET, &i) == -1)
+ perror("TIOCMGET");
+ i |= TIOCM_DTR;
+ i &= ~TIOCM_RTS;
+ if (ioctl(fd, TIOCMSET, &i) == -1)
+ perror("TIOCMSET");
+ }
+#endif
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ perror("F_SETFL");
+
+ if (ioctl(fd, TIOCCDTR, 0) == -1)
+ perror("TIOCCDTR");
+#endif
+
+ PRINTF(" DCF77 monitor - Copyright 1993,1994, Frank Kardel\n\n");
+ syslog(LOG_NOTICE, "Starting on %s", file);
+
+ pbuf[60] = '\0';
+ for ( i = 0; i < 60; i++)
+ pbuf[i] = '.';
+
+ read_drift(drift_file);
+
+ /*
+ * what time is it now (for interval measurement)
+ */
+ gettimeofday(&tlast, 0L);
+ i = 0;
+ /*
+ * loop until input trouble ...
+ */
+ do
+ {
+ /*
+ * get an impulse
+ */
+ while ((rrc = read(fd, &c, 1)) == 1)
+ {
+ gettimeofday(&t, 0L);
+ tt = t;
+ timersub(&t, &tlast);
+
+ if (errs > LINES)
+ {
+ PRINTF(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]);
+ PRINTF(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]);
+ errs = 0;
+ }
+
+ /*
+ * timeout -> possible minute mark -> interpretation
+ */
+ if (timercmp(&t, &timeout, >))
+ {
+ PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]);
+
+ if ((rtc = cvt_rawdcf(buf, i, &clock)) != CVT_OK)
+ {
+ /*
+ * this data was bad - well - forget synchronisation for now
+ */
+ PRINTF("\n");
+ if (sync_state == SYNC)
+ {
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (bad data)");
+ }
+ errs++;
+ }
+ else
+ if (trace)
+ {
+ PRINTF("\r %.*s ", 59 - offset, &buf[offset]);
+ }
+
+
+ buf[0] = c;
+
+ /*
+ * collect first character
+ */
+ if (((c^0xFF)+1) & (c^0xFF))
+ pbuf[0] = '?';
+ else
+ pbuf[0] = type(c) ? '#' : '-';
+
+ for ( i = 1; i < 60; i++)
+ pbuf[i] = '.';
+
+ i = 0;
+ }
+ else
+ {
+ /*
+ * collect character
+ */
+ buf[i] = c;
+
+ /*
+ * initial guess (usually correct)
+ */
+ if (((c^0xFF)+1) & (c^0xFF))
+ pbuf[i] = '?';
+ else
+ pbuf[i] = type(c) ? '#' : '-';
+
+ PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]);
+ }
+
+ if (i == 0 && rtc == CVT_OK)
+ {
+ /*
+ * we got a good time code here - try to convert it to
+ * UTC
+ */
+ if ((utc_time = dcf_to_unixtime(&clock, &rtc)) == -1)
+ {
+ PRINTF("*** BAD CONVERSION\n");
+ }
+
+ if (utc_time != (last_utc_time + 60))
+ {
+ /*
+ * well, two successive sucessful telegrams are not 60 seconds
+ * apart
+ */
+ PRINTF("*** NO MINUTE INC\n");
+ if (sync_state == SYNC)
+ {
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (data mismatch)");
+ }
+ errs++;
+ rtc = CVT_FAIL|CVT_BADTIME|CVT_BADDATE;
+ }
+ else
+ usecerror = 0;
+
+ last_utc_time = utc_time;
+ }
+
+ if (rtc == CVT_OK)
+ {
+ if (i == 0)
+ {
+ /*
+ * valid time code - determine offset and
+ * note regained reception
+ */
+ last_sync = ticks;
+ if (sync_state == NO_SYNC)
+ {
+ syslog(LOG_INFO, "receiving DCF77");
+ }
+ else
+ {
+ /*
+ * we had at least one minute SYNC - thus
+ * last error is valid
+ */
+#if defined(BOEDER)
+ if (abs(utc_time - tt.tv_sec) > 10)
+ {
+ time_offset.tv_sec = utc_time - tt.tv_sec;
+ time_offset.tv_usec = 0;
+ }
+ else
+ {
+ time_offset.tv_sec = lasterror / 1000000;
+ time_offset.tv_usec = lasterror % 1000000;
+ }
+#else
+ time_offset.tv_sec = lasterror / 1000000;
+ time_offset.tv_usec = lasterror % 1000000;
+#endif
+ adjust_clock(&time_offset, drift_file, utc_time);
+ }
+ sync_state = SYNC;
+ }
+
+ time_offset.tv_sec = utc_time + i;
+ time_offset.tv_usec = 0;
+
+ timeradd(&time_offset, &phase);
+
+ usecerror += (time_offset.tv_sec - tt.tv_sec) * 1000000 + time_offset.tv_usec
+ -tt.tv_usec;
+
+ /*
+ * output interpreted DCF77 data. DCF77 gives us a YY year
+ * but dcf_to_unixtime() adds the century, so print YYYY.
+ */
+ PRINTF(offsets ? "%s, %2d:%02d:%02d, %d.%02d.%4d, <%s%s%s%s> (%c%d.%06ds)" :
+ "%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>",
+ wday[clock.wday],
+ clock.hour, clock.minute, i, clock.day, clock.month,
+ clock.year,
+ (clock.flags & DCFB_ALTERNATE) ? "R" : "_",
+ (clock.flags & DCFB_ANNOUNCE) ? "A" : "_",
+ (clock.flags & DCFB_DST) ? "D" : "_",
+ (clock.flags & DCFB_LEAP) ? "L" : "_",
+ (lasterror < 0) ? '-' : '+', abs(lasterror) / 1000000, abs(lasterror) % 1000000
+ );
+
+ if (trace && (i == 0))
+ {
+ PRINTF("\n");
+ errs++;
+ }
+ lasterror = usecerror / (i+1);
+ }
+ else
+ {
+ lasterror = 0; /* we cannot calculate phase errors on bad reception */
+ }
+
+ PRINTF("\r");
+
+ if (i < 60)
+ {
+ i++;
+ }
+
+ tlast = tt;
+
+ if (interactive)
+ fflush(stdout);
+ }
+ } while ((rrc == -1) && (errno == EINTR));
+
+ /*
+ * lost IO - sorry guys
+ */
+ syslog(LOG_ERR, "TERMINATING - cannot read from device %s (%m)", file);
+
+ (void)close(fd);
+ }
+
+ closelog();
+
+ return 0;
+}
diff --git a/usr.sbin/xntpd/parse/util/parsetest.c b/usr.sbin/xntpd/parse/util/parsetest.c
new file mode 100644
index 0000000..33f3d9c
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/parsetest.c
@@ -0,0 +1,275 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/parsetest.c,v 3.14 1994/05/12 12:49:27 kardel Exp
+ *
+ * parsetest.c,v 3.14 1994/05/12 12:49:27 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Log: parsetest.c,v $
+ * Revision 1.1.1.4 1994/09/29 23:01:34 wollman
+ * xntp 3.4e from Dave Mills @ UDel
+ *
+ * Revision 3.14 1994/05/12 12:49:27 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.14 1994/05/11 09:25:43 kardel
+ * 3.3r + printf fmt/arg fixes
+ *
+ * Revision 3.13 1994/02/20 13:04:46 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.12 1994/02/02 17:45:51 kardel
+ * rcs ids fixed
+ *
+ */
+
+#ifndef STREAM
+ONLY STREAM OPERATION SUPPORTED
+#endif
+
+#define PARSESTREAM /* there is no other choice - TEST HACK */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+
+#define P(X) ()
+
+#include "ntp_fp.h"
+#ifdef USE_PROTOTYPES
+#include "ntp_stdlib.h"
+#endif
+#include "parse.h"
+
+static char *strstatus(buffer, state)
+ char *buffer;
+ unsigned LONG state;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADDITION WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETION WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ unsigned LONG bit;
+ char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ register char *s, *t;
+
+ if (buffer[0])
+ strcat(buffer, "; ");
+
+ strcat(buffer, "(");
+
+ t = s = buffer + strlen(buffer);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & state)
+ {
+ if (t != s)
+ {
+ strcpy(t, "; ");
+ t += 2;
+ }
+
+ strcpy(t, sflagstrings[i].name);
+ t += strlen(t);
+ }
+ i++;
+ }
+ strcpy(t, ")");
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *parsestatus(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc != 2)
+ {
+ fprintf(stderr,"usage: %s <parse-device>\n", argv[0]);
+ exit(1);
+ }
+ else
+ {
+ int fd;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd == -1)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+ else
+ {
+ parsectl_t dct;
+ parsetime_t parsetime;
+ struct strioctl strioc;
+
+ printf("parsetest.c,v 3.11 1994/01/23 19:00:01 kardel Exp\n");
+
+ while (ioctl(fd, I_POP, 0) == 0)
+ ;
+
+ if (ioctl(fd, I_PUSH, "parse") == -1)
+ {
+ perror("ioctl(I_PUSH,\"parse\")");
+ exit(1);
+ }
+
+ strioc.ic_cmd = PARSEIOC_GETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)&dct;
+ strioc.ic_len = sizeof(parsectl_t);
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))");
+ exit(1);
+ }
+ printf("parse status: %04lx\n", (unsigned long)dct.parsestatus.flags);
+
+ dct.parsestatus.flags |= PARSE_STAT_FILTER;
+ strioc.ic_cmd = PARSEIOC_SETSTAT;
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_SETSTAT))");
+ exit(1);
+ }
+ printf("PARSE clock FILTERMODE\n");
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))");
+ exit(1);
+ }
+ printf("parse status: %04lx\n", (unsigned long)dct.parsestatus.flags);
+
+ while (read(fd, &parsetime, sizeof(parsetime)) == sizeof(parsetime))
+ {
+ char tmp[200], tmp1[200], tmp2[60];
+
+ strncpy(tmp, asctime(localtime(&parsetime.parse_time.tv.tv_sec)), 30);
+ strncpy(tmp1,asctime(localtime(&parsetime.parse_stime.tv.tv_sec)), 30);
+ strncpy(tmp2,asctime(localtime(&parsetime.parse_ptime.tv.tv_sec)), 30);
+ tmp[24] = '\0';
+ tmp1[24] = '\0';
+ tmp2[24] = '\0';
+
+ printf("%s (+%06ldus) %s PPS: %s (+%06ldus), ", tmp1, (long int)parsetime.parse_stime.tv.tv_usec, tmp, tmp2,
+ (long int)parsetime.parse_ptime.tv.tv_usec);
+
+ strstatus(tmp, parsetime.parse_state);
+ printf("state: 0x%lx (%s) error: %ldus, dispersion: %ldus, Status: 0x%lx (%s)\n",
+ (unsigned long)parsetime.parse_state,
+ tmp,
+ (long)parsetime.parse_usecerror,
+ (long)parsetime.parse_usecdisp,
+ (unsigned long)parsetime.parse_status,
+ parsestatus(parsetime.parse_status, tmp1));
+ }
+
+ close(fd);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/xntpd/parse/util/testdcf.c b/usr.sbin/xntpd/parse/util/testdcf.c
new file mode 100644
index 0000000..bd5ea88
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/testdcf.c
@@ -0,0 +1,488 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/testdcf.c,v 3.13 1994/05/12 12:49:31 kardel Exp
+ *
+ * testdcf.c,v 3.13 1994/05/12 12:49:31 kardel Exp
+ *
+ * simple DCF77 100/200ms pulse test program (via 50Baud serial line)
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * This program may not be sold or used for profit without prior
+ * written consent of the author.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/types.h>
+#ifdef STREAM
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#endif
+#include <sys/time.h>
+
+#include "ntp_stdlib.h"
+
+/*
+ * state flags
+ */
+#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */
+#define DCFB_DST 0x0002 /* DST in effect */
+#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */
+#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */
+
+struct clocktime /* clock time broken up from time code */
+{
+ long wday;
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in minutes */
+ long flags; /* current clock status */
+};
+
+typedef struct clocktime clocktime_t;
+
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1))
+
+/*
+ * parser related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+static unsigned long ext_bf(buf, idx)
+ register char *buf;
+ register int idx;
+{
+ register unsigned long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != '-');
+ }
+ return sum;
+}
+
+static unsigned pcheck(buf, idx)
+ register char *buf;
+ register int idx;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != '-');
+
+ return psum;
+}
+
+static unsigned long convert_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ if (size < 57)
+ {
+ printf("%-30s", "*** INCOMPLETE");
+ return CVT_NONE;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S) == 1) &&
+ pcheck(buffer, DCF_P_P1) &&
+ pcheck(buffer, DCF_P_P2) &&
+ pcheck(buffer, DCF_P_P3))
+ {
+ /*
+ * buffer OK
+ */
+
+ clock->flags = 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1);
+ clock->hour = ext_bf(buffer, DCF_H10);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1);
+ clock->day = ext_bf(buffer, DCF_D10);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1);
+ clock->month = ext_bf(buffer, DCF_MO0);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO);
+ clock->year = ext_bf(buffer, DCF_Y10);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1);
+ clock->wday = ext_bf(buffer, DCF_DW);
+
+ switch (ext_bf(buffer, DCF_Z))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= DCFB_DST;
+ clock->utcoffset = -120;
+ break;
+
+ default:
+ printf("%-30s", "*** BAD TIME ZONE");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (ext_bf(buffer, DCF_A1))
+ clock->flags |= DCFB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2))
+ clock->flags |= DCFB_LEAP;
+
+ if (ext_bf(buffer, DCF_R))
+ clock->flags |= DCFB_ALTERNATE;
+
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+ printf("%-30s", "*** BAD FORMAT (invalid/parity)");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+char type(c)
+unsigned char c;
+{
+ c ^= 0xFF;
+ return (c > 0xF);
+}
+
+static char *wday[8] =
+{
+ "??",
+ "Mo",
+ "Tu",
+ "We",
+ "Th",
+ "Fr",
+ "Sa",
+ "Su"
+};
+
+static char pat[] = "-\\|/";
+
+#define LINES (24-2) /* error lines after which the two headlines are repeated */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if ((argc != 2) && (argc != 3))
+ {
+ fprintf(stderr, "usage: %s [-f|-t|-ft|-tf] <device>\n", argv[0]);
+ exit(1);
+ }
+ else
+ {
+ unsigned char c;
+ char *file;
+ int fd;
+ int offset = 15;
+ int trace = 0;
+ int errs = LINES+1;
+
+ /*
+ * SIMPLE(!) argument "parser"
+ */
+ if (argc == 3)
+ {
+ if (strcmp(argv[1], "-f") == 0)
+ offset = 0;
+ if (strcmp(argv[1], "-t") == 0)
+ trace = 1;
+ if ((strcmp(argv[1], "-ft") == 0) ||
+ (strcmp(argv[1], "-tf") == 0))
+ {
+ offset = 0;
+ trace = 1;
+ }
+ file = argv[2];
+ }
+ else
+ {
+ file = argv[1];
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ {
+ perror(file);
+ exit(1);
+ }
+ else
+ {
+ int i;
+#ifdef TIOCM_RTS
+ int on = TIOCM_RTS;
+#endif
+ struct timeval t, tt, tlast;
+ char buf[61];
+ clocktime_t clock;
+ struct termios term;
+ int rtc = CVT_NONE;
+
+ if (tcgetattr(fd, &term) == -1)
+ {
+ perror("tcgetattr");
+ exit(1);
+ }
+
+ memset(term.c_cc, 0, sizeof(term.c_cc));
+ term.c_cc[VMIN] = 1;
+ term.c_cflag = B50|CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = IGNPAR;
+ term.c_oflag = 0;
+ term.c_lflag = 0;
+
+ if (tcsetattr(fd, TCSANOW, &term) == -1)
+ {
+ perror("tcsetattr");
+ exit(1);
+ }
+
+#ifdef I_POP
+ while (ioctl(fd, I_POP, 0) == 0)
+ ;
+#endif
+#if defined(TIOCMBIC) && defined(TIOCM_RTS)
+ if (ioctl(fd, TIOCMBIC, (caddr_t)&on) == -1)
+ {
+ perror("TIOCM_RTS");
+ }
+#endif
+
+ printf(" DCF77 monitor - Copyright 1993, Frank Kardel\n\n");
+
+ clock.hour = 0;
+ clock.minute = 0;
+ clock.day = 0;
+ clock.wday = 0;
+ clock.month = 0;
+ clock.year = 0;
+ clock.flags = 0;
+ buf[60] = '\0';
+ for ( i = 0; i < 60; i++)
+ buf[i] = '.';
+
+ gettimeofday(&tlast, 0L);
+ i = 0;
+ while (read(fd, &c, 1) == 1)
+ {
+ gettimeofday(&t, 0L);
+ tt = t;
+ t.tv_sec -= tlast.tv_sec;
+ t.tv_usec -= tlast.tv_usec;
+ if (t.tv_usec < 0)
+ {
+ t.tv_usec += 1000000;
+ t.tv_sec -= 1;
+ }
+
+ if (errs > LINES)
+ {
+ printf(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]);
+ printf(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]);
+ errs = 0;
+ }
+
+ if (t.tv_sec > 1 ||
+ (t.tv_sec == 1 &&
+ t.tv_usec > 500000))
+ {
+ printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]);
+
+ if ((rtc = convert_rawdcf(buf, i, &clock)) != CVT_OK)
+ {
+ printf("\n");
+ clock.hour = 0;
+ clock.minute = 0;
+ clock.day = 0;
+ clock.wday = 0;
+ clock.month = 0;
+ clock.year = 0;
+ clock.flags = 0;
+ errs++;
+ }
+
+ if (((c^0xFF)+1) & (c^0xFF))
+ buf[0] = '?';
+ else
+ buf[0] = type(c) ? '#' : '-';
+
+ for ( i = 1; i < 60; i++)
+ buf[i] = '.';
+
+ i = 0;
+ }
+ else
+ {
+ if (((c^0xFF)+1) & (c^0xFF))
+ buf[i] = '?';
+ else
+ buf[i] = type(c) ? '#' : '-';
+
+ printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]);
+ }
+
+ if (rtc == CVT_OK)
+ {
+ /* We are only supplied two digits of the year, by the
+ * clock, so we'll just print two digits.
+ */
+ printf("%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>",
+ wday[clock.wday],
+ (int)clock.hour, (int)clock.minute, (int)i, (int)clock.day, (int)clock.month,
+ (int)clock.year,
+ (clock.flags & DCFB_ALTERNATE) ? "R" : "_",
+ (clock.flags & DCFB_ANNOUNCE) ? "A" : "_",
+ (clock.flags & DCFB_DST) ? "D" : "_",
+ (clock.flags & DCFB_LEAP) ? "L" : "_"
+ );
+ if (trace && (i == 0))
+ {
+ printf("\n");
+ errs++;
+ }
+ }
+
+ printf("\r");
+
+ if (i < 60)
+ {
+ i++;
+ }
+
+ tlast = tt;
+
+ fflush(stdout);
+ }
+ close(fd);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/xntpd/refclocks/Dependencies b/usr.sbin/xntpd/refclocks/Dependencies
new file mode 100644
index 0000000..2829476
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/Dependencies
@@ -0,0 +1,30 @@
+Clock Defines Restrictions AddDef Kernel
+PPS PPS
+ PPSPPS /STREAM/ STREAM(ppsclock|ppsclocd)
+ PPSCLK /LD/||/STREAM/ LD||STREAM(tty_clock||tty_clk_streams)
+ PPSCD /STREAM/ STREAM(ppsclock|ppsclocd)
+LOCAL LOCAL_CLOCK
+PST PST
+ PSTCLK /LD/||/STREAM/ LD||STREAM(tty_clock||tty_clk_streams)
+ PSTPPS /PPSPPS/
+WWVB WWVB
+ WWVBCLK /LD/||/STREAM/ PPSPPS LD||STREAM(tty_clock||tty_clk_streams)
+ WWVBPPS /PPSPPS/
+CHU CHU /SUNOS4/ none
+PARSE PARSE /SYSV_TTYS/||/STREAM/||/TERMIOS/ PPS any||STREAM(parse||ppsclock||ppsclocd)
+ PARSEPPS /SYSV_TTYS/||/STREAM/||/TERMIOS/ PPS any||STREAM(parse||ppsclock||ppsclocd)
+ CLOCK_*
+MX4200 MX4200PPS /PPSPPS/
+AS2201 AS2201
+ AS2201PPS /PPSPPS/
+GOES GOES
+ GOESPPS /PPSPPS/
+GPSTM GPSTM
+ GPSTTMPPS /PPSPPS/
+OMEGA OMEGA
+ OMEGAPPS /PPSPPS/
+TPRO TPRO /SUNOS/
+LEITCH LEITCH
+ LEITCHPPS /PPSPPS/
+MSFEES MSFEESPPS /PPSPPS/
+IRIG IRIG /SUNOS/ BSDAUDIO
diff --git a/usr.sbin/xntpd/refclocks/README b/usr.sbin/xntpd/refclocks/README
new file mode 100644
index 0000000..b27b006
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/README
@@ -0,0 +1,4 @@
+This directory contains shell scripts that should allow an interactive
+refclock configuration.
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/refclocks/check b/usr.sbin/xntpd/refclocks/check
new file mode 100755
index 0000000..d1e8b19
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/check
@@ -0,0 +1,2 @@
+#!/bin/sh
+`echo "$1" | awk '{ if ( '"$2"' ) { print ":"; } else { print "false"; } exit; }'`;
diff --git a/usr.sbin/xntpd/refclocks/echon b/usr.sbin/xntpd/refclocks/echon
new file mode 100755
index 0000000..8750ae8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/echon
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo -n "$@";
diff --git a/usr.sbin/xntpd/refclocks/query b/usr.sbin/xntpd/refclocks/query
new file mode 100755
index 0000000..07e11cc
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/query
@@ -0,0 +1,11 @@
+#!/bin/sh
+echon "$1 (y/n) [$2] ? "
+X=""
+read X
+if [ "$X" = "" ]; then
+ X="$2"
+fi
+case "$X" in
+ [yY]*) exit 0;;
+ *) exit 1;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.AS2201 b/usr.sbin/xntpd/refclocks/rclk.AS2201
new file mode 100644
index 0000000..9af89af
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.AS2201
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " AS2201 - Austron 2200A or 2201A GPS receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /AS2201/'; then
+ echo "AS2201 - Austron 2200A or 2201A GPS receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /AS2201/' ||
+ ( [ ! "$REFCONF" ] && query "Include Austron 2200A or 2201A GPS receiver (AS2201)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /AS2201PPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use AS2201 for PPS" n)); then
+ echo "-DAS2201PPS" >> $RCONFIG
+ else
+ echo "-DAS2201" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.CHU b/usr.sbin/xntpd/refclocks/rclk.CHU
new file mode 100644
index 0000000..fedd55c
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.CHU
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS4/'; then
+ case "$CMD" in
+ info)
+ echo " CHU - CHU via shortwave radio"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /CHU/'; then
+ echo "CHU - CHU via shortwave radio"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /CHU/' ||
+ ( [ ! "$REFCONF" ] && query "Include CHU via shortwave radio (CHU)" n); then
+ echo "-DCHU" >> $RCONFIG
+ fi
+ ;;
+esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.DATUM b/usr.sbin/xntpd/refclocks/rclk.DATUM
new file mode 100644
index 0000000..3639e4a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.DATUM
@@ -0,0 +1,22 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " DATUM - use Datum Programmable Time System"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /DATUM/'; then
+ echo "DATUM - Datum Programmable Time System"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /DATUM/' ||
+ ( [ ! "$REFCONF" ] && query "Include DATUM reference support (DATUM)" n); then
+ echo "-DDATUM" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.GOES b/usr.sbin/xntpd/refclocks/rclk.GOES
new file mode 100644
index 0000000..cb87c63
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.GOES
@@ -0,0 +1,32 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " GOES - Kinemetrics/TrueTime 468-DC GOES receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /GOES/'; then
+ echo "GOES - Kinemetrics/TrueTime 468-DC GOES receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /GOES/' ||
+ ( [ ! "$REFCONF" ] && query "Include Kinemetrics/TrueTime 468-DC GOES receiver (GOES)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /GOESPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use GOES for PPS" n)); then
+ echo "-DGOESPPS" >> $RCONFIG
+ else
+ echo "-DGOES" >> $RCONFIG
+ fi
+ else
+ echo "-DGOES" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.GPSTM b/usr.sbin/xntpd/refclocks/rclk.GPSTM
new file mode 100644
index 0000000..552747a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.GPSTM
@@ -0,0 +1,33 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " GPSTM - Kinemetrics/TrueTime GPS-TM/TMD receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /GPSTM/'; then
+ echo "GPSTM - Kinemetrics/TrueTime GPS-TM/TMD receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /GPSTM/' ||
+ ( [ ! "$REFCONF" ] &&
+ query "Include Kinemetrics/TrueTime GPS-TM/TMD receiver (GPSTM)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /GPSTMPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use GPSTM for PPS" n)); then
+ echo "-DGPSTMPPS" >> $RCONFIG
+ else
+ echo "-DGPSTM" >> $RCONFIG
+ fi
+ else
+ echo "-DGPSTM" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.IRIG b/usr.sbin/xntpd/refclocks/rclk.IRIG
new file mode 100644
index 0000000..b480e90
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.IRIG
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS4/'; then
+ case "$CMD" in
+ info)
+ echo " IRIG - IRIG-B timecode timecode using the audio codec"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /IRIG/'; then
+ echo "IRIG - IRIG-B timecode timecode using the audio codec"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /IRIG/' ||
+ ( [ ! "$REFCONF" ] && query "Include IRIG-B timecode timecode using the audio codec (IRIG)" n); then
+ echo "-DIRIG" >> $RCONFIG
+ fi
+ ;;
+ esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.LEITCH b/usr.sbin/xntpd/refclocks/rclk.LEITCH
new file mode 100644
index 0000000..1bd5fb8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.LEITCH
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " LEITCH - Leitch CSD 5300 Master Clock System Driver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /LEITCH/'; then
+ echo "LEITCH - Leitch CSD 5300 Master Clock System Driver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /LEITCH/' ||
+ ( [ ! "$REFCONF" ] && query "Include Leitch CSD 5300 Master Clock System Driver (LEITCH)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /LEITCHPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use LEITCH for PPS" n)); then
+ echo "-DLEITCHPPS" >> $RCONFIG
+ else
+ echo "-DLEITCH" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK b/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK
new file mode 100644
index 0000000..e2f9e1a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK
@@ -0,0 +1,22 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " LOCAL_CLOCK - use local clock as reference"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /LOCAL_CLOCK/'; then
+ echo "LOCAL_CLOCK - local clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /LOCAL_CLOCK/' ||
+ ( [ ! "$REFCONF" ] && query "Include local clock reference support (LOCAL_CLOCK)" y); then
+ echo "-DLOCAL_CLOCK" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MOTO b/usr.sbin/xntpd/refclocks/rclk.MOTO
new file mode 100644
index 0000000..d6e11f8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MOTO
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " MOTO - Motorola GPS clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MOTO/'; then
+ echo "MOTO - Motorola GPS clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /MOTO/' ||
+ ( [ ! "$REFCONF" ] && query "Include Motorola clock (MOTO)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MOTOPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use Motorola for PPS" n)); then
+ echo "-DMOTOPPS" >> $RCONFIG
+ else
+ echo "-DMOTO" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MSFEES b/usr.sbin/xntpd/refclocks/rclk.MSFEES
new file mode 100644
index 0000000..aeb988a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MSFEES
@@ -0,0 +1,26 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ if check "$DEFS" '$0 ~ /STREAM/'; then
+ echo " MSFEESPPS - EES M201 MSF receiver"
+ fi
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MSFEESPPS/'; then
+ echo "MSFEESPPS - EES M201 MSF receiver"
+ fi
+ ;;
+ config)
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MSFEESPPS/' ||
+ ( [ ! "$REFCONF" ] && query "Include EES M201 MSF receiver (MSFEESPPS)" n)); then
+ echo "-DMSFEESPPS" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MX4200 b/usr.sbin/xntpd/refclocks/rclk.MX4200
new file mode 100644
index 0000000..5d10712
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MX4200
@@ -0,0 +1,27 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ if check "$DEFS" '$0 ~ /STREAM/'; then
+ echo " MX4200 - Magnavox 4200 GPS receiver"
+ fi
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MX4200PPS/'; then
+ echo "MX4200 - Magnavox 4200 GPS receiver"
+ fi
+ ;;
+ config)
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MX4200PPS/' ||
+ ( [ ! "$REFCONF" ] && query "Include Magnavox 4200 GPS receiver (MX4200PPS)" n)); then
+ echo "-DMX4200PPS" >> $RCONFIG
+ fi
+ ;;
+esac
+
diff --git a/usr.sbin/xntpd/refclocks/rclk.NMEA b/usr.sbin/xntpd/refclocks/rclk.NMEA
new file mode 100644
index 0000000..f505465
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.NMEA
@@ -0,0 +1,23 @@
+case "$CMD" in
+ info)
+ echo " NMEA - NMEA GPS station clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /NMEA/'; then
+ echo "NMEA - NMEA GPS station clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /NMEA/' ||
+ ( [ ! "$REFCONF" ] && query "Include NMEA GPS station clock (NMEA)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /NMEAPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use NMEA for PPS" n)); then
+ echo "-DNMEAPPS" >> $RCONFIG
+ else
+ echo "-DNMEA" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.OMEGA b/usr.sbin/xntpd/refclocks/rclk.OMEGA
new file mode 100644
index 0000000..62094e4
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.OMEGA
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " OMEGA - Kinemetrics/TrueTime OM-DC OMEGA receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /OMEGA/'; then
+ echo "OMEGA - Kinemetrics/TrueTime OM-DC OMEGA receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /OMEGA/' ||
+ ( [ ! "$REFCONF" ] && query "Include Kinemetrics/TrueTime OM-DC OMEGA receiver (OMEGA)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /OMEGAPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use OMEGA for PPS" n)); then
+ echo "-DOMEGAPPS" >> $RCONFIG
+ else
+ echo "-DOMEGA" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.PARSE b/usr.sbin/xntpd/refclocks/rclk.PARSE
new file mode 100644
index 0000000..5211ccc
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.PARSE
@@ -0,0 +1,55 @@
+#!/bin/sh
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /HAVE_SYSV_TTYS|STREAM|HAVE_TERMIOS/'; then
+ case "$CMD" in
+ info)
+ echo " PARSE - GENERIC refence clock driver"
+ echo " DCF77:"
+ echo " Meinberg DCF U/A 31, PZF 535"
+ echo " Schmid DCF77 receiver"
+ echo " ELV DCF7000"
+ echo " Raw DCF77 signal (100/200ms pulses)"
+ echo " GPS:"
+ echo " Meinberg GPS 166"
+ echo " Trimble GPS (TAIP Protocol)"
+ echo " Trimble GPS (TSIP Protocol)"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /PARSE/ && $0 ~ /CLOCK/'; then
+ echon "PARSE - GENERIC"
+ if check "$RCONFIG" '$0 ~ /PARSEPPS/'; then echon "/PPS"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_MEINBERG/'; then echon ",MEINBERG"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_SCHMID/'; then echon ",SCHMID"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_DCF7000/'; then echon ",DCF7000"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_TRIMTAIP/'; then echon ",Trimble GPS (TAIP)"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_TRIMTSIP/'; then echon ",Trimble GPS (TSIP)"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_RAWDCF/'; then echon ",Raw DCF77 pulses"; fi
+ echo
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /PARSE/' ||
+ ( [ ! "$REFCONF" ] && query "Include PARSE generic driver support" n); then
+ if check "$REFCONF" '$0 ~ /PARSEPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for PPS simulation" y); then echo "-DPARSEPPS" >> $RCONFIG; else echo "-DPARSE" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /MEINBERG/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Meinberg clocks" y); then echo "-DCLOCK_MEINBERG" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /SCHMID/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Schmid DCF77 clock" y); then echo "-DCLOCK_SCHMID" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /DCF7000|ELV/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for ELV/DCF7000 clock" y); then echo "-DCLOCK_DCF7000" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /TRIMTAIP/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Trimble GPS receiver (TAIP Protocol)" y); then echo "-DCLOCK_TRIMTAIP" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /TRIMTSIP/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Trimble GPS receiver (TSIP Protocol)" y); then echo "-DCLOCK_TRIMTSIP" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /RAWDCF/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for raw DCF77 time code" y); then echo "-DCLOCK_RAWDCF" >> $RCONFIG; fi
+ fi
+ ;;
+ esac
+fi
+
diff --git a/usr.sbin/xntpd/refclocks/rclk.PST b/usr.sbin/xntpd/refclocks/rclk.PST
new file mode 100644
index 0000000..4f93c8e
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.PST
@@ -0,0 +1,37 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " PST - PST/Traconex 1020 WWV/H receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /PST/'; then
+ echo "PST - PST/Traconex 1020 WWV/H receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /PST/' ||
+ ( [ ! "$REFCONF" ] && query "Include PST/Traconex 1020 WWV/H receiver (PST)" n); then
+ _PST=0
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ if (check "$REFCONF" '$0 ~ /PSTCLK/' ||
+ ( [ ! "$REFCONF" ] && query " Support PPS via clk" y)); then
+ echo " -DPSTCLK" >> $RCONFIG
+ _PST=1
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /PSTPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use PST for PPS" n)); then
+ echo " -DPSTPPS" >> $RCONFIG
+ else
+ [ "$_PST" -eq 0 ] && echo "-DPST" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.TPRO b/usr.sbin/xntpd/refclocks/rclk.TPRO
new file mode 100644
index 0000000..74e5545
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.TPRO
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS/'; then
+ case "$CMD" in
+ info)
+ echo " TPRO - KSI/Odetics TPRO-S IRIG-B timecode reader"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /TPRO/'; then
+ echo "TPRO - KSI/Odetics TPRO-S IRIG-B timecode reader"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /TPRO/' ||
+ ( [ ! "$REFCONF" ] && query "Include KSI/Odetics TPRO-S IRIG-B timecode reader (TPRO)" n); then
+ echo "-DTPRO" >> $RCONFIG
+ fi
+ ;;
+ esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.TRAK b/usr.sbin/xntpd/refclocks/rclk.TRAK
new file mode 100644
index 0000000..188ffd4
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.TRAK
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " TRAK - TRAK 8810 GPS station clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /TRAK/'; then
+ echo "TRAK - TRAK 8810 GPS station clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /TRAK/' ||
+ ( [ ! "$REFCONF" ] && query "Include TRAK 8810 GPS station clock (TRAK)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /TRAKPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use TRAK for PPS" n)); then
+ echo "-DTRAKPPS" >> $RCONFIG
+ else
+ echo "-DTRAK" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.WWVB b/usr.sbin/xntpd/refclocks/rclk.WWVB
new file mode 100644
index 0000000..2801ac7
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.WWVB
@@ -0,0 +1,38 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " WWVB - Spectracom 8170 or Netclock/2 WWVB receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /WWVB/'; then
+ echo "WWVB - Spectracom 8170 or Netclock/2 WWVB receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /WWVB/' ||
+ ( [ ! "$REFCONF" ] && query "Include Spectracom 8170 or Netclock/2 WWVB receiver (WWVB)" n); then
+ _WWV=0
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ if check "$REFCONF" '$0 ~ /WWVBCLK/' ||
+ ( [ ! "$REFCONF" ] && query " Support PPS via clk" y); then
+ echo " -DWWVBCLK" >> $RCONFIG
+ _WWV=1
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /WWVBPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use WWVB for PPS" n)); then
+ echo " -DWWVBPPS" >> $RCONFIG
+ fi
+ else
+ [ "$_WWV" -eq 0 ] && echo "-DWWVB" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rconfig b/usr.sbin/xntpd/refclocks/rconfig
new file mode 100644
index 0000000..e49c559
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rconfig
@@ -0,0 +1,130 @@
+#!/bin/sh -
+#
+# Refclock configuration script
+#
+# batch configuration options (optional arg 1)
+# pps related
+# PPS - general PPS support
+# CLK - CLK line discipline or streams module
+# CD - ppsclock or ppsclockd streams module
+# LINE - dedicated line
+PATH=refclocks:${PATH}
+export PATH
+RCONFIG=rconf
+DLOCAL=dlocal
+REFCONF=${1-""}
+
+. refclocks/setup
+
+rcfg="`echo refclocks/rclk.*`"
+
+if [ "$rcfg" = "refclocks/rclk.*" ]; then
+ echo "no reference clock configuration information available"
+else
+ config="`egrep '^[ ]*CLOCKDEFS[ ]*=' Config.local | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*CLOCKDEFS[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ DEFS="`egrep '^[ ]*DEFS[ ]*=' Config | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*DEFS[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ DEFSLOCAL="`egrep '^[ ]*DEFS_LOCAL[ ]*=' Config.local | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*DEFS_LOCAL[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo "Current configuration"
+ echo
+ if check "$DEFSLOCAL" '$0 ~ /MCAST/'; then
+ echo "==> MULTICAST SUPPORT (if available)"
+ echo
+ fi
+ for i in $rcfg
+ do
+ sh $i check "$config" "" "" "$DEFS" "$REFCONF"
+ done
+ echo
+ fi
+ if [ "$REFCONF" ] || query "Change Configuration" n; then
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo 'Available reference clock drivers'
+ for i in $rcfg
+ do
+ sh $i info "" "" "" "$DEFS" "$REFCONF"
+ done
+ echo
+ fi
+ :>"$RCONFIG"
+ PPS=""
+ PPSFEATURES=""
+ PPSOK=0
+ if check "$REFCONF" '$0 ~ /PLL/' ||
+ ( [ ! "$REFCONF" ] && query "Include support for Kernel PLL" n); then
+ PPS="-DKERNEL_PLL $PPS"
+ fi
+ if check "$REFCONF" '$0 ~ /[^A-Za-z]PPS/' ||
+ ( [ ! "$REFCONF" ] && query "Do you have a PPS (pulse per second) signal" n); then
+ if check "$DEFS" '$0 ~ /HAVE_BSD_TTYS|STREAM/' &&
+ (check "$REFCONF" '$0 ~ /CLK/' ||
+ ( [ ! "$REFCONF" ] && query "Is the clk line discipline available" n)); then
+ PPSFEATURES="CLK"
+ else
+ if check "$DEFS" '$0 ~ /STREAM/' &&
+ (check "$REFCONF" '$0 ~ /CD/' ||
+ ( [ ! "$REFCONF" ] && query "Is the ppsclock or ppsclocd STREAMS module available" n)); then
+ PPSFEATURES="CD $PPSFEATURES"
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CLK|CD/' &&
+ (check "$REFCONF" '$0 ~ /LINE/' ||
+ ( [ ! "$REFCONF" ] && query "Do you want to use a dedicated serial port for PPS signal" n)); then
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ PPS="-DPPSCLK $PPS"
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ PPS="-DPPSCD $PPS"
+ fi
+ else
+ PPSOK=1
+ PPS="-DPPS $PPS"
+ fi
+ fi
+ if (check "$REFCONF" '$0 ~ /MCAST/' ||
+ ( [ ! "$REFCONF" ] && query "Do you want allow for multicast support (if available) ?" y)); then
+ MCAST="-DMCAST"
+ else
+ MCAST=""
+ fi
+ for i in $rcfg
+ do
+ sh $i config "$RCONFIG" "$PPSFEATURES" "$PPSOK" "$DEFS" "$REFCONF"
+ if [ "$PPSOK" -eq 1 ] && egrep -e '-D..*PPS' "$RCONFIG" >/dev/null 2>&1; then
+ PPSOK=0
+ fi
+ done
+ if egrep -e '-D..*PPS' "$RCONFIG" >/dev/null 2>&1; then
+ PPS="-DPPSPPS $PPS"
+ fi
+ CLOCKDEFS="`tr '\012' ' ' < $RCONFIG`"
+ if check "$CLOCKDEFS" '$0 !~ /^[ ]*$/'; then
+ PPS="-DREFCLOCK $PPS"
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo "Do not forget to set up the appropriate device links in the /dev directory"
+ echo
+ fi
+ fi
+ if sed -e 's/^[ ]*CLOCKDEFS[ ]*=.*$/CLOCKDEFS='"$CLOCKDEFS"'/;' \
+ -e 's/^[ ]*DEFS_LOCAL[ ]*=.*$/DEFS_LOCAL= $(DEFS_OPT) '"$PPS $MCAST"'/;' \
+ Config.local > Config.local.new; then
+ mv Config.local Config.local.old &&
+ mv Config.local.new Config.local &&
+ rm -f Config.local.old
+ echo
+ echo "New configuration defines:"
+ echo " CLOCKDEFS=$CLOCKDEFS"
+ echo " DEFS_LOCAL="'$(DEFS_OPT)'" $PPS $MCAST"
+ echo
+ echo "Configuration updated"
+ else
+ echo "Configuration update FAILED"
+ fi
+ rm -f "$RCONFIG"
+ else
+ :;
+ fi
+fi
diff --git a/usr.sbin/xntpd/refclocks/setup b/usr.sbin/xntpd/refclocks/setup
new file mode 100644
index 0000000..4611178
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/setup
@@ -0,0 +1,16 @@
+#
+# gobble possible parameters
+#
+if [ $# -eq 5 ]; then
+ RCONFIG="$1"
+ PPSFEATURES="$2"
+ PPSOK="$3"
+ DEFS="$4"
+ REFCONF="$5"
+fi
+#
+# shell dumbness detection
+#
+if (eval "_x () { :; }") >/dev/null 2>&1 ; then
+ . refclocks/setupfn
+fi
diff --git a/usr.sbin/xntpd/refclocks/setupfn b/usr.sbin/xntpd/refclocks/setupfn
new file mode 100644
index 0000000..5724dcb
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/setupfn
@@ -0,0 +1,27 @@
+#
+# sh io functions
+#
+if [ "`echo -n`" = "-n" ]; then
+ echon () { echo "$@\\c"; }
+else
+ echon () { echo -n "$@"; }
+fi
+
+query() {
+ _Q="$1"
+ _A="$2"
+ echon "$_Q (y/n) [$_A] ? "
+ X=""
+ read X
+ if [ "$X" = "" ]; then
+ X="$_A"
+ fi
+ case "$X" in
+ [yY]*) return 0;;
+ *) return 1;;
+ esac
+}
+
+check () {
+ `echo "$1" | awk '{ if ( '"$2"' ) { print ":"; } else { print "false"; } exit; }'`;
+}
diff --git a/usr.sbin/xntpd/scripts/Guess.sh b/usr.sbin/xntpd/scripts/Guess.sh
new file mode 100755
index 0000000..9b3180b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/Guess.sh
@@ -0,0 +1,130 @@
+#! /bin/sh
+
+if [ -f /bin/uname -o -f /usr/bin/uname ]; then
+ set `uname -a | tr '[A-Z]' '[a-z]'`
+# set `cat test | tr '[A-Z]' '[a-z]'`
+ case "$1" in
+ convexos) case "$4" in
+ 10.*) guess="convexos10" ;;
+ esac
+ ;;
+ aix) case "$4" in
+ 3) case "$3" in
+ 1) guess="aix3.1" ;;
+ 2) guess="aix3.2" ;;
+ esac
+ ;;
+ esac
+ ;;
+ sinix-m)
+ guess=sinix-m
+ ;;
+ sunos|solaris)
+ case "$3" in
+ 4.1*) guess="sunos4" ;;
+ 5.1) guess="sunos5.1" ;;
+ 5.2) guess="sunos5.2" ;;
+ 5.*) guess="sunos5.3" ;;
+ esac
+ ;;
+ irix) case "$3" in
+ 4.*) guess="irix4" ;;
+ 5.*) guess="irix5" ;;
+ esac
+ ;;
+ "a/ux") case "$3" in
+ 2.*) guess="aux2" ;;
+ 3.*) guess="aux3" ;;
+ esac
+ ;;
+ ultrix)
+ guess="ultrix"
+ ;;
+ hp-ux) case "$3" in
+ *.10.*) guess="hpux-adj" ;;
+ *.09.03) case "$5" in
+ 9000/3*) guess="hpux-adj" ;;
+ *) guess="hpux" ;;
+ esac ;;
+ *) guess="hpux" ;;
+ esac
+ ;;
+ linux) guess="linux" ;;
+
+ osf1) case "$5" in
+ alpha) guess="decosf1" ;;
+ esac
+ ;;
+ "bsd/386")
+ guess="bsdi"
+ ;;
+ "freebsd")
+ guess="freebsd"
+ ;;
+ "netbsd")
+ guess="netbsd"
+ ;;
+ "4.4bsd")
+ guess="4.4bsd"
+ ;;
+ # now the fun starts - there are vendors that
+ # do not really identify their OS in uname.
+ # Fine - now I look at our version and hope
+ # that nobody else had this marvellous idea.
+ # I am not willing to mention the vendor explicitly
+ *) # Great ! - We are dealing with an industry standard !
+ if [ -f /unix ]; then
+ #
+ # looks like this thing has the license
+ # to call itself Unix
+ #
+ case "$3" in
+ 3.2.*)
+ case "$4" in
+ v*)
+ (i386) >/dev/null 2>&1 && [ -f /usr/lib/libseq.a ] && guess=ptx;;
+ esac
+ esac
+ fi
+ ;;
+ esac
+fi
+
+if [ "0$guess" != "0" ]; then
+ echo $guess
+ exit 0
+fi
+
+if [ -f /bin/machine ]; then
+ echo `/bin/machine`
+ exit 0
+fi
+
+if [ -f /usr/convex/vers ]; then
+ set `/usr/convex/vers /vmunix`
+ case "$2" in
+ 9.0) echo "convexos9"
+ exit 0 ;;
+ esac
+fi
+
+if [ -d /usr/lib/NextStep ]; then
+ echo next
+ exit 0
+fi
+
+if [ -f /netbsd ]; then
+ echo netbsd
+ exit 0
+fi
+
+if [ -f /lib/clib -a -f /lib/libc ]; then
+ echo domainos
+ exit 0
+fi
+
+case "$guess" in
+ '') guess="none"
+esac
+
+echo $guess
diff --git a/usr.sbin/xntpd/scripts/README b/usr.sbin/xntpd/scripts/README
new file mode 100644
index 0000000..7439c6c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/README
@@ -0,0 +1,41 @@
+README file for directory ./scripts of the NTP Version 3 distribution
+
+This directory contains shell and perl script files for the configuration,
+monitoring and support of NTP installations. See the README and RELNOTES
+files in the parent directory for directions on how to use these files.
+
+Guess.sh script to figure out what machine and operating system
+ is running this thing. Prizes awarded for new machines
+ added to the list.
+
+autoconf awesome script swiped from Jeff Johnson (who may have
+ swiped it from GNU) which delves deep into the system
+ files to reveal dark secrets necessary to port NTP to
+ everything except sewing machines. Unfinished work.
+
+makeconfig.sh shell script that calles Guess.sh and then figures out
+ what compiler is available, then builds the
+ configuration file in the base directory. Finally, it
+ launches the sed script Config.sed in the base directory
+ to make the makefiles used by most programs.
+
+mklinks script useful to create directories for multiple
+ architecture maintenance
+
+mkversion script useful to create new version numbers for all
+ sources
+
+monitoring directory containing perl scripts useful for monitoring
+ operations
+
+ntp-groper script useful for reaching out and rattling the cages of
+ NTP peers to see if animals are inside the bars
+
+ntp-restart script useful for killing and restarting the NTP daemon
+
+stats directory containing awk ans shell scripts useful for
+ maintaining statistics summaries of clockstats, loopstats
+ and peerstats files
+
+support directory containing shell and perl scripts useful for
+ configuration and monitoring of NTP subnets
diff --git a/usr.sbin/xntpd/scripts/autoconf b/usr.sbin/xntpd/scripts/autoconf
new file mode 100755
index 0000000..b661910
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/autoconf
@@ -0,0 +1,885 @@
+#!/bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf.
+# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Usage: configure [--srcdir=DIR] [--host=HOST] [--gas] [--nfp] [--no-create]
+# [--prefix=PREFIX] [--exec-prefix=PREFIX] [--with-PACKAGE] [TARGET]
+# Ignores all args except --srcdir, --prefix, --exec-prefix, --no-create, and
+# --with-PACKAGE unless this script has special code to handle it.
+
+
+for arg
+do
+ # Handle --exec-prefix with a space before the argument.
+ if test x$next_exec_prefix = xyes; then exec_prefix=$arg; next_exec_prefix=
+ # Handle --host with a space before the argument.
+ elif test x$next_host = xyes; then next_host=
+ # Handle --prefix with a space before the argument.
+ elif test x$next_prefix = xyes; then prefix=$arg; next_prefix=
+ # Handle --srcdir with a space before the argument.
+ elif test x$next_srcdir = xyes; then srcdir=$arg; next_srcdir=
+ else
+ case $arg in
+ # For backward compatibility, also recognize exact --exec_prefix.
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* | --exec=* | --exe=* | --ex=* | --e=*)
+ exec_prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- | --exec | --exe | --ex | --e)
+ next_exec_prefix=yes ;;
+
+ -gas | --gas | --ga | --g) ;;
+
+ -host=* | --host=* | --hos=* | --ho=* | --h=*) ;;
+ -host | --host | --hos | --ho | --h)
+ next_host=yes ;;
+
+ -nfp | --nfp | --nf) ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre | --no-cr | --no-c | --no- | --no)
+ no_create=1 ;;
+
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ next_prefix=yes ;;
+
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=* | --s=*)
+ srcdir=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr | --s)
+ next_srcdir=yes ;;
+
+ -with-* | --with-*)
+ package=`echo $arg|sed 's/-*with-//'`
+ # Delete all the valid chars; see if any are left.
+ if test -n "`echo $package|sed 's/[-a-zA-Z0-9_]*//g'`"; then
+ echo "configure: $package: invalid package name" >&2; exit 1
+ fi
+ eval "with_`echo $package|sed s/-/_/g`=1" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb | --ver | --ve | --v)
+ verbose=yes ;;
+
+ *) ;;
+ esac
+ fi
+done
+
+trap 'rm -f conftest* core; exit 1' 1 3 15
+
+# Needed for some versions of `tr' so that character classes in `[]' work.
+if test "${LANG+set}" = "set" ; then
+ LANG=C
+fi
+
+rm -f conftest*
+compile='${CC-cc} $CFLAGS $DEFS conftest.c -o conftest $LIBS >/dev/null 2>&1'
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+unique_file=lib/msyslog.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ srcdirdefaulted=yes
+ # Try the directory containing this script, then `..'.
+ prog=$0
+ confdir=`echo $prog|sed 's%/[^/][^/]*$%%'`
+ test "X$confdir" = "X$prog" && confdir=.
+ srcdir=$confdir
+ if test ! -r $srcdir/$unique_file; then
+ srcdir=..
+ fi
+fi
+if test ! -r $srcdir/$unique_file; then
+ if test x$srcdirdefaulted = xyes; then
+ echo "configure: Can not find sources in \`${confdir}' or \`..'." 1>&2
+ else
+ echo "configure: Can not find sources in \`${srcdir}'." 1>&2
+ fi
+ exit 1
+fi
+# Preserve a srcdir of `.' to avoid automounter screwups with pwd.
+# But we can't avoid them for `..', to make subdirectories work.
+case $srcdir in
+ .|/*|~*) ;;
+ *) srcdir=`cd $srcdir; pwd` ;; # Make relative path absolute.
+esac
+
+
+useresolv=""
+
+echo CONFIGURE -- initializing DEFS
+test -z "$DEFS" && DEFS="-DDEBUG -DREFCLOCK"
+echo CONFIGURE -- initializing AUTHDEFS
+test -z "$AUTHDEFS" && AUTHDEFS="-DDES -DMD5"
+echo CONFIGURE -- initializing CLOCKDEFS
+test -z "$CLOCKDEFS" && CLOCKDEFS="-DAS2201 -DCHU -DGOES -DLEITCH -DLOCAL_CLOCK -DOMEGA -DPST -DWWVB"
+echo CONFIGURE -- initializing COPTS
+test -z "$COPTS" && COPTS="-O"
+
+test -z "$INCLUDE" && INCLUDE="-I../include"
+test -z "$LIB" && LIB="../lib/libntp.a"
+
+test "`uname`" = "SunOS" && {
+ DEFS="$DEFS -DXNTP_RETROFIT_STDLIB"
+}
+test "`uname`" = "ULTRIX" && {
+ DEFS="$DEFS -DXNTP_RETROFIT_STDLIB -DPLL -DREADKMEM"
+}
+test "`uname`" = "BSD/386" && {
+ DEFS="$DEFS -D__bsdi__"
+}
+test -d /usr/lib/NextStep && {
+ DEFS="$DEFS -DREADKMEM -DSYNCTODR_SUCKS"
+}
+
+SOLARIS=""
+test -d /kernel && test -d /opt && isSOLARIS="1"
+
+echo TODO -- checking for HPUX
+
+# We want these before the checks, so the checks can modify their values.
+test -z "$CFLAGS" && CFLAGS=-g auto_cflags=1
+test -z "$LDFLAGS" && LDFLAGS=-g
+
+#==========================================================================
+if test -z "$CC"; then
+ # Extract the first word of `gcc', so it can be a program name with args.
+ set dummy gcc; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ CC="gcc"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$CC" && CC="cc"
+test -n "$CC" -a -n "$verbose" && echo " setting CC to $CC"
+
+# Find out if we are using GNU C, under whatever name.
+cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes
+#endif
+EOF
+${CC-cc} -E conftest.c > conftest.out 2>&1
+if egrep yes conftest.out >/dev/null 2>&1; then
+ GCC=1 # For later tests.
+fi
+rm -f conftest*
+
+
+# If we're using gcc and the user hasn't specified CFLAGS, add -O to CFLAGS.
+test -n "$GCC" && test -n "$auto_cflags" && CFLAGS="$CFLAGS -O"
+
+
+echo checking how to run the C preprocessor
+if test -z "$CPP"; then
+ CPP='${CC-cc} -E'
+ cat > conftest.c <<EOF
+#include <stdio.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+
+if test -n "$GCC"; then
+ echo checking whether -traditional is needed
+ pattern="Autoconf.*'x'"
+ prog='#include <sgtty.h>
+Autoconf TIOCGETP'
+ cat > conftest.c <<EOF
+$prog
+EOF
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ need_trad=1
+fi
+rm -f conftest*
+
+
+ if test -z "$need_trad"; then
+ prog='#include <termio.h>
+Autoconf TCGETA'
+ cat > conftest.c <<EOF
+$prog
+EOF
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ need_trad=1
+fi
+rm -f conftest*
+
+ fi
+ test -n "$need_trad" && CC="$CC -traditional"
+fi
+
+if test -z "$RANLIB"; then
+ # Extract the first word of `ranlib', so it can be a program name with args.
+ set dummy ranlib; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$RANLIB" && RANLIB=":"
+test -n "$RANLIB" -a -n "$verbose" && echo " setting RANLIB to $RANLIB"
+
+
+# aC_MEMORY_H
+echo checking for ANSI C header files
+cat > conftest.c <<EOF
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+echo '#include <string.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "memchr" conftest.out >/dev/null 2>&1; then
+ # SGI's /bin/cc from Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+cat > conftest.c <<EOF
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e,f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' STDC_HEADERS
+DEFS="$DEFS -DSTDC_HEADERS=1"
+}
+
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+for hdr in string.h memory.h
+do
+trhdr=HAVE_`echo $hdr | tr '[a-z]./' '[A-Z]__'`
+echo checking for ${hdr}
+cat > conftest.c <<EOF
+#include <${hdr}>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' ${trhdr}
+DEFS="$DEFS -D${trhdr}=1"
+}
+
+fi
+rm -f conftest*
+done
+
+echo checking for unistd.h
+cat > conftest.c <<EOF
+#include <unistd.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' NTP_POSIX_SOURCE
+DEFS="$DEFS -DNTP_POSIX_SOURCE=1"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for mode_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "mode_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' mode_t to be 'int'
+DEFS="$DEFS -Dmode_t=int"
+}
+
+fi
+rm -f conftest*
+
+echo checking for pid_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "pid_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' pid_t to be 'int'
+DEFS="$DEFS -Dpid_t=int"
+}
+
+fi
+rm -f conftest*
+
+echo checking for return type of signal handlers
+cat > conftest.c <<EOF
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+extern void (*signal ()) ();
+int main() { exit(0); }
+int t() { int i; }
+EOF
+if eval $compile; then
+ {
+test -n "$verbose" && \
+echo ' defining' RETSIGTYPE to be 'void'
+DEFS="$DEFS -DRETSIGTYPE=void"
+}
+
+else
+ {
+test -n "$verbose" && \
+echo ' defining' RETSIGTYPE to be 'int'
+DEFS="$DEFS -DRETSIGTYPE=int"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for size_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "size_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' size_t to be 'unsigned'
+DEFS="$DEFS -Dsize_t=unsigned"
+}
+
+fi
+rm -f conftest*
+
+
+# aC_VPRINTF
+
+# aC_TIME_WITH_SYS_TIME
+# aC_STRUCT_TM
+
+# aC_CHAR_UNSIGNED
+echo checking for signed char declaration
+cat > conftest.c <<EOF
+main() { signed char c; }
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' NO_SIGNED_CHAR_DECL
+DEFS="$DEFS -DNO_SIGNED_CHAR_DECL=1"
+}
+
+fi
+rm -f conftest*
+echo checking for s_char in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "s_char" conftest.out >/dev/null 2>&1; then
+ {
+test -n "$verbose" && \
+echo ' defining' S_CHAR_DEFINED
+DEFS="$DEFS -DS_CHAR_DEFINED=1"
+}
+
+fi
+rm -f conftest*
+
+
+prog='/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+p = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25,17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}'
+echo checking for working const
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { $prog }
+EOF
+if eval $compile; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' const to be 'empty'
+DEFS="$DEFS -Dconst="
+}
+
+fi
+rm -f conftest*
+
+if test -n "$GCC"; then
+echo checking for inline
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { } inline foo() { }
+EOF
+if eval $compile; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' inline to be '__inline'
+DEFS="$DEFS -Dinline=__inline"
+}
+
+fi
+rm -f conftest*
+
+fi
+
+
+# aC_INT_16_BITS
+# DEC Alpha running OSF/1
+echo checking integer size
+cat > conftest.c <<EOF
+main() { exit(!(sizeof(long) > sizeof(int))); }
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' LONG to be 'int'
+DEFS="$DEFS -DLONG=int"
+}
+ {
+test -n "$verbose" && \
+echo ' defining' U_LONG to be 'u_int'
+DEFS="$DEFS -DU_LONG=u_int"
+}
+
+fi
+rm -f conftest*
+
+if test -n "$GCC"; then
+{
+test -n "$verbose" && \
+echo ' defining' HAVE_LONG_DOUBLE
+DEFS="$DEFS -DHAVE_LONG_DOUBLE=1"
+}
+
+else
+echo checking for long double
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { } long double foo() { }
+EOF
+if eval $compile; then
+ {
+test -n "$verbose" && \
+echo ' defining' HAVE_LONG_DOUBLE
+DEFS="$DEFS -DHAVE_LONG_DOUBLE=1"
+}
+
+fi
+rm -f conftest*
+
+fi
+
+echo checking byte ordering
+cat > conftest.c <<EOF
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' WORDS_BIGENDIAN
+DEFS="$DEFS -DWORDS_BIGENDIAN=1"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for restartable system calls
+cat > conftest.c <<EOF
+/* Exit 0 (true) if wait returns something other than -1,
+ i.e. the pid of the child, which means that wait was restarted
+ after getting the signal. */
+#include <sys/types.h>
+#include <signal.h>
+ucatch (isig) { }
+main () {
+ int i = fork (), status;
+ if (i == 0) { sleep (3); kill (getppid (), SIGINT); sleep (3); exit (0); }
+ signal (SIGINT, ucatch);
+ status = wait(&i);
+ if (status == -1) wait(&i);
+ exit (status == -1);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' HAVE_RESTARTABLE_SYSCALLS
+DEFS="$DEFS -DHAVE_RESTARTABLE_SYSCALLS=1"
+}
+
+fi
+rm -f conftest*
+
+
+havestreams=""
+echo checking for sys/stream.h
+cat > conftest.c <<EOF
+#include <sys/stream.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' STREAM
+DEFS="$DEFS -DSTREAM=1"
+}
+ havestreams="1"
+fi
+rm -f conftest*
+
+echo checking clock options
+
+if test -f /dev/pps ; then
+ echo found ppsclk
+ DEFS="$DEFS -DPPSCLK"
+fi
+if test -f /dev/tpro ; then
+ echo found tpro
+ CLOCKDEFS="$CLOCKDEFS -DTPRO"
+fi
+if test -f /dev/irig ; then
+ echo found irig
+ CLOCKDEFS="$CLOCKDEFS -DIRIG"
+fi
+
+echo TODO -- checking for adjtime/libadjtimed.a
+
+test -n "$useresolv" && {
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lresolv"
+have_lib=""
+echo checking for -lresolv
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lresolv"
+else
+ :;
+fi
+
+}
+test -n "$isSOLARIS" && {
+ {
+test -n "$verbose" && \
+echo ' defining' SOLARIS
+DEFS="$DEFS -DSOLARIS=1"
+}
+
+ {
+test -n "$verbose" && \
+echo ' defining' SLEWALWAYS
+DEFS="$DEFS -DSLEWALWAYS=1"
+}
+
+ {
+test -n "$verbose" && \
+echo ' defining' STUPID_SIGNAL
+DEFS="$DEFS -DSTUPID_SIGNAL=1"
+}
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lsocket"
+have_lib=""
+echo checking for -lsocket
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lsocket"
+else
+ :;
+fi
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lnsl"
+have_lib=""
+echo checking for -lnsl
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lnsl"
+else
+ :;
+fi
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lelf"
+have_lib=""
+echo checking for -lelf
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lelf"
+else
+ :;
+fi
+
+}
+
+LIBS_save="${LIBS}"
+LIBS="${LIBS} -lBSD"
+have_lib=""
+echo checking for -lBSD
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; LIBS="$LIBS -lBSD"
+else
+ :;
+fi
+
+test -n "$have_lib" && {
+test -n "$verbose" && \
+echo ' defining' COMPAT to be '"-lBSD"'
+DEFS="$DEFS -DCOMPAT=\"-lBSD\""
+}
+
+
+test -z "$isSOLARIS" && {
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lkvm"
+have_lib=""
+echo checking for -lkvm
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lkvm"
+else
+ :;
+fi
+
+ test -n "$have_lib" && {
+test -n "$verbose" && \
+echo ' defining' USELIBKVM
+DEFS="$DEFS -DUSELIBKVM=1"
+}
+
+}
+
+LIBS_save="${LIBS}"
+LIBS="${LIBS} -lmld"
+have_lib=""
+echo checking for -lmld
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lmld"
+else
+ :;
+fi
+
+
+prefix=/usr/local
+bindir=/usr/local/bin
+
+if test -n "$prefix"; then
+ test -z "$exec_prefix" && exec_prefix='${prefix}'
+ prsub="s%^prefix\\([ ]*\\)=\\([ ]*\\).*$%prefix\\1=\\2$prefix%"
+fi
+if test -n "$exec_prefix"; then
+ prsub="$prsub
+s%^exec_prefix\\([ ]*\\)=\\([ ]*\\).*$%\
+exec_prefix\\1=\\2$exec_prefix%"
+fi
+DEFS="`echo \"$DEFS\" | sed 's%[&\\\]%\\\&%g'`"
+
+trap 'rm -f config.status; exit 1' 1 3 15
+echo creating config.status
+rm -f config.status
+cat > config.status <<EOF
+#!/bin/sh
+# Generated automatically by autoconf.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $*
+PROGS='$PROGS'
+CC='$CC'
+CFLAGS='$CFLAGS'
+LDFLAGS='$LDFLAGS'
+CPP='$CPP'
+RANLIB='$RANLIB'
+bindir='$bindir'
+AUTHDEFS='$AUTHDEFS'
+CLOCKDEFS='$CLOCKDEFS'
+COPTS='$COPTS'
+INCLUDE='$INCLUDE'
+LIB='$LIB'
+ADJLIB='$ADJLIB'
+RESLIB='$RESLIB'
+COMPAT='$COMPAT'
+LIBS='$LIBS'
+srcdir='$srcdir'
+DEFS='$DEFS'
+prefix='$prefix'
+exec_prefix='$exec_prefix'
+prsub='$prsub'
+EOF
+
diff --git a/usr.sbin/xntpd/scripts/install.sh b/usr.sbin/xntpd/scripts/install.sh
new file mode 100755
index 0000000..1bc4989
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/install.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# Emulate the BSD install command with cpset for System V
+# Tom Moore - NCR Corporation
+#
+PATH=/bin:/etc:/usr/bin:/usr/ucb
+export PATH
+
+# Default values
+mode=0755
+owner=bin
+group=bin
+strip=FALSE
+remove=TRUE
+
+USAGE="install [-s] [-c] [-m mode] [-o owner] [-g group] source file|directory"
+set -- `getopt scm:o:g: $*` || {
+ echo $USAGE >&2
+ exit 2
+}
+for option in $*
+do
+ case $option in
+ -s) # Strip the installed file
+ strip=TRUE
+ shift
+ ;;
+ -c) # Copy the source file rather than move it
+ remove=FALSE
+ shift
+ ;;
+ -m) # File mode
+ mode=$2
+ shift 2
+ ;;
+ -o) # File owner
+ owner=$2
+ shift 2
+ ;;
+ -g) # File group
+ group=$2
+ shift 2
+ ;;
+ --) # End of options
+ shift
+ break
+ ;;
+ esac
+done
+
+case $# in
+0) echo "install: no file or destination specified" >&2
+ exit 2
+ ;;
+1) echo "install: no destination specified" >&2
+ exit 2
+ ;;
+2) source=$1
+ destination=$2
+ ;;
+*) echo "install: too many files specified" >&2
+ exit 2
+ ;;
+esac
+
+[ $source = $destination -o $destination = . ] && {
+ echo "install: can't move $source onto itself" >&2
+ exit 1
+}
+
+[ -f $source ] || {
+ echo "install: can't open $source" >&2
+ exit 1
+}
+
+if [ -d $destination ]
+then
+ file=`basename $source`
+ OLDdestination=$destination/OLD$file
+ destination=$destination/$file
+else
+ dir=`dirname $destination`
+ file=`basename $destination`
+ OLDdestination=$dir/OLD$file
+fi
+
+(cp $source $destination &&
+ chmod $mode $destination &&
+ chown $owner $destination &&
+ chgrp $group $destination) || true # exit 1
+
+# /bin/rm -f $OLDdestination
+
+[ $strip = TRUE ] &&
+ strip $destination
+
+[ $remove = TRUE ] &&
+ rm -f $source
+
+exit 0
diff --git a/usr.sbin/xntpd/scripts/makeconfig.sh b/usr.sbin/xntpd/scripts/makeconfig.sh
new file mode 100755
index 0000000..8842a86
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/makeconfig.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+MACHINE=${1-${OS}}
+COMPILER=${2-${CC}}
+
+#
+# Figure out which compiler to use. Stolen from Jeff Johnson.
+#
+if [ "0$COMPILER" = "0" ]; then
+ COMPILER="cc"
+ set dummy gcc; word=$2
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ COMPILER="gcc"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+
+#
+# Figure out the byte order and word size.
+#
+if (cd util && rm -f longsize && $COMPILER -o longsize longsize.c ); then
+ if util/longsize >/dev/null 2>&1; then
+ LONG=`util/longsize`
+ else
+ echo "TROUBLE: executables built by your compiler don't work - bug your vendor"
+ exit 1
+ fi
+else
+ echo "TROUBLE: could not compile !"
+ echo "TROUBLE: either your compiler does not work / is not present"
+ echo "TROUBLE: or you have mangled the file tree"
+ exit 1
+fi
+(cd util && rm -f byteorder && $COMPILER -o byteorder byteorder.c $LONG )
+BYTE=`util/byteorder `
+if [ "0$BYTE" = "0" ]; then
+ BYTE="XNTP_BIG_ENDIAN"
+fi
+(cd util && rm -f byteorder longsize)
+
+#
+# Figure out which machine we have.
+#
+if [ "0$MACHINE" = "0" ]; then
+ GUESS=`scripts/Guess.sh`
+ if [ "0$GUESS" = "0none" ]; then
+ echo ' '
+ echo "I don't know your system!"
+ echo "I do know about the following systems:"
+ (cd machines && ls -C *)
+ echo "Choose a system and type \"make OS=<system>\""
+ exit 1
+ else
+ if [ -f machines/$GUESS ]; then
+ MACHINE=$GUESS
+ else
+ if [ -f machines/$GUESS.posix ]; then
+ MACHINE="$GUESS.posix"
+ else
+ MACHINE="$GUESS.bsd"
+ fi
+ fi
+ fi
+fi
+
+echo "Configuring machines/$MACHINE compilers/$MACHINE.$COMPILER"
+
+if [ -f machines/$MACHINE ]; then
+ cat machines/$MACHINE >Config ;
+ if [ -f compilers/$MACHINE.$COMPILER ]; then
+ cat compilers/$MACHINE.$COMPILER >>Config
+ else
+ echo "COMPILER= $COMPILER" >>Config
+ fi
+ echo "LIBDEFS= -D$BYTE" >>Config
+ cat Config.local >>Config
+else
+ echo "Don't know how to build xntpd for machine $MACHINE " ;
+ exit 1
+fi
diff --git a/usr.sbin/xntpd/scripts/mklinks b/usr.sbin/xntpd/scripts/mklinks
new file mode 100755
index 0000000..8565d1c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/mklinks
@@ -0,0 +1,9 @@
+#!/bin/sh
+# call from the source root as 'mklinks ../sun4 ../src'
+find . -type d -print | sort | sed "s-^\.-mkdir $1-" | sh
+root=`echo $2 | sed "s-^\.\./--"`
+find . ! -type d -a ! -name Config -print | sed "s-^\./--" | while read file
+ do
+ down=`echo $file | sed -e "s-[^/]*-..-g"`
+ ln -s $down/$root/$file $1/$file
+ done
diff --git a/usr.sbin/xntpd/scripts/mkversion b/usr.sbin/xntpd/scripts/mkversion
new file mode 100755
index 0000000..fe043e0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/mkversion
@@ -0,0 +1,36 @@
+#!/bin/sh -
+PROG=${1-UNKNOWN}
+if [ ! -f .version ]; then
+ echo 0 > .version
+fi
+
+RUN="`cat .version`"
+RUN="`expr $RUN + 1`"
+echo $RUN > .version
+
+DATE="`LC_TIME=C date`"
+TOPDIR=`echo $0 | sed -e 's;mkversion;..;'`
+
+if [ -r VERSION ]; then
+ VERSION=VERSION
+else if [ -r ${TOPDIR}/VERSION ]; then
+ VERSION=${TOPDIR}/VERSION
+else
+ VERSION=../VERSION
+fi; fi
+
+if [ -f "$VERSION" ]; then
+ FLAGS="`egrep '^[0-9a-zA-Z_]+=' "$VERSION" | tr '\012' ';'` "
+else
+ FLAGS=""
+fi
+
+echo "Version $PROG ${FLAGS}${DATE} (${RUN})";
+
+rm -f version.c
+cat > version.c << -EoF-
+/*
+ * version file for $PROG
+ */
+char * Version = "$PROG ${FLAGS}${DATE} (${RUN})";
+-EoF-
diff --git a/usr.sbin/xntpd/scripts/monitoring/README b/usr.sbin/xntpd/scripts/monitoring/README
new file mode 100644
index 0000000..fa8ad8b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/README
@@ -0,0 +1,154 @@
+This directory contains support for monitoring the local clock of xntp daemons.
+
+WARNING: The scripts and routines contained in this directory are bete realease!
+ Do not depend on their correct operation. They are, however, in regular
+ use at University of Erlangen-Nuernberg. No severe problems are known
+ for this code.
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+PLEASE THINK TWICE BEFORE STARTING MONITORING REMOTE XNTP DEAMONS !!!!
+MONITORING MAY INCREASE THE LOAD OF THE DEAMON MONITORED AND MAY
+INCREASE THE NETWORK LOAD SIGNIFICANTLY
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+Files are:
+
+README:
+ This file
+
+ntptrap:
+ perl script to log ntp mode 6 trap messages.
+
+ It sends a set_trap request to each server given and dumps the
+ trap messages received. It handles refresh of set_trap.
+ Currently it handles only NTP V2, however the NTP V3 servers
+ also accept v2 requests. It will not interpret v3 system and peer
+ stati correctly.
+
+ usage:
+ ntptrap [-n] [-p <port>] [-l <debug-output>] servers...
+
+ -n: do not send set_trap requests
+
+ port: port to listen for responses
+ useful if you have a configured trap
+
+ debug-output: file to write trace output to (for debugging)
+
+ This script convinced me that ntp trap messages are only of
+ little use.
+
+ntploopstat:
+ perl script to gather loop info statistics from xntpd via mode 7
+ LOOP_INFO requests.
+
+ This script collects data to allow monitoring of remote xntp servers
+ where it is not possible to directly access the loopstats file
+ produced by xntpd itself. Of course, it can be used to sample
+ a local server if it is not configured to produce a loopstats file.
+
+ Please note, this program poses a high load on the server as
+ a communication takes place every delay seconds ! USE WITH CARE !
+
+ usage:
+ ntploopstat [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+
+ delay: number of seconds to wait between samples
+ default: 60 seconds
+ timeout: number of seconds to wait for reply
+ default 12 seconds
+ logfile: file to log samples to
+ default: loopstats:<ntpserver>:
+ (note the trailing colon)
+ This name actually is a prefix.
+ The file name is dynamically derived by appending
+ the name of the month the sample belongs to.
+ Thus all samples of a month end up in the same file.
+
+ the format of the files generated is identical to the format used by
+ xntpd with the loopstats file:
+ MJD <seconds since midnight UTC> offset frequency compliance
+
+ if a timeout occurs the next sample is tried after delay/2 seconds
+
+ The script will terminate after MAX_FAIL (currently 60) consecutive errors.
+ Errors are counted for:
+ - error on send call
+ - error on select call
+ - error on recv call
+ - short packet received
+ - bad packet
+ - error on open for logfile
+
+ntploopwatch:
+ perl script to display loop filter statistics collected by ntploopstat
+ or dumped directly by xntpd.
+
+ Gnuplot is used to produce a graphical representation of the sample
+ values, that have been preprocessed and analysed by this script.
+
+ It can either be called to produce a printout of specific data set or
+ used to continously monitor the values. Monitoring is achieved by
+ periodically reprocessing the logfiles, which are updated regularly
+ either by a running ntploopstat process or by the running xntpd.
+
+ usage:
+ to watch statistics permanently:
+ ntploopwatch [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+
+ to get a single print out specify also
+ -P<printer> [-s<samples>]
+ [-S <start-time>] [-E <end-time>]
+ [-O <MaxOffs>] [-o <MinOffs>]
+
+ level: level of verbosity for debugging
+ config-file: file to read configurable settings from
+ On each iteration it is checked and reread
+ if it has been changed
+ default: loopwatch.config
+ working-dir: specify working directory for process, affects
+ interpretation of relative file names
+
+ All other flags are only useful with printing plots, as otherwise
+ command line values would be replaced by settings from the config file.
+
+ printer: specify printer to print plot
+ BSD print systems semantics apply; if printer is omitted
+ the name "ps" is used; plots are prepared using
+ PostScript, thus the printer should best accept
+ postscript input
+
+ For the following see also the comments in loopwatch.config.SAMPLE
+
+ samples: use last # samples from input data
+ start-time: ignore input samples before this date
+ end-time: ignore input samples after this date
+ if both start-time and end-time are specified
+ a given samples value is ignored
+ MaxOffs:
+ MinOffs: restrict value range
+
+loopwatch.config.SAMPLE:
+ sample config file for ntploopwatch
+ each configurable option is explained there
+
+lr.pl:
+ linear regression package used by ntploopwatch to compute
+ linear approximations for frequency and offset values
+ within display range
+
+timelocal.pl:
+ used during conversion of ISO_DATE_TIME values specified in loopwatch
+ config files to unix epoch values (seconds since 1970-01-01_00:00_00 UTC)
+
+ A version of this file is distributed with perl-4.x, however,
+ it has a bug related to dates crossing 1970, causing endless loops..
+ The version contained here has been fixed.
+
+ntp.pl:
+ perl support for ntp v2 mode 6 message handling
+ WARNING: This code is beta level - it triggers a memory leak;
+ as for now it is not quite clear, wether this is caused by a
+ bug in perl or by bad usage of perl within this script.
+
diff --git a/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE b/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE
new file mode 100644
index 0000000..8cefea3
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE
@@ -0,0 +1,89 @@
+# sample configuration and control file for ntploowatch
+#
+# delay: sampling interval in seconds
+delay=60
+# samples: use only last # samples
+samples=600
+# DO NOT USE srcprefix in shared config files
+# srcprefix: name of file to read samples from
+# current time suffix (month name) is appended
+# defaults to "./var@$STATHOST/loopstats."
+# The string "$STATHOST"is replaced by the name of the host
+# being monitored
+#srcprefix=./var@$STATHOST/loopstats.
+#
+# showoffs: yes/no control display of offset values
+showoffs=yes
+#
+# showfreq: yes/no control display of frequency values
+showfreq=yes
+#
+# showcmpl: yes/no control display of compliance values
+showcmpl=no
+#
+# showoreg: yes/no control display of linear regression of offset values
+showoreg=no
+#
+# showfreg: yes/no control display of linear regression of frequency values
+showfreg=no
+#
+# timebase: dynamic/ISO_DATE_TIME point of zero for linear regression
+# ISO_DATE_TIME: yyyy-mm-dd_hh:mm:ss.ms
+# values are interpreted using local time zone
+# parts omitted from front default to current date/time
+# parts omitted from end default to lowest permitted values
+# to get aa:bb being interpreted as minutes:seconds use aa:bb.0
+# for dynamic '00:00:00.0 of current day' is used
+timebase=dynamic
+#
+# freqbase: dynamic/<baseval>
+# if a number is given, subtract this from sampling values for display
+# if dynamic is selected, freqbase is adjusted to fit into the range of
+# offset values
+freqbase=dynamic
+#
+# cmplscale: dynamic/<scaling>
+# if a number is given, the sampling values are divided by this number
+# if dynamic is selected, cmplscale is adjusted to fit into the range of
+# offset values
+cmplscale=dynamic
+#
+# DumbScale: 0/1
+# 0 enables dynamic adjust of value ranges for freqbase and cmplscale
+# timescale is labeled with human readable times
+# 1 only uses explicit scaling for numbers
+# timescale is labeled with hours relative to timebase
+DumbScale=0
+#
+# StartTime: none/ISO_DATE_TIME
+# ignore any samples before the specified date
+StartTime=none
+#
+# EndTime: none/ISO_DATE_TIME
+# ignore any samples after the specified date
+#
+# if both StartTime and EndTime are specified
+# the value specified for samples is ignored
+EndTime=none
+#
+# MaxOffs: none/<number>
+# limit display (y-axis) to values not larger than <number>
+MaxOffset=none
+#
+# MinOffs: none/<number>
+# limit display (y-axis) to values not smaller than <number>
+MinOffset=none
+
+#
+# verbose: <number>
+# specify level for debugging
+# default is 0 for printing and 1 for monitoring
+# level 1 will just print a timestamp for any display update
+# (this is every delay seconds)
+verbose=1
+#
+# deltaT: <seconds>
+# mark `holes' in the sample data grater than <seconds>
+# by a break in the plot
+# default: 512 seconds
+deltaT=512
diff --git a/usr.sbin/xntpd/scripts/monitoring/lr.pl b/usr.sbin/xntpd/scripts/monitoring/lr.pl
new file mode 100755
index 0000000..02c7550
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/lr.pl
@@ -0,0 +1,145 @@
+;#
+;# lr.pl,v 3.1 1993/07/06 01:09:08 jbj Exp
+;#
+;#
+;# Linear Regression Package for perl
+;# to be 'required' from perl
+;#
+;# Copyright (c) 1992
+;# Frank Kardel, Rainer Pruy
+;# Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+##
+## y = A + Bx
+##
+## B = (n * Sum(xy) - Sum(x) * Sum(y)) / (n * Sum(x^2) - Sum(x)^2)
+##
+## A = (Sum(y) - B * Sum(x)) / n
+##
+
+##
+## interface
+##
+*lr_init = *lr'lr_init; #';# &lr_init(tag); initialize data set for tag
+*lr_sample = *lr'lr_sample; #';# &lr_sample(x,y,tag); enter sample
+*lr_Y = *lr'lr_Y; #';# &lr_Y(x,tag); compute y for given x
+*lr_X = *lr'lr_X; #';# &lr_X(y,tag); compute x for given y
+*lr_r = *lr'lr_r; #';# &lr_r(tag); regression coeffizient
+*lr_cov = *lr'lr_cov; #';# &lr_cov(tag); covariance
+*lr_A = *lr'lr_A; #';# &lr_A(tag);
+*lr_B = *lr'lr_B; #';# &lr_B(tag);
+*lr_sigma = *lr'lr_sigma; #';# &lr_sigma(tag); standard deviation
+*lr_mean = *lr'lr_mean; #';# &lr_mean(tag);
+#########################
+
+package lr;
+
+sub tagify
+{
+ local($tag) = @_;
+ if (defined($tag))
+ {
+ *lr_n = eval "*${tag}_n";
+ *lr_sx = eval "*${tag}_sx";
+ *lr_sx2 = eval "*${tag}_sx2";
+ *lr_sxy = eval "*${tag}_sxy";
+ *lr_sy = eval "*${tag}_sy";
+ *lr_sy2 = eval "*${tag}_sy2";
+ }
+}
+
+sub lr_init
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ $lr_n = 0;
+ $lr_sx = 0.0;
+ $lr_sx2 = 0.0;
+ $lr_sxy = 0.0;
+ $lr_sy = 0.0;
+ $lr_sy2 = 0.0;
+}
+
+sub lr_sample
+{
+ local($_x, $_y) = @_;
+
+ &tagify($_[$[+2]) if defined($_[$[+2]);
+
+ $lr_n++;
+ $lr_sx += $_x;
+ $lr_sy += $_y;
+ $lr_sxy += $_x * $_y;
+ $lr_sx2 += $_x**2;
+ $lr_sy2 += $_y**2;
+}
+
+sub lr_B
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 1 unless ($lr_n * $lr_sx2 - $lr_sx**2);
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / ($lr_n * $lr_sx2 - $lr_sx**2);
+}
+
+sub lr_A
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sy - &lr_B * $lr_sx) / $lr_n;
+}
+
+sub lr_Y
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return &lr_A + &lr_B * $_[$[];
+}
+
+sub lr_X
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($_[$[] - &lr_A) / &lr_B;
+}
+
+sub lr_r
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ local($s) = ($lr_n * $lr_sx2 - $lr_sx**2) * ($lr_n * $lr_sy2 - $lr_sy**2);
+
+ return 1 unless $s;
+
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / sqrt($s);
+}
+
+sub lr_cov
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sxy - $lr_sx * $lr_sy / $lr_n) / ($lr_n - 1);
+}
+
+sub lr_sigma
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 1;
+ return sqrt(($lr_sy2 - ($lr_sy * $lr_sy) / $lr_n) / ($lr_n));
+}
+
+sub lr_mean
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 0;
+ return $lr_sy / $lr_n;
+}
+
+&lr_init();
+
+1;
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntp.pl b/usr.sbin/xntpd/scripts/monitoring/ntp.pl
new file mode 100755
index 0000000..f3bfd2b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntp.pl
@@ -0,0 +1,477 @@
+#!/local/bin/perl
+;#
+;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+package ntp;
+
+$NTP_version = 2;
+$ctrl_mode=6;
+
+$byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
+$MAX_DATA = 468;
+
+$sequence = 0; # initial sequence number incred before used
+$pad=4;
+$do_auth=0; # no possibility today
+$keyid=0;
+;#list if known keys (passwords)
+%KEYS = ( 0, "\200\200\200\200\200\200\200\200",
+ );
+
+;#-----------------------------------------------------------------------------
+;# access routines for ntp control packet
+ ;# NTP control message format
+ ;# C LI|VN|MODE LI 2bit=00 VN 3bit=2(3) MODE 3bit=6 : $byte1
+ ;# C R|E|M|Op R response E error M more Op opcode
+ ;# n sequence
+ ;# n status
+ ;# n associd
+ ;# n offset
+ ;# n count
+ ;# a+ data (+ padding)
+ ;# optional authentication data
+ ;# N key
+ ;# N2 checksum
+
+;# first bye of packet
+sub pkt_LI { return ($_[$[] >> 6) & 0x3; }
+sub pkt_VN { return ($_[$[] >> 3) & 0x7; }
+sub pkt_MODE { return ($_[$[] ) & 0x7; }
+
+;# second byte of packet
+sub pkt_R { return ($_[$[] & 0x80) == 0x80; }
+sub pkt_E { return ($_[$[] & 0x40) == 0x40; }
+sub pkt_M { return ($_[$[] & 0x20) == 0x20; }
+sub pkt_OP { return $_[$[] & 0x1f; }
+
+;#-----------------------------------------------------------------------------
+
+sub setkey
+{
+ local($id,$key) = @_;
+
+ $KEYS{$id} = $key if (defined($key));
+ if (! defined($KEYS{$id}))
+ {
+ warn "Key $id not yet specified - key not changed\n";
+ return undef;
+ }
+ return ($keyid,$keyid = $id)[$[];
+}
+
+;#-----------------------------------------------------------------------------
+sub numerical { $a <=> $b; }
+
+;#-----------------------------------------------------------------------------
+
+sub send #'
+{
+ local($fh,$opcode, $associd, $data,$address) = @_;
+ $fh = caller(0)."'$fh";
+
+ local($junksize,$junk,$packet,$offset,$ret);
+ $offset = 0;
+
+ $sequence++;
+ while(1)
+ {
+ $junksize = length($data);
+ $junksize = $MAX_DATA if $junksize > $MAX_DATA;
+
+ ($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
+ $packet
+ = pack("C2n5a".(($junk eq "") ? 0 : &pad($junksize+12,$pad)-12),
+ $byte1,
+ ($opcode & 0x1f) | ($data ? 0x20 : 0),
+ $sequence,
+ 0, $associd,
+ $offset, $junksize, $junk);
+ if ($do_auth)
+ {
+ ;# not yet
+ }
+ $offset += $junksize;
+
+ if (defined($address))
+ {
+ $ret = send($fh, $packet, 0, $address);
+ }
+ else
+ {
+ $ret = send($fh, $packet, 0);
+ }
+
+ if (! defined($ret))
+ {
+ warn "send failed: $!\n";
+ return undef;
+ }
+ elsif ($ret != length($packet))
+ {
+ warn "send failed: sent only $ret from ".length($packet). "bytes\n";
+ return undef;
+ }
+ return $sequence unless $data;
+ }
+}
+
+;#-----------------------------------------------------------------------------
+;# status interpretation
+;#
+sub getval
+{
+ local($val,*list) = @_;
+
+ return $list{$val} if defined($list{$val});
+ return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
+ return "unknown-$val";
+}
+
+;#---------------------------------
+;# system status
+;#
+;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
+sub ssw_LI { return ($_[$[] >> 14) & 0x3; }
+sub ssw_CS { return ($_[$[] >> 8) & 0x3f; }
+sub ssw_SECnt { return ($_[$[] >> 4) & 0xf; }
+sub ssw_SECode { return $_[$[] & 0xf; }
+
+%LI = ( 0, "leap_none", 1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
+%ClockSource = (0, "sync_unspec",
+ 1, "sync_lf_clock",
+ 2, "sync_uhf_clock",
+ 3, "sync_hf_clock",
+ 4, "sync_local_proto",
+ 5, "sync_ntp",
+ 6, "sync_udp/time",
+ 7, "sync_wristwatch",
+ "-", "ClockSource",
+ );
+
+%SystemEvent = (0, "event_unspec",
+ 1, "event_restart",
+ 2, "event_fault",
+ 3, "event_sync_chg",
+ 4, "event_sync/strat_chg",
+ 5, "event_clock_reset",
+ 6, "event_bad_date",
+ 7, "event_clock_excptn",
+ "-", "event",
+ );
+sub LI
+{
+ &getval(&ssw_LI($_[$[]),*LI);
+}
+sub ClockSource
+{
+ &getval(&ssw_CS($_[$[]),*ClockSource);
+}
+
+sub SystemEvent
+{
+ &getval(&ssw_SECode($_[$[]),*SystemEvent);
+}
+
+sub system_status
+{
+ return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]),
+ &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"),
+ &SystemEvent($_[$[]));
+}
+;#---------------------------------
+;# peer status
+;#
+;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
+sub psw_PStat_config { return ($_[$[] & 0x8000) == 0x8000; }
+sub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; }
+sub psw_PStat_authentic { return ($_[$[] & 0x2000) == 0x2000; }
+sub psw_PStat_reach { return ($_[$[] & 0x1000) == 0x1000; }
+sub psw_PStat_sane { return ($_[$[] & 0x0800) == 0x0800; }
+sub psw_PStat_dispok { return ($_[$[] & 0x0400) == 0x0400; }
+sub psw_PStat { return ($_[$[] >> 10) & 0x3f; }
+sub psw_PSel { return ($_[$[] >> 8) & 0x3; }
+sub psw_PCnt { return ($_[$[] >> 4) & 0xf; }
+sub psw_PCode { return $_[$[] & 0xf; }
+
+%PeerSelection = (0, "sel_reject",
+ 1, "sel_candidate",
+ 2, "sel_selcand",
+ 3, "sel_sys.peer",
+ "-", "PeerSel",
+ );
+%PeerEvent = (0, "event_unspec",
+ 1, "event_ip_err",
+ 2, "event_authen",
+ 3, "event_unreach",
+ 4, "event_reach",
+ 5, "event_clock_excptn",
+ 6, "event_stratum_chg",
+ "-", "event",
+ );
+
+sub PeerSelection
+{
+ &getval(&psw_PSel($_[$[]),*PeerSelection);
+}
+sub PeerEvent
+{
+ &getval(&psw_PCode($_[$[]),*PeerEvent);
+}
+
+sub peer_status
+{
+ local($x) = ("");
+ $x .= "config," if &psw_PStat_config($_[$[]);
+ $x .= "authenable," if &psw_PStat_authenable($_[$[]);
+ $x .= "authentic," if &psw_PStat_authentic($_[$[]);
+ $x .= "reach," if &psw_PStat_reach($_[$[]);
+ $x .= &psw_PStat_sane($_[$[]) ? "sane," : "insane,";
+ $x .= "hi_disp," unless &psw_PStat_dispok($_[$[]);
+
+ $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]),
+ &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"),
+ &PeerEvent($_[$[]));
+ return $x;
+}
+
+;#---------------------------------
+;# clock status
+;#
+;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
+sub csw_CStat { return ($_[$[] >> 8) & 0xff; }
+sub csw_CEvnt { return $_[$[] & 0xff; }
+
+%ClockStatus = (0, "clk_nominal",
+ 1, "clk_timeout",
+ 2, "clk_badreply",
+ 3, "clk_fault",
+ 4, "clk_prop",
+ 5, "clk_baddate",
+ 6, "clk_badtime",
+ "-", "clk",
+ );
+
+sub clock_status
+{
+ return sprintf("%s, last %s",
+ &getval(&csw_CStat($_[$[]),*ClockStatus),
+ &getval(&csw_CEvnt($_[$[]),*ClockStatus));
+}
+
+;#---------------------------------
+;# error status
+;#
+;# format: |Err|reserved| Err=8bit
+;#
+sub esw_Err { return ($_[$[] >> 8) & 0xff; }
+
+%ErrorStatus = (0, "err_unspec",
+ 1, "err_auth_fail",
+ 2, "err_invalid_fmt",
+ 3, "err_invalid_opcode",
+ 4, "err_unknown_assoc",
+ 5, "err_unknown_var",
+ 6, "err_invalid_value",
+ 7, "err_adm_prohibit",
+ );
+
+sub error_status
+{
+ return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus));
+}
+
+;#-----------------------------------------------------------------------------
+;#
+;# cntrl op name translation
+
+%CntrlOpName = (1, "read_status",
+ 2, "read_variables",
+ 3, "write_variables",
+ 4, "read_clock_variables",
+ 5, "write_clock_variables",
+ 6, "set_trap",
+ 7, "trap_response",
+ 31, "unset_trap", # !!! unofficial !!!
+ "-", "cntrlop",
+ );
+
+sub cntrlop_name
+{
+ return &getval($_[$[],*CntrlOpName);
+}
+
+;#-----------------------------------------------------------------------------
+
+$STAT_short_pkt = 0;
+$STAT_pkt = 0;
+
+;# process a NTP control message (response) packet
+;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
+;# $ret: undef --> not yet complete
+;# "" --> complete packet received
+;# "ERROR" --> error during receive, bad packet, ...
+;# else --> error packet - list may contain useful info
+
+
+sub handle_packet
+{
+ local($pkt,$from) = @_; # parameters
+ local($len_pkt) = (length($pkt));
+;# local(*FRAGS,*lastseen);
+ local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
+ local($autch_keyid,$auth_cksum);
+
+ $STAT_pkt++;
+ if ($len_pkt < 12)
+ {
+ $STAT_short_pkt++;
+ return ("ERROR","short packet received");
+ }
+
+ ;# now break packet apart
+ ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
+ unpack("C2n5a".($len_pkt-12),$pkt);
+ $data=substr($data,$[,$count);
+ if ((($len_pkt - 12) - &pad($count,4)) >= 12)
+ {
+ ;# looks like an authenticator
+ ($auth_keyid,$auth_cksum) =
+ unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
+ $STAT_auth++;
+ ;# no checking of auth_cksum (yet ?)
+ }
+
+ if (&pkt_VN($li_vn_mode) != $NTP_version)
+ {
+ $STAT_bad_version++;
+ return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored");
+ }
+
+ if (&pkt_MODE($li_vn_mode) != $ctrl_mode)
+ {
+ $STAT_bad_mode++;
+ return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored");
+ }
+
+ ;# handle single fragment fast
+ if ($offset == 0 && &pkt_M($r_e_m_op) == 0)
+ {
+ $STAT_single_frag++;
+ if (&pkt_E($r_e_m_op))
+ {
+ $STAT_err_pkt++;
+ return (&error_status($status),
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ else
+ {
+ return ("",
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ }
+ else
+ {
+ ;# fragment - set up local name space
+ $id = "$from$seq".&pkt_OP($r_e_m_op);
+ $ID{$id} = 1;
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ $STAT_frag++;
+
+ $lastseen = 1 if !&pkt_M($r_e_m_op);
+ if (!defined(%FRAGS))
+ {
+ (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ $FRAGS{$offset} = $data;
+ ;# save other info
+ @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
+ }
+ else
+ {
+ (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ ;# add frag to previous - combine on the fly
+ if (defined($FRAGS{$offset}))
+ {
+ $STAT_dup_frag++;
+ return ("ERROR","duplicate fragment at $offset seq=$seq");
+ }
+
+ $FRAGS{$offset} = $data;
+
+ undef($loff);
+ foreach $off (sort numerical keys(%FRAGS))
+ {
+ next unless defined($FRAGS{$off});
+ if (defined($loff) &&
+ ($loff + length($FRAGS{$loff})) == $off)
+ {
+ $FRAGS{$loff} .= $FRAGS{$off};
+ delete $FRAGS{$off};
+ last;
+ }
+ $loff = $off;
+ }
+
+ ;# return packet if all frags arrived
+ ;# at most two frags with possible padding ???
+ if ($lastseen && defined($FRAGS{0}) &&
+ scalar(@x=sort numerical keys(%FRAGS)) <= 2 &&
+ (length($FRAGS{0}) + 8) > $x[$[+1])
+ {
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""),
+ $FRAGS{0},@FRAGS);
+ &pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ &main'clear_timeout($id);
+ return @x;
+ }
+ else
+ {
+ &main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
+ }
+ }
+ return (undef);
+ }
+}
+
+sub handle_packet_timeout
+{
+ local($id) = @_;
+ local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]);
+
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"),
+ $FRAGS{0},@FRAGS[$[ .. $[+4]);
+ $STAT_frag_timeout++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ return @x;
+}
+
+
+sub pad
+{
+ return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);
+}
+
+1;
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntploopstat b/usr.sbin/xntpd/scripts/monitoring/ntploopstat
new file mode 100755
index 0000000..75cdff2
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntploopstat
@@ -0,0 +1,457 @@
+#!/local/bin/perl -w--*-perl-*-
+;#
+;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp
+;#
+;# Poll NTP server using NTP mode 7 loopinfo request.
+;# Log info and timestamp to file for processing by ntploopwatch.
+;#
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#################################################################
+;#
+;# The format written to the logfile is the same as used by xntpd
+;# for the loopstats file.
+;# This script however allows to gather loop filter statistics from
+;# remote servers where you do not have access to the loopstats logfile.
+;#
+;# Please note: Communication delays affect the accuracy of the
+;# timestamps recorded. Effects from these delays will probably
+;# not show up, as timestamps are recorded to the second only.
+;# (Should have implemented &gettimeofday()..)
+;#
+
+$0 =~ s!^.*/([^/]+)$!\1!; # beautify script name
+
+$ntpserver = 'localhost'; # default host to poll
+$delay = 60; # default sampling rate
+ ;# keep it shorter than minpoll (=64)
+ ;# to get all values
+
+require "ctime.pl";
+;# handle bug in early ctime distributions
+$ENV{'TZ'} = 'MET' unless defined($ENV{'TZ'}) || $] > 4.010;
+
+if (defined(@ctime'MoY))
+{
+ *MonthName = *ctime'MoY;
+}
+else
+{
+ @MonthName = ('Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec');
+}
+
+;# this routine can be redefined to point to syslog if necessary
+sub msg
+{
+ return unless $verbose;
+
+ print STDERR "$0: ";
+ printf STDERR @_;
+}
+
+;#############################################################
+;#
+;# process command line
+$usage = <<"E-O-S";
+
+usage:
+ $0 [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+E-O-S
+
+while($_ = shift)
+{
+ /^-v(\d*)$/ && ($verbose=($1 eq '') ? 1 : $1,1) && next;
+ /^-d(\d*)$/ &&
+ do {
+ ($1 ne '') && ($delay = $1,1) && next;
+ @ARGV || die("$0: delay value missing after -d\n$usage");
+ $delay = shift;
+ ($delay >= 0) || die("$0: bad delay value \"$delay\"\n$usage");
+ next;
+ };
+ /^-l$/ &&
+ do {
+ @ARGV || die("$0: logfile missing after -l\n$usage");
+ $logfile = shift;
+ next;
+ };
+ /^-t(\d*(\.\d*)?)$/ &&
+ do {
+ ($1 ne '') && ($timeout = $1,1) && next;
+ @ARGV || die("$0: timeout value missing after -t\n$usage\n");
+ $timeout = shift;
+ ($timeout > 0) ||
+ die("$0: bad timeout value \"$timeout\"\n$usage");
+ next;
+ };
+
+ /^-/ && die("$0: unknown option \"$_\"\n$usage");
+
+ ;# any other argument is server to poll
+ $ntpserver = $_;
+ last;
+}
+
+if (@ARGV)
+{
+ warn("unexpected arguments: ".join(" ",@ARGV).".\n");
+ die("$0: too many servers specified\n$usage");
+}
+
+;# logfile defaults to include server name
+;# The name of the current month is appended and
+;# the file is opened and closed for each sample.
+;#
+$logfile = "loopstats:$ntpserver." unless defined($logfile);
+$timeout = 12.0 unless defined($timeout); # wait $timeout seconds for reply
+
+$MAX_FAIL = 60; # give up after $MAX_FAIL failed polls
+
+
+$MJD_1970 = 40587;
+
+if (eval 'require "syscall.ph";')
+{
+ if (defined(&SYS_gettimeofday))
+ {
+ ;# assume standard
+ ;# gettimeofday(struct timeval *tp,struct timezone *tzp)
+ ;# syntax for gettimeofday syscall
+ ;# tzp = NULL -> undef
+ ;# tp = (long,long)
+ eval 'sub time { local($tz) = pack("LL",0,0);
+ (&msg("gettimeofday failed: $!\n"),
+ return (time))
+ unless syscall(&SYS_gettimeofday,$tz,undef) == 0;
+ local($s,$us) = unpack("LL",$tz);
+ return $s + $us/1000000; }';
+ local($t1,$t2,$t3);
+ $t1 = time;
+ eval '$t2 = &time;';
+ $t3 = time;
+ die("$0: gettimeofday failed: $@.\n") if defined($@) && $@;
+ die("$0: gettimeofday inconsistency time=$t1,gettimeofday=$t2,time=$t2\n")
+ if (int($t1) != int($t2) && int($t3) != int($t2));
+ &msg("Using gettimeofday for timestamps\n");
+ }
+ else
+ {
+ warn("No gettimeofday syscall found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+ }
+}
+else
+{
+ warn("No syscall.ph file found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+}
+
+
+;#------------------+
+;# from ntp_request.h
+;#------------------+
+
+;# NTP mode 7 packet format:
+;# Byte 1: ResponseBit MoreBit Version(3bit) Mode(3bit)==7
+;# Byte 2: AuthBit Sequence # - 0 - 127 see MoreBit
+;# Byte 3: Implementation #
+;# Byte 4: Request Code
+;#
+;# Short 1: Err(3bit) NumItems(12bit)
+;# Short 2: MBZ(3bit)=0 DataItemSize(12bit)
+;# 0 - 500 byte Data
+;# if AuthBit is set:
+;# Long: KeyId
+;# 2xLong: AuthCode
+
+;#
+$IMPL_XNTPD = 2;
+$REQ_LOOP_INFO = 8;
+
+
+;# request packet for REQ_LOOP_INFO:
+;# B1: RB=0 MB=0 V=2 M=7
+;# B2: S# = 0
+;# B3: I# = IMPL_XNTPD
+;# B4: RC = REQ_LOOP_INFO
+;# S1: E=0 NI=0
+;# S2: MBZ=0 DIS=0
+;# data: 32 byte 0 padding
+;# 8byte timestamp if encryption, 0 padding otherwise
+$loopinfo_reqpkt =
+ pack("CCCC nn x32 x8", 0x17, 0, $IMPL_XNTPD, $REQ_LOOP_INFO, 0, 0);
+
+;# ignore any auth data in packets
+$loopinfo_response_size =
+ 1+1+1+1+2+2 # header size like request pkt
+ + 8 # l_fp last_offset
+ + 8 # l_fp drift_comp
+ + 4 # u_long compliance
+ + 4 # u_long watchdog_timer
+ ;
+$loopinfo_response_fmt = "C4n2N2N2NN";
+$loopinfo_response_fmt_v2 = "C4n2N2N2N2N";
+
+;#
+;# prepare connection to server
+;#
+
+;# workaround for broken socket.ph on dynix_ptx
+eval 'sub INTEL {1;}' unless defined(&INTEL);
+eval 'sub ATT {1;}' unless defined(&ATT);
+
+require "sys/socket.ph";
+
+require 'netinet/in.ph';
+
+;# if you do not have netinet/in.ph enable the following lines
+;#eval 'sub INADDR_ANY { 0x00000000; }' unless defined(&INADDR_ANY);
+;#eval 'sub IPPRORO_UDP { 17; }' unless defined(&IPPROTO_UDP);
+
+if ($ntpserver =~ /^((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)$/)
+{
+ local($a,$b,$c,$d) = ($1,$3,$5,$7);
+ $a = oct($a) if defined($2);
+ $b = oct($b) if defined($4);
+ $c = oct($c) if defined($6);
+ $d = oct($d) if defined($8);
+ $server_addr = pack("C4", $a,$b,$c,$d);
+
+ $server_mainname
+ = (gethostbyaddr($server_addr,&AF_INET))[$[] || $ntpserver;
+}
+else
+{
+ ($server_mainname,$server_addr)
+ = (gethostbyname($ntpserver))[$[,$[+4];
+
+ die("$0: host \"$ntpserver\" is unknown\n")
+ unless defined($server_addr);
+}
+&msg ("Address of server \"$ntpserver\" is \"%d.%d.%d.%d\"\n",
+ unpack("C4",$server_addr));
+
+$proto_udp = (getprotobyname('udp'))[$[+2] || &IPPROTO_UDP;
+
+$ntp_port =
+ (getservbyname('ntp','udp'))[$[+2] ||
+ (warn "Could not get port number for service \"ntp/udp\" using 123\n"),
+ ($ntp_port=123);
+
+;#
+0 && &SOCK_DGRAM; # satisfy perl -w ...
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) ||
+ die("Cannot open socket: $!\n");
+
+bind(S, pack("S n N x8", &AF_INET, 0, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+
+&msg("Listening at address %d.%d.%d.%d port %d\n",
+ unpack("C4",$my_addr), $my_port);
+
+$server_inaddr = pack("Sna4x8", &AF_INET, $ntp_port, $server_addr);
+
+;############################################################
+;#
+;# the main loop:
+;# send request
+;# get reply
+;# wait til next sample time
+
+undef($lasttime);
+$lostpacket = 0;
+
+while(1)
+{
+ $stime = &time;
+
+ &msg("Sending request $stime...\n");
+
+ $ret = send(S,$loopinfo_reqpkt,0,$server_inaddr);
+
+ if (! defined($ret) || $ret < length($loopinfo_reqpkt))
+ {
+ warn("$0: send failed ret=($ret): $!\n");
+ $fail++;
+ next;
+ }
+
+ &msg("Waiting for reply...\n");
+
+ $mask = ""; vec($mask,fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ if (! defined($ret))
+ {
+ warn("$0: select failed: $!\n");
+ $fail++;
+ next;
+ }
+ elsif ($ret == 0)
+ {
+ warn("$0: request to $ntpserver timed out ($timeout seconds)\n");
+ ;# do not count this event as failure
+ ;# it usually this happens due to dropped udp packets on noisy and
+ ;# havily loaded lines, so just try again;
+ $lostpacket = 1;
+ next;
+ }
+
+ &msg("Receiving reply...\n");
+
+ $len = 520; # max size of a mode 7 packet
+ $reply = ""; # just make it defined for -w
+ $ret = recv(S,$reply,$len,0);
+
+ if (!defined($ret))
+ {
+ warn("$0: recv failed: $!\n");
+ $fail++;
+ next;
+ }
+
+ $etime = &time;
+ &msg("Received at\t$etime\n");
+
+ ;#$time = ($stime + $etime) / 2; # symmetric delay assumed
+ $time = $etime; # the above assumption breaks for X25
+ ;# so taking etime makes timestamps be a
+ ;# little late, but keeps them increasing
+ ;# monotonously
+
+ &msg(sprintf("Reply from %d.%d.%d.%d took %f seconds\n",
+ (unpack("SnC4",$ret))[$[+2 .. $[+5], ($etime - $stime)));
+
+ if ($len < $loopinfo_response_size)
+ {
+ warn("$0: short packet ($len bytes) received ($loopinfo_response_size bytes expected\n");
+ $fail++;
+ next;
+ }
+
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl,$watchdog)
+ = unpack($loopinfo_response_fmt,$reply);
+
+ ;# check reply
+ if (($s1 >> 12) != 0) # error !
+ {
+ die("$0: got error reply ".($s1>>12)."\n");
+ }
+ if (($b1 != 0x97 && $b1 != 0x9f) || # Reply NotMore V=2 M=7
+ ($b2 != 0 && $b2 != 0x80) || # S=0 Auth no/yes
+ $b3 != $IMPL_XNTPD || # ! IMPL_XNTPD
+ $b4 != $REQ_LOOP_INFO || # Ehh.. not loopinfo reply ?
+ $s1 != 1 || # ????
+ ($s2 != 24 && $s2 != 28) #
+ )
+ {
+ warn("$0: Bad/unexpected reply from server:\n");
+ warn(" \"".unpack("H*",$reply)."\"\n");
+ warn(" ".sprintf("b1=%x b2=%x b3=%x b4=%x s1=%d s2=%d\n",
+ $b1,$b2,$b3,$b4,$s1,$s2));
+ $fail++;
+ next;
+ }
+ elsif ($s2 == 28)
+ {
+ ;# seems to be a version 2 xntpd
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl_i,$compl_f,$watchdog)
+ = unpack($loopinfo_response_fmt_v2,$reply);
+ $compl = &lfptoa($compl_i, $compl_f);
+ }
+
+ $time -= $watchdog;
+
+ $offset = &lfptoa($offset_i, $offset_f);
+ $drift = &lfptoa($drift_i, $drift_f);
+
+ &log($time,$offset,$drift,$compl) && ($fail = 0);;
+}
+continue
+{
+ die("$0: Too many failures - terminating\n") if $fail > $MAX_FAIL;
+ &msg("Sleeping " . ($lostpacket ? ($delay / 2) : $delay) . " seconds...\n");
+
+ sleep($lostpacket ? ($delay / 2) : $delay);
+ $lostpacket = 0;
+}
+
+sub log
+{
+ local($time,$offs,$freq,$cmpl) = @_;
+ local($y,$m,$d);
+ local($fname,$suff) = ($logfile);
+
+
+ ;# silently drop sample if distance to last sample is too low
+ if (defined($lasttime) && ($lasttime + 2) >= $time)
+ {
+ &msg("Dropped packet - old sample\n");
+ return 1;
+ }
+
+ ;# $suff determines which samples end up in the same file
+ ;# could have used $year (;-) or WeekOfYear, DayOfYear,....
+ ;# Change it to your suit...
+
+ ($d,$m,$y) = (localtime($time))[$[+3 .. $[+5];
+ $suff = sprintf("%04d%02d%02d",$y+1900,$m+1,$d);
+ $fname .= $suff;
+ if (!open(LOG,">>$fname"))
+ {
+ warn("$0: open($fname) failed: $!\n");
+ $fail++;
+ return 0;
+ }
+ else
+ {
+ ;# file format
+ ;# MJD seconds offset drift compliance
+ printf LOG ("%d %.3lf %.8lf %.7lf %d\n",
+ int($time/86400)+$MJD_1970,
+ $time - int($time/86400) * 86400,
+ $offs,$freq,$cmpl);
+ close(LOG);
+ $lasttime = $time;
+ }
+ return 1;
+}
+
+;# see ntp_fp.h to understand this
+sub lfptoa
+{
+ local($i,$f) = @_;
+ local($sign) = 1;
+
+
+ if ($i & 0x80000000)
+ {
+ if ($f == 0)
+ {
+ $i = -$i;
+ }
+ else
+ {
+ $f = -$f;
+ $i = ~$i;
+ $i += 1; # 2s complement
+ }
+ $sign = -1;
+ ;#print "NEG: $i $f\n";
+ }
+ else
+ {
+ ;#print "POS: $i $f\n";
+ }
+ ;# unlike xntpd I have perl do the dirty work.
+ ;# Using floats here may affect precision, but
+ ;# currently these bits aren't significant anyway
+ return $sign * ($i + $f/2**32);
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntploopwatch b/usr.sbin/xntpd/scripts/monitoring/ntploopwatch
new file mode 100755
index 0000000..655ed71
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntploopwatch
@@ -0,0 +1,1631 @@
+#!/local/bin/perl -w--*-perl-*-
+;#
+;# ntploopwatch,v 3.1 1993/07/06 01:09:13 jbj Exp
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!\1!;
+$F = ' ' x length($0);
+$|=1;
+
+$ENV{'SHELL'} = '/bin/sh'; # use bourne shell
+
+undef($config);
+undef($workdir);
+undef($PrintIt);
+undef($samples);
+undef($StartTime);
+undef($EndTime);
+($a,$b) if 0; # keep -w happy
+$usage = <<"E-O-P";
+usage:
+ to watch statistics permanently:
+ $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+ $F [-h <hostname>]
+
+ to get a single print out specify also
+ $F -P[<printer>] [-s<samples>]
+ $F [-S <start-time>] [-E <end-time>]
+ $F [-Y <MaxOffs>] [-y <MinOffs>]
+
+If You like long option names, You can use:
+ -help
+ -c +config
+ -d +directory
+ -h +host
+ -v +verbose[=<level>]
+ -P +printer[=<printer>]
+ -s +samples[=<samples>]
+ -S +starttime
+ -E +endtime
+ -Y +maxy
+ -y +miny
+
+If <printer> contains a '/' (slash character) output is directed to
+a file of this name instead of delivered to a printer.
+E-O-P
+
+;# add directory to look for lr.pl and timelocal.pl (in front of current list)
+unshift(@INC,"/src/NTP/v3/xntp/monitoring");
+
+require "lr.pl"; # linear regresion routines
+
+$MJD_1970 = 40587; # from ntp.h (V3)
+$RecordSize = 48; # usually a line fits into 42 bytes
+$MinClip = 0.12; # clip Y scales with greater range than this
+
+;# largest extension of Y scale from mean value, factor for standart deviation
+$FuzzLow = 2; # for side closer to zero
+$FuzzBig = 1; # for side farther from zero
+
+require "ctime.pl";
+require "timelocal.pl";
+;# early distributions of ctime.pl had a bug
+$ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
+if (defined(@ctime'MoY))
+{
+ *Month=*ctime'MoY;
+ *Day=*ctime'DoW;
+}
+else
+{
+ @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+ @Day = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+}
+;# max number of days per month
+@MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+
+;# config settable parameters
+$delay = 60;
+$srcprefix = "./var\@\$STATHOST/loopstats.";
+$showoffs = 1;
+$showfreq = 1;
+$showcmpl = 0;
+$showoreg = 0;
+$showfreg = 0;
+undef($timebase);
+undef($freqbase);
+undef($cmplscale);
+undef($MaxY);
+undef($MinY);
+$deltaT = 512; # indicate sample data gaps greater than $deltaT seconds
+$verbose = 1;
+
+while($_ = shift(@ARGV))
+{
+ (/^[+-]help$/) && die($usage);
+
+ (/^-c$/ || /^\+config$/) &&
+ (@ARGV || die($usage), $config = shift(@ARGV), next);
+
+ (/^-d$/ || /^\+directory$/) &&
+ (@ARGV || die($usage), $workdir = shift(@ARGV), next);
+
+ (/^-h$/ || /^\+host$/) &&
+ (@ARGV || die($usage), $STATHOST = shift, next);
+
+ (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
+ ($verbose=($1 eq "") ? 1 : $1, next);
+
+ (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
+ ($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
+
+ (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
+ (($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
+
+ (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
+ (@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
+
+ (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
+ (@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
+
+ (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
+ (@ARGV || die($usage), $MaxY = shift, next);
+
+ (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
+ (@ARGV || die($usage), $MinY = shift, next);
+
+ die("$0: unexpected argument \"$_\"\n$usage");
+}
+
+if (defined($workdir))
+{
+ chdir($workdir) ||
+ die("$0: failed to change working dir to \"$workdir\": $!\n");
+}
+
+$PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
+
+if (!defined($PrintIt))
+{
+ defined($samples) &&
+ print "WARNING: your samples value may be shadowed by config file settings\n";
+ defined($StartTime) &&
+ print "WARNING: your StartTime value may be shadowed by config file settings\n";
+ defined($EndTime) &&
+ print "WARNING: your EndTime value may be shadowed by config file settings\n";
+ defined($MaxY) &&
+ print "WARNING: your MaxY value may be shadowed by config file settings\n";
+ defined($MinY) &&
+ print "WARNING: your MinY value may be shadowed by config file settings\n";
+
+ ;# check operating environment
+ ;#
+ ;# gnuplot usually has X support
+ ;# I vaguely remember there was one with sunview support
+ ;#
+ ;# If Your plotcmd can display graphics using some other method
+ ;# (Tek window,..) fix the following test
+ ;# (or may be, just disable it)
+ ;#
+ !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
+ die("Need window system to monitor statistics\n");
+}
+
+;# configuration file
+$config = "loopwatch.config" unless defined($config);
+($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!\1!
+ unless defined($STATHOST);
+($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/\1/;
+
+$srcprefix =~ s/\$STATHOST/$STATHOST/g;
+
+;# plot command
+@plotcmd=("gnuplot",
+ '-title', "Ntp loop filter statistics $STATHOST",
+ '-name', "NtpLoopWatch_$STATTAG");
+$tmpfile = "/tmp/ntpstat.$$";
+
+;# other variables
+$doplot = ""; # assembled command for @plotcmd to display plot
+undef($laststat);
+
+;# plot value ranges
+undef($mintime);
+undef($maxtime);
+undef($minoffs);
+undef($maxoffs);
+undef($minfreq);
+undef($maxfreq);
+undef($mincmpl);
+undef($maxcmpl);
+undef($miny);
+undef($maxy);
+
+;# stop operation if plot command dies
+sub sigchld
+{
+ local($pid) = wait;
+ unlink($tmpfile);
+ warn(sprintf("%s: %s died: exit status: %d signal %d\n",
+ $0,
+ (defined($Plotpid) && $Plotpid == $pid)
+ ? "plotcmd" : "unknown child $pid",
+ $?>>8,$? & 0xff)) if $?;
+ exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
+}
+&sigchld if 0;
+$SIG{'CHLD'} = "sigchld";
+$SIG{'CLD'} = "sigchld";
+
+sub abort
+{
+ unlink($tmpfile);
+ defined($Plotpid) && kill('TERM',$Plotpid);
+ die("$0: received signal SIG$_[$[] - exiting\n");
+}
+&abort if 0; # make -w happy - &abort IS used
+$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
+
+;#
+sub abs
+{
+ ($_[$[] < 0) ? -($_[$[]) : $_[$[];
+}
+
+;#####################
+;# start of real work
+
+print "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
+
+$Plotpid = open(PLOT,"|-");
+select((select(PLOT),$|=1)[$[]); # make PLOT line bufferd
+
+defined($Plotpid) ||
+ die("$0: failed to start plot command: $!\n");
+
+unless ($Plotpid)
+{
+ ;# child == plot command
+ close(STDOUT);
+ open(STDOUT,">&STDERR") ||
+ die("$0: failed to redirect STDOUT of plot command: $!\n");
+
+ print STDOUT "plot command running as $$\n";
+
+ exec @plotcmd;
+ die("$0: failed to exec (@plotcmd): $!\n");
+ exit(1); # in case ...
+}
+
+sub read_config
+{
+ local($at) = (stat($config))[$[+9];
+ local($_,$c,$v);
+
+ (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
+ return if (defined($laststat) && ($laststat == $at));
+ $laststat = $at;
+
+ print "reading configuration from \"$config\"\n" if $verbose;
+
+ open(CF,"<$config") ||
+ (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
+ return);
+ while(<CF>)
+ {
+ chop;
+ s/^([^\#]*[^\#\s]?)\s*\#.*$//;
+ next if /^\s*$/;
+
+ s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/\1=\2/;
+
+ ($c,$v) = split(/=/,$_,2);
+ print "processing \"$c=$v\"\n" if $verbose > 3;
+ ($c eq "delay") && ($delay = $v,1) && next;
+ ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
+ ($samples = $v,1) && next;
+ ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
+ && next;
+ ($c eq 'showoffs') &&
+ ($showoffs = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showfreq') &&
+ ($showfreq = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showcmpl') &&
+ ($showcmpl = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showoreg') &&
+ ($showoreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showfreg') &&
+ ($showfreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+
+ ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
+
+ ($c eq 'freqbase' ||
+ $c eq 'cmplscale') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq 'dynamic')
+ {
+ eval "undef(\$$c);";
+ }
+ else
+ {
+ eval "\$$c = \$v;";
+ }
+ next;
+ };
+ ($c eq 'timebase') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq "dynamic")
+ {
+ undef($timebase);
+ }
+ else
+ {
+ $timebase=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'EndTime') &&
+ do {
+ next if defined($EndTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($EndTime);
+ }
+ else
+ {
+ $EndTime=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'StartTime') &&
+ do {
+ next if defined($StartTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($StartTime);
+ }
+ else
+ {
+ $StartTime=&date_time_spec2seconds($v);
+ }
+ };
+
+ ($c eq 'MaxY') &&
+ do {
+ next if defined($MaxY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MaxY);
+ }
+ else
+ {
+ $MaxY=$v;
+ }
+ };
+
+ ($c eq 'MinY') &&
+ do {
+ next if defined($MinY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MinY);
+ }
+ else
+ {
+ $MinY=$v;
+ }
+ };
+
+ ($c eq 'deltaT') &&
+ do {
+ if (!defined($v) || $v eq "")
+ {
+ undef($deltaT);
+ }
+ else
+ {
+ $deltaT = $v;
+ }
+ next;
+ };
+ ($c eq 'verbose') && ! defined($PrintIt) &&
+ do {
+ if (!defined($v) || $v == 0)
+ {
+ $verbose = 0;
+ }
+ else
+ {
+ $verbose = $v;
+ }
+ next;
+ };
+ ;# otherwise: silently ignore unrecognized config line
+ }
+ close(CF);
+ ;# set show defaults when nothing selected
+ $showoffs = $showfreq = $showcmpl = 1
+ unless $showoffs || $showfreq || $showcmpl;
+ if ($verbose > 3)
+ {
+ print "new configuration:\n";
+ print " delay\t= $delay\n";
+ print " samples\t= $samples\n";
+ print " srcprefix\t= $srcprefix\n";
+ print " showoffs\t= $showoffs\n";
+ print " showfreq\t= $showfreq\n";
+ print " showcmpl\t= $showcmpl\n";
+ print " showoreg\t= $showoreg\n";
+ print " showfreg\t= $showfreg\n";
+ printf " timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
+ printf " freqbase\t= %s\n",defined($freqbase) ?"$freqbase":"dynamic";
+ printf " cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
+ printf " StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
+ printf " EndTime\t= %s", defined($EndTime) ? &ctime($EndTime):"none\n";
+ printf " MaxY\t= %s",defined($MaxY)? $MaxY :"none\n";
+ printf " MinY\t= %s",defined($MinY)? $MinY :"none\n";
+ print " verbose\t= $verbose\n";
+ }
+print "configuration file read\n" if $verbose > 2;
+}
+
+sub make_doplot
+{
+ local($c) = ("");
+ local($fmt)
+ = ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
+ local($regfmt)
+ = ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
+
+ $doplot = " set title 'NTP loopfilter statistics for $STATHOST " .
+ "(last $LastCnt samples from $srcprefix*)'\n";
+
+ local($xts,$xte,$i,$t);
+
+ local($s,$c) = ("");
+
+ ;# number of integral seconds to get at least 12 tic marks on x axis
+ $t = int(($maxtime - $mintime) / 12 + 0.5);
+ $t = 1 unless $t; # prevent $t to be zero
+ foreach $i (30,
+ 60,5*60,15*60,30*60,
+ 60*60,2*60*60,6*60*60,12*60*60,
+ 24*60*60,48*60*60)
+ {
+ last if $t < $i;
+ $t = $t - ($t % $i);
+ }
+ print "time label resolution: $t seconds\n" if $verbose > 1;
+
+ ;# make gnuplot use wall clock time labels instead of NTP seconds
+ for ($c="", $i = $mintime - ($mintime % $t);
+ $i <= $maxtime + $t;
+ $i += $t, $c=",")
+ {
+ $s .= $c;
+ ((int($i / $t) % 2) &&
+ ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
+ (($t <= 60) &&
+ ($s .= sprintf("'%d:%02d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1,$[+0],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 2*60*60) &&
+ ($s .= sprintf("'%d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 12*60*60) &&
+ ($s .= sprintf("'%s %d:00' %lf",
+ $Day[(localtime($i))[$[+6]],
+ (localtime($i))[$[+2],
+ ($i - $LastTimeBase)/3600)))
+ || ($s .= sprintf("'%d.%d-%d:00' %lf",
+ (localtime($i))[$[+3,$[+4,$[+2],
+ ($i - $LastTimeBase)/3600));
+ }
+ $doplot .= "set xtics ($s)\n";
+
+ chop($xts = &ctime($mintime));
+ chop($xte = &ctime($maxtime));
+ $doplot .= "set xlabel 'Start: $xts -- Time Scale -- End: $xte'\n";
+ $doplot .= "set yrange [" ;
+ $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
+ $doplot .= ':';
+ $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
+ $doplot .= "]\n";
+
+ $doplot .= " plot";
+ $c = "";
+ $showoffs &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,2,
+ "offset",
+ $minoffs,$maxoffs,
+ "[ms]"),
+ $c = ",");
+ $showcmpl &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,4,
+ "compliance" .
+ (&abs($LastCmplScale) > 1
+ ? " / $LastCmplScale"
+ : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
+ $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
+ ""),
+ $c = ",");
+ $showfreq &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,3,
+ "frequency" .
+ ($LastFreqBase > 0
+ ? " - $LastFreqBaseString"
+ : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
+ $minfreq * $FreqScale - $LastFreqBase,
+ $maxfreq * $FreqScale - $LastFreqBase,
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $showoreg && $showoffs &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('offs'),&lr_A('offs'),
+ "offset ",
+ &lr_B('offs'),
+ ((&lr_A('offs')) < 0 ? '-' : '+'),
+ &abs(&lr_A('offs')), &lr_r('offs'),
+ "[ms]"),
+ $c = ",");
+ $showfreg && $showfreq &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('freq') * $FreqScale,
+ (&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase,
+ "frequency",
+ &lr_B('freq') * $FreqScale,
+ ((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
+ &abs((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase),
+ &lr_r('freq'),
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $doplot .= "\n";
+}
+
+%F_key = ();
+%F_name = ();
+%F_size = ();
+%F_mtime = ();
+%F_first = ();
+%F_last = ();
+
+sub genfile
+{
+ local($cnt,$in,$out,@fpos) = @_;
+
+ local(@F,@t,$t,$lastT) = ();
+ local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
+ local($lm,$l,@f);
+
+ local($sdir,$sname);
+
+ ;# allocate some storage for the tables
+ ;# otherwise realloc may get into troubles
+ if (defined($StartTime) && defined($EndTime))
+ {
+ $l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
+ }
+ else
+ {
+ $l = $cnt + 10;
+ }
+ print "preextending arrays to $l entries\n" if $verbose > 2;
+ $#break = $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
+ $#time = $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
+ $#offs = $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
+ $#freq = $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
+ $#cmpl = $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
+ $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
+ $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
+ ;# now reduce size again
+ $#break = $[ - 1;
+ $#time = $[ - 1;
+ $#offs = $[ - 1;
+ $#freq = $[ - 1;
+ $#cmpl = $[ - 1;
+ $#loffset = $[ - 1;
+ $#filekey = $[ - 1;
+ print "memory allocation ready\n" if $verbose > 2;
+ sleep(3) if $verbose > 1;
+
+ if (index($in,"/") < $[)
+ {
+ $sdir = ".";
+ $sname = $in;
+ }
+ else
+ {
+ ($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
+ $sname = "" unless defined($sname);
+ }
+
+ if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
+ grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
+
+ {
+ print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
+ if $verbose > 1;
+
+ ;# rescan directory on changes
+ $Lsdir = $sdir;
+ $Ltime = (stat($sdir))[$[+9];
+ </X{> if 0; # dummy line - calm down my formatter
+ local(@newfiles) = < ${in}*[0-9] >;
+ local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
+
+ foreach $name (@newfiles)
+ {
+ ($st_dev,$st_ino,$st_size,$st_mtime) =
+ (stat($name))[$[,$[+1,$[+7,$[+9];
+ $modified = 0;
+ $key = sprintf("%lx|%lu", $st_dev, $st_ino);
+
+ print "candidate file \"$name\"",
+ (defined($st_dev) ? "" : " failed: $!"),"\n"
+ if $verbose > 2;
+
+ if (! defined($F_key{$name}) || $F_key{$name} ne $key)
+ {
+ $F_key{$name} = $key;
+ $modified++;
+ }
+ if (!defined($F_name{$key}) || $F_name{$key} != $name)
+ {
+ $F_name{$key} = $name;
+ $modified++;
+ }
+ if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
+ {
+ $F_size{$key} = $st_size;
+ $modified++;
+ }
+ if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
+ {
+ $F_mtime{$key} = $st_mtime;
+ $modified++;
+ }
+ if ($modified)
+ {
+ print "new data \"$name\" key: $key;\n" if $verbose > 1;
+ print " size: $st_size; mtime: $st_mtime;\n"
+ if $verbose > 1;
+ $F_last{$key} = $F_first{$key} = $st_mtime;
+ $F_first{$key}--; # prevent zero divide later on
+ ;# now compute derivated attributes
+ open(IN, "<$name") ||
+ do {
+ warn "$0: failed to open \"$name\": $!";
+ next;
+ };
+
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_first{$key} = $t;
+ print "\tfound first entry: $t ",&ctime($t)
+ if $verbose > 4;
+ last;
+ }
+ seek(IN,
+ ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
+ 0);
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_last{$key} = $t;
+ $_ = <IN>;
+ print "\tfound last entry: $t ", &ctime($t)
+ if $verbose > 4 && ! defined($_);
+ last unless defined($_);
+ redo;
+ ;# Ok, calm down...
+ ;# using $_ = <IN> in conjunction with redo
+ ;# is semantically equivalent to the while loop, but
+ ;# I needed a one line look ahead and this solution
+ ;# was what I thought of first
+ ;# and.. If you do not like it dont look
+ }
+ close(IN);
+ print(" first: ",$F_first{$key},
+ " last: ",$F_last{$key},"\n") if $verbose > 1;
+ }
+ }
+ ;# now reclaim memory used for files no longer referenced ...
+ local(%Names);
+ grep($Names{$_} = 1,@newfiles);
+ foreach (keys %F_key)
+ {
+ next if defined($Names{$_});
+ delete $F_key{$_};
+ $verbose > 2 && print "no longer referenced: \"$_\"\n";
+ }
+ %Names = ();
+
+ grep($Names{$_} = 1,values(%F_key));
+ foreach (keys %F_name)
+ {
+ next if defined($Names{$_});
+ delete $F_name{$_};
+ $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
+ }
+ foreach (keys %F_size)
+ {
+ next if defined($Names{$_});
+ delete $F_size{$_};
+ $verbose > 2 && print "unref size($_)\n";
+ }
+ foreach (keys %F_mtime)
+ {
+ next if defined($Names{$_});
+ delete $F_mtime{$_};
+ $verbose > 2 && print "unref mtime($_)\n";
+ }
+ foreach (keys %F_first)
+ {
+ next if defined($Names{$_});
+ delete $F_first{$_};
+ $verbose > 2 && print "unref first($_)\n";
+ }
+ foreach (keys %F_last)
+ {
+ next if defined($Names{$_});
+ delete $F_last{$_};
+ $verbose > 2 && print "unref last($_)\n";
+ }
+ ;# create list sorted by time
+ @F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
+ if ($verbose > 1)
+ {
+ print "Resulting file list:\n";
+ foreach (@F_files)
+ {
+ print "\t$_\t$F_name{$_}\n";
+ }
+ }
+ }
+
+ printf("processing %s; output \"$out\" (%d input files)\n",
+ ((defined($StartTime) && defined($EndTime))
+ ? "time range"
+ : (defined($StartTime) ? "$cnt samples from StartTime" :
+ (defined($EndTime) ? "$cnt samples to EndTime" :
+ "last $cnt samples"))),
+ scalar(@F_files))
+ if $verbose > 1;
+
+ ;# open output file - will be input for plotcmd
+ open(OUT,">$out") ||
+ do {
+ warn("$0: cannot create \"$out\": $!\n");
+ };
+
+ @f = @F_files;
+ if (defined($StartTime))
+ {
+ while (@f && ($F_last{$f[$[]} < $StartTime))
+ {
+ print("shifting ", $F_name{$f[$[]},
+ " last: ", $F_last{$f[$[]},
+ " < StartTime: $StartTime\n")
+ if $verbose > 3;
+ shift(@f);
+ }
+
+
+ }
+ if (defined($EndTime))
+ {
+ while (@f && ($F_first{$f[$#f]} > $EndTime))
+ {
+ print("popping ", $F_name{$f[$#f]},
+ " first: ", $F_first{$f[$#f]},
+ " > EndTime: $EndTime\n")
+ if $verbose > 3;
+ pop(@f);
+ }
+ }
+
+ if (@f)
+ {
+ if (defined($StartTime))
+ {
+ print "guess start according to StartTime ($StartTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'start')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('start', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('start' , $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ if ($StartTime <= $F_first{$f[$[]})
+ {
+ $fpos[$[+2] = 0;
+ }
+ else
+ {
+ $fpos[$[+2] =
+ int($F_size{$f[$[]} *
+ (($StartTime - $F_first{$f[$[]})/
+ ($F_last{$f[$[]} - $F_first{$f[$[]})));
+ $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
+ ? 0 : $fpos[$[+2] - 2 * $RecordSize;
+ ;# anyway as the data may contain "time holes"
+ ;# our heuristics may baldly fail
+ ;# so just start at 0
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ elsif (defined($EndTime))
+ {
+ print "guess starting point according to EndTime ($EndTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'end')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+ if ($EndTime < $F_last{$x[$[]})
+ {
+ ;# last file will only be used partially
+ $s = int($F_size{$x[$[]} *
+ (($EndTime - $F_first{$x[$[]}) /
+ ($F_last{$x[$[]} - $F_first{$x[$[]})));
+ $s = int($s/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ ;# start is in the same file
+ $fpos[$[+1] = $x[$[];
+ $fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $x[$[]);
+ }
+ else
+ {
+ shift(@x);
+ }
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local($_);
+ while($_ = shift(@x))
+ {
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ print "guessing starting point according to count ($cnt)\n"
+ if $verbose > 3;
+ ;# guess offset to get last available $cnt samples
+ if ($fpos[$[] eq 'cnt')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ print "old positioning applies\n" if $verbose > 3;
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+
+ local($_);
+ while($_ = shift(@x))
+ {
+ print "examing \"$_\" $c samples still needed\n"
+ if $verbose > 4;
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ if (!defined($fpos[$[+2]))
+ {
+ print "no starting point yet - using start of data\n"
+ if $verbose > 2;
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ }
+ print "Ooops, no suitable input file ??\n"
+ if $verbose > 1 && @f <= 0;
+
+ printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
+ $fpos[$[+1],
+ $F_name{$fpos[$[+1]},
+ $fpos[$[+2],
+ scalar(@f))
+ if $verbose > 2;
+
+ $lm = 1;
+ $l = 0;
+ foreach $key (@f)
+ {
+ $file = $F_name{$key};
+ print "processing file \"$file\"\n" if $verbose > 2;
+
+ open(IN,"<$file") ||
+ (warn("$0: cannot read \"$file\": $!\n"), next);
+
+ ;# try to seek to a position nearer to the start of the interesting lines
+ ;# should always affect only first item in @f
+ ($key eq $fpos[$[+1]) &&
+ (($verbose > 1) &&
+ print("Seeking to offset $fpos[$[+2]\n"),
+ seek(IN,$fpos[$[+2],0) ||
+ warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
+
+ while(<IN>)
+ {
+ $l++;
+ ($verbose > 3) &&
+ (($l % $lm) == 0 && print("\t$l lines read\n") &&
+ (($l == 2) && ($lm = 10) ||
+ ($l == 100) && ($lm = 100) ||
+ ($l == 500) && ($lm = 500) ||
+ ($l == 1000) && ($lm = 1000) ||
+ ($l == 5000) && ($lm = 5000) ||
+ ($l == 10000) && ($lm = 10000)));
+
+ @F = split;
+
+ next if @F < 5; # no valid input line is this short
+ next if $F[$[] eq "";
+ ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
+ die("$0: unexpected input line: $_\n");
+
+ ;# modified Julian to UNIX epoch
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1]; # add seconds + fraction
+
+ ;# multiply offset by 1000 to get ms - try to avoid float op
+ (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/\1\2.\3/) &&
+ $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
+ || $F[$[+2] *= 1000;
+
+
+ ;# skip samples out of specified time range
+ next if (defined($StartTime) && $StartTime > $t);
+ next if (defined($EndTime) && $EndTime < $t);
+
+ next if defined($lastT) && $t < $lastT; # backward in time ??
+
+ push(@offs,$F[$[+2]);
+ push(@freq,$F[$[+3] * (2**20/10**6));
+ push(@cmpl,$F[$[+4]);
+
+ push(@break, (defined($lastT) && ($t - $lastT > $deltaT)));
+ $lastT = $t;
+ push(@time,$t);
+ push(@loffset, tell(IN) - length($_));
+ push(@filekey, $key);
+
+ shift(@break),shift(@time),shift(@offs),
+ shift(@freq), shift(@cmpl),shift(@loffset),
+ shift(@filekey)
+ if @time > $cnt &&
+ ! (defined($StartTime) && defined($EndTime));
+
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ close(IN);
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ print "input scanned ($l lines/",scalar(@time)," samples)\n"
+ if $verbose > 1;
+
+ &lr_init('offs');
+ &lr_init('freq');
+
+ if (@time)
+ {
+ local($_,@F);
+
+ local($timebase) unless defined($timebase);
+ local($freqbase) unless defined($freqbase);
+ local($cmplscale) unless defined($cmplscale);
+
+ undef($mintime,$maxtime,$minoffs,$maxoffs,
+ $minfreq,$maxfreq,$mincmpl,$maxcmpl,
+ $miny,$maxy);
+
+ print "computing ranges\n" if $verbose > 2;
+
+ $LastCnt = @time;
+
+ ;# @time is in ascending order (;-)
+ $mintime = @time[$[];
+ $maxtime = @time[$#time];
+ unless (defined($timebase))
+ {
+ local($time,@X) = (time);
+ @X = localtime($time);
+
+ ;# compute today 00:00:00
+ $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
+
+ }
+ $LastTimeBase = $timebase;
+
+ if ($showoffs)
+ {
+ local($i,$m,$f);
+
+ $minoffs = &min(@offs);
+ $maxoffs = &max(@offs);
+
+ ;# I know, it is not perl style using indices to access arrays,
+ ;# but I have to proccess two arrays in sync, non-destructively
+ ;# (otherwise a (shift(@a1),shift(a2)) would do),
+ ;# I dont like to make copies of these arrays as they may be huge
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$offs[$i],'offs'),$i++
+ while $i <= $#time;
+
+ ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
+
+ $i = &lr_sigma('offs');
+ $m = &lr_mean('offs');
+
+ print "mean offset: $m sigma: $i\n" if $verbose > 2;
+
+ if (($maxoffs - $minoffs) > $MinClip)
+ {
+ $f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
+ $miny = (($m - $minoffs) <= ($f * $i))
+ ? $minoffs : ($m - $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxoffs - $m) <= ($f * $i))
+ ? $maxoffs : ($m + $f * $i);
+ }
+ else
+ {
+ $miny = $minoffs;
+ $maxy = $maxoffs;
+ }
+ ($maxy-$miny) == 0 &&
+ (($maxy,$miny)
+ = (($maxoffs - $minoffs) > 0)
+ ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print "offset min clipped from $minoffs to $miny\n"
+ if $verbose > 2 && $minoffs != $miny;
+ print "offset max clipped from $maxoffs to $maxy\n"
+ if $verbose > 2 && $maxoffs != $maxy;
+ }
+
+ if ($showfreq)
+ {
+ local($i,$m);
+
+ $minfreq = &min(@freq);
+ $maxfreq = &max(@freq);
+
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq,'freq'),
+ $i++
+ while $i <= $#time;
+
+ $i = &lr_sigma('freq');
+ $m = &lr_mean('freq') + $minfreq;
+
+ print "mean frequency: $m sigma: $i\n" if $verbose > 2;
+
+ if (defined($maxy))
+ {
+ local($s) =
+ ($maxfreq - $minfreq)
+ ? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
+
+ if (defined($freqbase))
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScale = 10 ** int(log($s)/log(10) - 0.8);
+ $FreqScaleInv =
+ ("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" :
+ ($FreqScale == 1 ? "" : (1/$FreqScale));
+
+ $freqbase = $m * $FreqScale;
+ $freqbase -= &lr_mean('offs');
+
+ ;# round resulting freqbase
+ ;# to precision of min max difference
+ $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1;
+ $s = 10 ** $s;
+ $freqbase = int($freqbase / $s) * $s;
+ }
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $freqbase = $m unless defined($freqbase);
+ if (($maxfreq - $minfreq) > $MinClip)
+ {
+ $f = (&abs($minfreq) < &abs($maxfreq))
+ ? $FuzzLow : $FuzzBig;
+ $miny = (($freqbase - $minfreq) <= ($f * $i))
+ ? ($minfreq-$freqbase) : (- $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxfreq - $freqbase) <= ($f * $i))
+ ? ($maxfreq-$freqbase) : ($f * $i);
+ }
+ else
+ {
+ $miny = $minfreq - $freqbase;
+ $maxy = $maxfreq - $freqbase;
+ }
+ ($maxy - $miny) == 0 &&
+ (($maxy,$miny) =
+ (($maxfreq - $minfreq) > 0)
+ ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print("frequency min clipped from ",$minfreq-$freqbase,
+ " to $miny\n")
+ if $verbose > 2 && $miny != ($minfreq - $freqbase);
+ print("frequency max clipped from ",$maxfreq-$freqbase,
+ " to $maxy\n")
+ if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
+ }
+ $LastFreqBaseString =
+ sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
+ $LastFreqBase = $freqbase;
+ print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
+ if $verbose > 5;
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $LastFreqBase = 0;
+ $LastFreqBaseString = "";
+ }
+
+ if ($showcmpl)
+ {
+ $mincmpl = &min(@cmpl);
+ $maxcmpl = &max(@cmpl);
+
+ if (!defined($cmplscale))
+ {
+ if (defined($maxy))
+ {
+ local($cmp)
+ = (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
+ $cmplscale = $cmp == $maxy ? 1 : -1;
+
+ foreach (0.01, 0.02, 0.05,
+ 0.1, 0.2, 0.25, 0.4, 0.5,
+ 1, 2, 4, 5,
+ 10, 20, 25, 50,
+ 100, 200, 250, 500, 1000)
+ {
+ $cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
+ }
+ }
+ else
+ {
+ $cmplscale = 1;
+ $miny = $mincmpl ? 0 : -$MinClip;
+ $maxy = $maxcmpl+$MinClip;
+ }
+ }
+ $LastCmplScale = $cmplscale;
+ }
+ else
+ {
+ $LastCmplScale = 1;
+ }
+
+ print "creating plot command input file\n" if $verbose > 2;
+
+
+ print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
+ print OUT ("# timebase is: ",&ctime($LastTimeBase))
+ if defined($LastTimeBase);
+ print OUT ("# frequency is offset by ",
+ ($LastFreqBase >= 0 ? "+" : "-"),
+ "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
+ print OUT ("# compliance is scaled by $LastCmplScale\n");
+ print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
+
+ printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
+ (shift(@break) ? "\n" : ""),
+ (shift(@time) - $LastTimeBase)/3600,
+ shift(@offs),
+ shift(@freq) * $FreqScale - $LastFreqBase,
+ shift(@cmpl) / $LastCmplScale)
+ while(@time);
+ }
+ else
+ {
+ ;# prevent plotcmd from processing empty file
+ print "Creating plot command dummy...\n" if $verbose > 2;
+ print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
+ &lr_sample(0,1,'offs');
+ &lr_sample(1,1,'offs');
+ &lr_sample(0,2,'freq');
+ &lr_sample(1,2,'freq');
+ @time = (0, 1); $maxtime = 1; $mintime = 0;
+ @offs = (1, 1); $maxoffs = 1; $minoffs = 1;
+ @freq = (2, 2); $maxfreq = 2; $minfreq = 2;
+ @cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
+ $LastCnt = 2;
+ $LastFreqBase = 0;
+ $LastCmplScale = 1;
+ $LastTimeBase = 0;
+ $miny = -$MinClip;
+ $maxy = 3 + $MinClip;
+ }
+ close(OUT);
+
+ print "plot command input file created\n"
+ if $verbose > 2;
+
+ if (($fpos[$[] eq 'cnt' && @loffset >= $cnt) ||
+ ($fpos[$[] eq 'start' && $time[$[] <= $StartTime) ||
+ ($fpos[$[] eq 'end'))
+ {
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ else # found to few lines - next time start search earlier in file
+ {
+ if ($fpos[$[] eq 'start')
+ {
+ ;# the timestamps we got for F_first and F_last guaranteed
+ ;# that no file is left out
+ ;# the only thing that could happen is:
+ ;# we guessed the starting point wrong
+ ;# compute a new guess from the first record found
+ ;# if this equals our last guess use data of first record
+ ;# otherwise try new guess
+
+ if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
+ {
+ local($noff);
+ $noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
+ $noff = 0 if $noff < 0;
+
+ return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
+ }
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
+ {
+ ;# try to start earlier in file
+ ;# if we already started at the beginning
+ ;# try to use previous file
+ ;# this assumes distance to better starting point is at most one file
+ ;# the primary guess at top of genfile() should usually allow this
+ ;# assumption
+ ;# if the offset of the first sample used is within
+ ;# a different file than we guessed it must have occured later
+ ;# in the sequence of files
+ ;# this only can happen if our starting file did not contain
+ ;# a valid sample from the starting point we guessed
+ ;# however this does not invalidate our assumption, no check needed
+ local($noff,$key);
+ if ($fpos[$[+2] > 0)
+ {
+ $noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
+ $noff = 0 if $noff < 0;
+ return (@fpos[$[,$[+1],$noff);
+ }
+ else
+ {
+ if ($fpos[$[+1] eq $F_files[$[])
+ {
+ ;# first file - and not enough samples
+ ;# use data of first sample
+ return ($fpos[$[], $filekey[$[], $loffset[$[]);
+ }
+ else
+ {
+ ;# search key of previous file
+ $key = $F_files[$[];
+ @F = reverse(@F_files);
+ while ($_ = shift(@F))
+ {
+ if ($_ eq $fpos[$[+1])
+ {
+ $key = shift(@F) if @F;
+ last;
+ }
+ }
+ $noff = int($F_size{$key} / $RecordSize);
+ $noff -= $cnt - @loffset;
+ $noff = 0 if $noff < 0;
+ $noff *= $RecordSize;
+ return ($fpos[$[], $key, $noff);
+ }
+ }
+ }
+ else
+ {
+ return ();
+ }
+
+ return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
+
+ ;# EOF - 1.1 * avg(line) * $cnt
+ local($val) = $loffset[$#loffset]
+ - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
+ return ($val < 0) ? 0 : $val;
+ }
+}
+
+;# initial setup of plot
+print "initialize plotting\n" if $verbose;
+if (defined($PrintIt))
+{
+ if ($PrintIt =~ m,/,)
+ {
+ print "Saving plot to file $PrintIt\n";
+ print PLOT "set output '$PrintIt'\n";
+ }
+ else
+ {
+ print "Printing plot on printer $PrintIt\n";
+ print PLOT "set output '| lpr -P$PrintIt -h'\n";
+ }
+ print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
+}
+print PLOT "set grid\n";
+print PLOT "set tics out\n";
+print PLOT "set format y '%g '\n";
+printf PLOT "set time 47\n" unless defined($PrintIt);
+
+@filepos =();
+while(1)
+{
+ print &ctime(time) if $verbose;
+
+ ;# update diplay characteristics
+ &read_config;# unless defined($PrintIt);
+
+ unlink($tmpfile);
+ @filepos = &genfile($samples,$srcprefix,$tmpfile,@filepos);
+
+ ;# make plotcmd display samples
+ &make_doplot;
+ print "Displaying plot...\n" if $verbose > 1;
+ print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
+ print PLOT $doplot;
+}
+continue
+{
+ if (defined($PrintIt))
+ {
+ delete $SIG{'CHLD'};
+ print PLOT "quit\n";
+ close(PLOT);
+ if ($PrintIt =~ m,/,)
+ {
+ print "Plot saved to file $PrintIt\n";
+ }
+ else
+ {
+ print "Plot spooled to printer $PrintIt\n";
+ }
+ unlink($tmpfile);
+ exit(0);
+ }
+ ;# wait $delay seconds
+ print "waiting $delay seconds ..." if $verbose > 2;
+ sleep($delay);
+ print " continuing\n" if $verbose > 2;
+ undef($LastFreqBaseString);
+}
+
+
+sub date_time_spec2seconds
+{
+ local($_) = @_;
+ ;# a date_time_spec consistes of:
+ ;# YYYY-MM-DD_HH:MM:SS.ms
+ ;# values can be omitted from the beginning and default than to
+ ;# values of current date
+ ;# values omitted from the end default to lowest possible values
+
+ local($time) = time;
+ local($sec,$min,$hour,$mday,$mon,$year)
+ = localtime($time);
+
+ local($last) = ();
+
+ s/^\D*(.*\d)\D*/\1/; # strip off garbage
+
+ PARSE:
+ {
+ if (s/^(\d{4})(-|$)//)
+ {
+ if ($1 < 1970)
+ {
+ warn("$0: can not handle years before 1970 - year $1 ignored\n");
+ return undef;
+ }
+ elsif ( $1 >= 2070)
+ {
+ warn("$0: can not handle years past 2070 - year $1 ignored\n");
+ return undef;
+ }
+ else
+ {
+ $year = $1 % 100; # 0<= $year < 100
+ ;# - interpreted 70 .. 99,00 .. 69
+ }
+ $last = $[ + 5;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
+ return(undef)
+ if $2 eq '';
+ }
+
+ if (s/^(\d{1,2})(-|$)//)
+ {
+ warn("$0: implausible month $1\n"),return(undef)
+ if $1 < 1 || $1 > 12;
+ $mon = $1 - 1;
+ $last = $[ + 4;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
+ if defined($last);
+
+ }
+
+ if (s/^(\d{1,2})([_ ]|$)//)
+ {
+ warn("$0: implausible month day $1 for month ".($mon+1)." (".
+ $MaxNumDaysPerMonth[$mon].")$mon\n"),
+ return(undef)
+ if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
+ $mday = $1;
+ $last = $[ + 3;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ ;# now we face a problem:
+ ;# if ! defined($last) a prefix of "07:"
+ ;# can be either 07:MM or 07:ss
+ ;# to get the second interpretation make the user add
+ ;# a msec fraction part and check for this special case
+ if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >= 60;
+ warn("$0: implausible second $1\n"), return undef
+ if $2 < 0 || $2 >= 60;
+ $min = $1;
+ $sec = $2;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
+ return undef;
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible hour $1\n"), return undef
+ if $1 < 0 || $1 > 24;
+ $hour = $1;
+ $last = $[ + 2;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $min = $1;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2}(\.\d+)?)//)
+ {
+ warn("$0: implausible second $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $sec = $1;
+ $last = $[;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
+ return undef;
+ }
+ }
+
+ return $time unless defined($last);
+
+ $sec = 0 if $last > $[;
+ $min = 0 if $last > $[ + 1;
+ $hour = 0 if $last > $[ + 2;
+ $mday = 1 if $last > $[ + 3;
+ $mon = 0 if $last > $[ + 4;
+ local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
+
+ ;# $rtime may be off if daylight savings time is in effect at given date
+ return $rtime + ($sec - int($sec))
+ if $hour == (localtime($rtime))[$[+2];
+ return
+ &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
+ + ($sec - int($sec));
+}
+
+
+sub min
+{
+ local($m) = shift;
+
+ grep((($m > $_) && ($m = $_),0),@_);
+ $m;
+}
+
+sub max
+{
+ local($m) = shift;
+
+ grep((($m < $_) && ($m = $_),0),@_);
+ $m;
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntptrap b/usr.sbin/xntpd/scripts/monitoring/ntptrap
new file mode 100755
index 0000000..69c6660
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntptrap
@@ -0,0 +1,453 @@
+#!/local/bin/perl --*-perl-*-
+;#
+;# ntptrap,v 3.1 1993/07/06 01:09:15 jbj Exp
+;#
+;# a client for the xntp mode 6 trap mechanism
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!\1!; # strip to filename
+;# enforce STDOUT and STDERR to be line buffered
+$| = 1;
+select((select(STDERR),$|=1)[$[]);
+
+;#######################################
+;# load utility routines and definitions
+;#
+require('ntp.pl'); # implementation of the NTP protocol
+eval { require('sys/socket.ph'); require('netinet/in.ph') unless defined(&INADDR_ANY); } ||
+do {
+ die("$0: $@") unless $[ == index($@, "Can't locate ");
+ warn "$0: $@";
+ warn "$0: supplying some default definitions\n";
+ eval 'sub INADDR_ANY { 0; } sub AF_INET {2;} sub SOCK_DGRAM {2;} 1;' || die "$0: $@";
+};
+require('getopts.pl'); # option parsing
+require('ctime.pl'); # date/time formatting
+
+;######################################
+;# define some global constants
+;#
+$BASE_TIMEOUT=10;
+$FRAG_TIMEOUT=10;
+$MAX_TRY = 5;
+$REFRESH_TIME=60*15; # 15 minutes (server uses 1 hour)
+$ntp'timeout = $FRAG_TIMEOUT; #';
+
+;######################################
+;# now process options
+;#
+sub usage
+{
+ die("usage: $0 [-n] [-p <port>] [-l <logfile>] [host] ...\n");
+}
+
+$opt_l = "/dev/null"; # where to write debug messages to
+$opt_p = 0; # port to use locally - (0 does mean: will be choosen by kernel)
+
+&usage unless &Getopts('l:p:');
+&Getopts if 0; # make -w happy
+
+@Hosts = ($#ARGV < $[) ? ("localhost") : @ARGV;
+
+;# setup for debug output
+$DEBUGFILE=$opt_l;
+$DEBUGFILE="&STDERR" if $DEBUGFILE eq '-';
+
+open(DEBUG,">>$DEBUGFILE") || die("Cannot open \"$DEBUGFILE\": $!\n");
+select((select(DEBUG),$|=1)[$[]);
+
+;# &log prints a single trap record (adding a (local) time stamp)
+sub log
+{
+ chop($date=&ctime(time));
+ print "$date ",@_,"\n";
+}
+
+sub debug
+{
+ print DEBUG @_,"\n";
+}
+;#
+$proto_udp = (getprotobyname('udp'))[$[+2] ||
+ (warn("$0: Could not get protocoll number for 'udp' using 17"), 17);
+
+$ntp_port = (getservbyname('ntp','udp'))[$[+2] ||
+ (warn("$0: Could not get port number for service ntp/udp using 123"), 123);
+
+;#
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) || die("Cannot open socket: $!\n");
+
+;#
+bind(S, pack("S n N x8", &AF_INET, $opt_p, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+&log(sprintf("Listening at address %d.%d.%d.%d port %d",
+ unpack("C4",$my_addr), $my_port));
+
+;# disregister with all servers in case of termination
+sub cleanup
+{
+ &log("Aborted by signal \"$_[$[]\"") if defined($_[$[]);
+
+ foreach (@Hosts)
+ {
+ &ntp'send(S,31,0,"",pack("Sna4x8",&AF_INET,$ntp_port,$Hosts{$_})); #';
+ }
+ close(S);
+ exit(2);
+}
+
+$SIG{'HUP'} = 'cleanup';
+$SIG{'INT'} = 'cleanup';
+$SIG{'QUIT'} = 'cleanup';
+$SIG{'TERM'} = 'cleanup';
+
+0 && $a && $b;
+sub timeouts # sort timeout id array
+{
+ $TIMEOUTS{$a} <=> $TIMEOUTS{$b};
+}
+
+;# a Request element looks like: pack("a4SC",addr,associd,op)
+@Requests= ();
+
+;# compute requests for set trap control msgs to each host given
+{
+ local($name,$addr);
+
+ foreach (@Hosts)
+ {
+ if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
+ {
+ ($name,$addr) =
+ (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[,$[+4];
+ unless (defined($name))
+ {
+ $name = sprintf("[[%d.%d.%d.%d]]",$1,$2,$3,$4);
+ $addr = pack("C4",$1,$2,$3,$4);
+ }
+ }
+ else
+ {
+ ($name,$addr) = (gethostbyname($_))[$[,$[+4];
+ unless (defined($name))
+ {
+ warn "$0: unknown host \"$_\" - ignored\n";
+ next;
+ }
+ }
+ next if defined($Host{$name});
+ $Host{$name} = $addr;
+ push(@Requests,pack("a4SC",$addr,0,6)); # schedule a set trap request for $name
+ }
+}
+
+sub hostname
+{
+ local($addr) = @_;
+ return $HostName{$addr} if defined($HostName{$addr});
+ local($name) = gethostbyaddr($addr,&AF_INET);
+ &debug(sprintf("hostname(%d.%d.%d.%d) = \"%s\"",unpack("C4",$addr),$name))
+ if defined($name);
+ defined($name) && ($HostName{$addr} = $name) && (return $name);
+ &debug(sprintf("Failed to get name for %d.%d.%d.%d",unpack("C4",$addr)));
+ return sprintf("[%d.%d.%d.%d]",unpack("C4",$addr));
+}
+
+;# when no hosts were given on the commandline no requests have been scheduled
+&usage unless (@Requests);
+
+&debug(sprintf("%d request(s) scheduled",scalar(@Requests)));
+grep(&debug(" - ".$_),keys(%Host));
+
+;# allocate variables;
+$addr="";
+$assoc=0;
+$op = 0;
+$timeout = 0;
+$ret="";
+%TIMEOUTS = ();
+%TIMEOUT_PROCS = ();
+@TIMEOUTS = ();
+
+$len = 512;
+$buf = " " x $len;
+
+while (1)
+{
+ if (@Requests || @TIMEOUTS) # if there is some work pending
+ {
+ if (@Requests)
+ {
+ ($addr,$assoc,$op) = unpack("a4SC",($req = shift(@Requests)));
+ &debug(sprintf("Request: %s: %s(%d)",&hostname($addr), &ntp'cntrlop_name($op), $assoc)); #';))
+ $ret = &ntp'send(S,$op,$assoc,"", #'(
+ pack("Sna4x8",&AF_INET,$ntp_port,$addr));
+ &set_timeout("retry-".unpack("H*",$req),time+$BASE_TIMEOUT,
+ sprintf("&retry(\"%s\");",unpack("H*",$req)));
+
+ last unless (defined($ret)); # warn called by ntp'send();
+
+ ;# if there are more requests just have a quick look for new messages
+ ;# otherwise grant server time for a response
+ $timeout = @Requests ? 0 : $BASE_TIMEOUT;
+ }
+ if ($timeout && @TIMEOUTS)
+ {
+ ;# ensure not to miss a timeout
+ if ($timeout + time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ $timeout = $TIMEOUTS{$TIMEOUTS[$[]} - time;
+ $timeout = 0 if $timeout < 0;
+ }
+ }
+ }
+ else
+ {
+ ;# no work yet - wait for some messages dropping in
+ ;# usually this will not hapen as the refresh semantic will
+ ;# always have a pending timeout
+ undef($timeout);
+ }
+
+ vec($mask="",fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ warn("$0: select: $!\n"),last if $ret < 0; # give up on error return from select
+
+ if ($ret == 0)
+ {
+ ;# timeout
+ if (@TIMEOUTS && time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ ;# handle timeout
+ $timeout_proc =
+ (delete $TIMEOUT_PROCS{$TIMEOUTS[$[]},
+ delete $TIMEOUTS{shift(@TIMEOUTS)})[$[];
+ eval $timeout_proc;
+ die "timeout eval (\"$timeout_proc\"): $@\n" if $@;
+ }
+ ;# else: there may be something to be sent
+ }
+ else
+ {
+ ;# data avail
+ $from = recv(S,$buf,$len,0);
+ ;# give up on error return from recv
+ warn("$0: recv: $!\n"), last unless (defined($from));
+
+ $from = (unpack("Sna4",$from))[$[+2]; # keep host addr only
+ ;# could check for ntp_port - but who cares
+ &debug("-Packet from ",&hostname($from));
+
+ ;# stuff packet into ntp mode 6 receive machinery
+ ($ret,$data,$status,$associd,$op,$seq,$auth_keyid) =
+ &ntp'handle_packet($buf,$from); # ';
+ &debug(sprintf("%s uses auth_keyid %d",&hostname($from),$auth_keyid)) if defined($auth_keyid);
+ next unless defined($ret);
+
+ if ($ret eq "")
+ {
+ ;# handle packet
+ ;# simple trap response messages have neither timeout nor retries
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op))) unless $op == 7;
+ delete $RETRY{pack("a4SC",$from,$associd,$op)} unless $op == 7;
+
+ &process_response($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid);
+ }
+ else
+ {
+ ;# some kind of error
+ &log(sprintf("%50s: %s: %s",(gethostbyaddr($from,&AF_INET))[$[],$ret,$data));
+ if ($ret ne "TIMEOUT" && $ret ne "ERROR")
+ {
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op)));
+ }
+ }
+ }
+
+}
+
+warn("$0: terminating\n");
+&cleanup;
+exit 0;
+
+;##################################################
+;# timeout support
+;#
+sub set_timeout
+{
+ local($id,$time,$proc) = @_;
+
+ $TIMEOUTS{$id} = $time;
+ $TIMEOUT_PROCS{$id} = $proc;
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ chop($date=&ctime($time));
+ &debug(sprintf("Schedule timeout \"%s\" for %s", $id, $date));
+}
+
+sub clear_timeout
+{
+ local($id) = @_;
+ delete $TIMEOUTS{$id};
+ delete $TIMEOUT_PROCS{$id};
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ &debug("Clear timeout \"$id\"");
+}
+
+0 && &refresh;
+sub refresh
+{
+ local($addr) = @_;
+ $addr = pack("H*",$addr);
+ &debug(sprintf("Refreshing trap for %s", &hostname($addr)));
+ push(@Requests,pack("a4SC",$addr,0,6));
+}
+
+0 && &retry;
+sub retry
+{
+ local($tag) = @_;
+ $tag = pack("H*",$tag);
+ $RETRY{$tag} = 0 if (!defined($RETRY{$tag}));
+
+ if (++$RETRY{$tag} > $MAX_TRY)
+ {
+ &debug(sprintf("Retry failed: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ return;
+ }
+ &debug(sprintf("Retrying: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ push(@Requests,$tag);
+}
+
+sub process_response
+{
+ local($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid) = @_;
+
+ $msg="";
+ if ($op == 7) # trap response
+ {
+ $msg .= sprintf("%40s trap#%-5d",
+ &hostname($from),$seq);
+ &debug (sprintf("\nTrap %d associd %d:\n%s\n===============\n",$seq,$associd,$data));
+ if ($associd == 0) # system event
+ {
+ $msg .= " SYSTEM ";
+ $evnt = &ntp'SystemEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases add additional info
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ ($refid) = ($data =~ /refid=([\w\.]+)/);
+ $msg .= "stratum=$stratum refid=$refid";
+ if ($refid =~ /\[?(\d+)\.(\d+)\.(\d+)\.(\d+)/)
+ {
+ $msg .= " " . (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[];
+ }
+ if ($evnt eq "event_sync_chg")
+ {
+ $msg .= sprintf("%s %s ",
+ &ntp'LI($status), #',
+ &ntp'ClockSource($status) #'
+ );
+ }
+ elsif ($evnt eq "event_sync/strat_chg")
+ {
+ ($peer) = ($data =~ /peer=([0-9]+)/);
+ $msg .= " peer=$peer";
+ }
+ elsif ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ }
+ else # peer event
+ {
+ $msg .= sprintf("peer %5d ",$associd);
+ ($srcadr) = ($data =~ /srcadr=\[?([\d\.]+)/);
+ $msg .= sprintf("%-18s %40s ", "[$srcadr]",
+ &hostname(pack("C4",split(/\./,$srcadr))));
+ $evnt = &ntp'PeerEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases include additional info
+ if ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ;#&debug("----\n$data\n====\n");
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ ;# no clockvars included - post a cv request
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ elsif ($evnt eq "event_stratum_chg")
+ {
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ $msg .= "new stratum $stratum";
+ }
+ }
+ }
+ elsif ($op == 6) # set trap resonse
+ {
+ &debug("Set trap ok from ",&hostname($from));
+ &set_timeout("refresh-".unpack("H*",$from),time+$REFRESH_TIME,
+ sprintf("&refresh(\"%s\");",unpack("H*",$from)));
+ return;
+ }
+ elsif ($op == 4) # read clock variables response
+ {
+ ;# status of clock
+ $msg .= sprintf(" %40s ", &hostname($from));
+ if ($associd == 0)
+ {
+ $msg .= "system clock status: ";
+ }
+ else
+ {
+ $msg .= sprintf("peer %5d clock",$associd);
+ }
+ $msg .= sprintf("%-32s",&ntp'clock_status($status)); #');
+ ($device) = ($data =~ /device=\"([^\"]+)\"/);
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ elsif ($op == 31) # unset trap response (UNOFFICIAL op)
+ {
+ ;# clear timeout
+ &debug("Clear Trap ok from ",&hostname($from));
+ &clear_timeout("refresh-".unpack("H*",$from));
+ return;
+ }
+ else # unexpected response
+ {
+ $msg .= "unexpected response to op $op assoc=$associd";
+ $msg .= sprintf(" status=%04x",$status);
+ }
+ &log($msg);
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/timelocal.pl b/usr.sbin/xntpd/scripts/monitoring/timelocal.pl
new file mode 100755
index 0000000..061f925
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/timelocal.pl
@@ -0,0 +1,78 @@
+;# timelocal.pl
+;#
+;# Usage:
+;# $time = timelocal($sec,$min,$hours,$mday,$mon,$year,$junk,$junk,$isdst);
+;# $time = timegm($sec,$min,$hours,$mday,$mon,$year);
+
+;# These routines are quite efficient and yet are always guaranteed to agree
+;# with localtime() and gmtime(). We manage this by caching the start times
+;# of any months we've seen before. If we know the start time of the month,
+;# we can always calculate any time within the month. The start times
+;# themselves are guessed by successive approximation starting at the
+;# current time, since most dates seen in practice are close to the
+;# current date. Unlike algorithms that do a binary search (calling gmtime
+;# once for each bit of the time value, resulting in 32 calls), this algorithm
+;# calls it at most 6 times, and usually only once or twice. If you hit
+;# the month cache, of course, it doesn't call it at all.
+
+;# timelocal is implemented using the same cache. We just assume that we're
+;# translating a GMT time, and then fudge it when we're done for the timezone
+;# and daylight savings arguments. The timezone is determined by examining
+;# the result of localtime(0) when the package is initialized. The daylight
+;# savings offset is currently assumed to be one hour.
+
+CONFIG: {
+ package timelocal;
+
+ @epoch = localtime(0);
+ $tzmin = $epoch[2] * 60 + $epoch[1]; # minutes east of GMT
+ if ($tzmin > 0) {
+ $tzmin = 24 * 60 - $tzmin; # minutes west of GMT
+ $tzmin -= 24 * 60 if $epoch[5] == 70; # account for the date line
+ }
+
+ $SEC = 1;
+ $MIN = 60 * $SEC;
+ $HR = 60 * $MIN;
+ $DAYS = 24 * $HR;
+ $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
+ 1;
+}
+
+sub timegm {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS;
+}
+
+sub timelocal {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS
+ + $tzmin * $MIN - 60 * 60 * ($_[8] != 0);
+}
+
+package timelocal;
+
+sub cheat {
+ $year = $_[5];
+ $month = $_[4];
+ $guess = $^T;
+ @g = gmtime($guess);
+ $year += $YearFix if $year < $epoch[5];
+ while ($diff = $year - $g[5]) {
+ $guess += $diff * (364 * $DAYS);
+ @g = gmtime($guess);
+ }
+ while ($diff = $month - $g[4]) {
+ $guess += $diff * (28 * $DAYS);
+ @g = gmtime($guess);
+ }
+ $g[3]--;
+ $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS;
+ $cheat{$ym} = $guess;
+}
diff --git a/usr.sbin/xntpd/scripts/ntp-groper b/usr.sbin/xntpd/scripts/ntp-groper
new file mode 100755
index 0000000..1fd0cfe
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/ntp-groper
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# ntpgroper host ...
+#
+# This script checks each hostname given as an argument to see if
+# it is running NTP. It reports one of the following messages (assume
+# the host is named "dumbo.hp.com":
+#
+# dumbo.hp.com not registered in DNS
+# dumbo.hp.com not responding to ping
+# dumbo.hp.com refused ntpq connection
+# dumbo.hp.com not responding to NTP
+# dumbo.hp.com answers NTP version 2, stratum: 3, ref: telford.nsa.hp.com
+# dumbo.hp.com answers NTP version 3, stratum: 3, ref: telford.nsa.hp.com
+#
+# It ain't pretty, but it is kinda useful.
+#
+# Walter Underwood, 11 Feb 1993, wunder@hpl.hp.com
+#
+# converted to /bin/sh from /bin/ksh by scott@ee.udel.edu 24 Mar 1993
+
+PATH="/usr/local/etc:$PATH" export PATH
+
+verbose=1
+logfile=/tmp/cntp-log$$
+ntpqlog=/tmp/cntp-ntpq$$
+
+# I wrap the whole thing in parens so that it is possible to redirect
+# all the output somewhere, if desired.
+(
+for host in $*
+do
+ # echo "Trying $host."
+
+ gethost $host > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not registered in DNS"
+ continue
+ fi
+
+ ping $host 64 1 > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not responding to ping"
+ continue
+ fi
+
+ # Attempt to contact with version 3 ntp, then try version 2.
+ for version in 3 2
+ do
+
+ ntpq -c "ntpversion $version" -p $host > $ntpqlog 2>&1
+
+ if fgrep -s 'Connection refused' $ntpqlog
+ then
+ echo "$host refused ntpq connection"
+ break
+ fi
+
+ responding=1
+ fgrep -s 'timed out, nothing received' $ntpqlog > /dev/null && responding=0
+
+ if [ $responding -eq 1 ]
+ then
+ ntpq -c "ntpversion $version" -c rl $host > $ntpqlog
+
+ # First we extract the reference ID (usually a host or a clock)
+ synchost=`fgrep "refid=" $ntpqlog`
+ #synchost=${synchost##*refid=} # strip off the beginning of the line
+ #synchost=${synchost%%,*} # strip off the end
+ synchost=`expr "$synchost" : '.*refid=\([^,]*\),.*'`
+
+ # Next, we get the stratum
+ stratum=`fgrep "stratum=" $ntpqlog`
+ #stratum=${stratum##*stratum=}
+ #stratum=${stratum%%,*}
+ stratum=`expr "$stratum" : '.*stratum=\([^,]*\),.*'`
+
+ echo "$host answers NTP version $version, stratum: $stratum, ref: $synchost"
+ break;
+ fi
+
+ if [ $version -eq 2 -a $responding -eq 0 ]
+ then
+ echo "$host not responding to NTP"
+ fi
+ done
+done
+)
+# ) >> $logfile
+
+if [ -f $ntpqlog ]; then
+ rm $ntpqlog
+fi
diff --git a/usr.sbin/xntpd/scripts/ntp-restart b/usr.sbin/xntpd/scripts/ntp-restart
new file mode 100755
index 0000000..d2023f0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/ntp-restart
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# This script can be used to kill and restart the NTP daemon. Edit the
+# /usr/local/bin/xntpd line to fit.
+#
+kill -INT `ps -ax | egrep "xntpd" | egrep -v "egrep" | sed 's/^\([ 0-9]*\) .*/\1'/`
+sleep 10
+/usr/local/bin/xntpd
+exit 0
diff --git a/usr.sbin/xntpd/scripts/stats/README b/usr.sbin/xntpd/scripts/stats/README
new file mode 100644
index 0000000..6808963
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README
@@ -0,0 +1,39 @@
+Statistics processing scripts (README)
+
+This directory contains a number of scripts for use with the filegen
+facility. Those files ending in .awk are for the Unix awk utility, while
+those ending in .sh are for the csh utility. Normally, the summary.sh
+script is called from a cron job once per day. This script processes the
+daily loopstats, peerstats and clockstats files produced by the daemon,
+updates the loop_summary, peer_summary and clock_summary archive files,
+and deletes the daily files.
+
+In the case of the Austron 2201A GPS receiver, the clockstats file
+contains a wealth of additional monitoring data. These data are summarized
+and writted to the clock_summary file, then a series of special files are
+constructed for later processing by the S utility.
+
+The summary.sh script invokes a number of awk scripts to actually produce
+the data. This may result in multiple scans of the same input file.
+The input file is deleted after processing. In fact, the shell scripts will
+process all input files found of the correct type in chronological order,
+deleting each one as it is scanned, except the current day file.
+
+The summary.sh script can produce input files for the S utility, if it
+is found on the search path. This utility makes PostScript graphs of the
+loopstats data for each day, as well as various statistics produced by
+the Austorn 220aA GPS receiver. The S utility is automatically run
+as a background job. Its control files have the .S extension.
+
+The psummary.awk script can be used to scan the peer_summary file and
+construct an historical reprise of the daily summaries.
+
+The file formats are documented in the README.stats file and in the
+scripts themselves. Further detail on the radio clock ASCII timecode
+formats and related data are in the README.timecode file.
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+1 November 1993
+Revised 12 April 1994
diff --git a/usr.sbin/xntpd/scripts/stats/README.stats b/usr.sbin/xntpd/scripts/stats/README.stats
new file mode 100644
index 0000000..aa8e77f
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README.stats
@@ -0,0 +1,246 @@
+Statistics file formats (README.stats)
+
+The xntp3 daemon can produce a variety of statistics files which are
+useful for maintenance, evaluation and retrospective calibration
+purposes. See the xntpd.8 man page for instructions on how to configure
+this feature. Since these files can become rather large and cumbersome,
+they are ordinarily reduced to summary form by running the summary.sh
+shell script once per day, week or month, as appropriate. There are
+three file collections presently defined: peerstats, loopstats and
+clockstats, each of which is described in this note.
+
+peerstats
+
+The following data are collected in the peerstats files. The files are
+reduced to summary data using the peer.sh shell script. See the peer.awk
+script for further information. A line in the file is produced upon
+reception of each valid update from a configured peer.
+
+ 49236 30.756 140.173.96.1 9474 0.000603 0.37532
+
+ 49236 modified Julian day number
+ 30.756 time of day (s) past midnight UTC
+ 140.173.96.1 peer identifier (IP address or receiver identifier)
+ 9474 peer status word (hex) (see NTP specification)
+ 0.000603 offset (s)
+ 0.08929 delay (s)
+ 0.37532 dispersion (s)
+
+loopstats
+
+The following data are collected in the loopstats files. The files are
+reduced to summary data using the loop.sh shell script. See the loop.awk
+script for further information. A line in the file is produced at each
+valid update of the local clock.
+
+ 49236 11.897 -0.000004 -35.9384 0
+
+ 49236 modified Julian day number
+ 11.897 time of day (s) past midnight UTC
+ -0.000004 time offset (s)
+ -35.9384 frequency offset (ppm)
+ 0 phase-lock loop time constant
+
+clockstats
+
+The following data are collected in the clockstats files. The files are
+reduced to summary data using the clock.sh shell script, which also
+updates the ensemble, etf, itf and tdata data files as well. See the
+clock.awk, ensemble.awk, etf.awk, itf.awk and tdta.awk scripts for
+further information. A line in the file is produced at each valid update
+received from a configured radio clock. Data are at present recorded for
+several radios. The first part of each data line is similar for all
+radios, e.g.:
+
+ 49234 60517.826 127.127.4.1 93 247 16:48:21.814
+
+ 49234 modified Julian day number
+ 60517.826 time of day (s) past midnight UTC
+ 127.127.4.1 receiver identifier (Spectracom 8170/Netclock-2)
+ 93 247 16:48:21.814 timecode (format varies)
+
+In the case of the Austron GPS receiver, a good deal of additional
+information is extracted from the radio, as described below. The formats
+shown consist of one line with all the fields shown in order. The
+timecode formats specific to each radio follow. See the file
+README.timecodes for detailed information on the timecode formats used
+by these radios.
+
+Spectracom 8170/Netclock-2 WWVB receiver
+
+ 49234 60517.826 127.127.4.1 ?A93 247 16:48:21.814
+
+ The '?' and 'A' characters are present only when the receiver is
+ unsynchronized; otherwise, they are replaced by space ' ' characters.
+
+IRIG audio decoder
+
+ 49234 60517.826 127.127.6.0 247 16:48:21?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Austron 2200A/2201A GPS receiver
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Depending on the installed options, the Austron 2200A/2201A recognizes a
+number of special commands that report various data items. See the
+refclock_as2201.c source module for a list of the commands used. These
+data are collected only if the following line is included in the
+configuration file ntp.conf:
+
+ fudge 127.127.10.1 flag4 1 # enable extended statistics collection
+
+The format of each data line returned is summarized in the following
+list.
+
+External time/frequency data (requires input buffer option IN)
+
+These data determine the deviations of external time/frequency inputs
+relative to receiver oscillator time. The following data are typical
+using an external cesium oscillator PPS and 5-MHz outputs.
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814 ETF
+
+ -85.9 time interval (ns)
+ -89.0 average time interval (ns)
+ 4.0 time interval sigma (ns)
+ +1.510E-11 time interval rate
+ -4.500E-11 deltaf/f
+ +1.592E-11 average deltaf/f
+ 5.297E-13 sigma deltaf/f
+ 500 number of samples
+
+Model and option identifiers
+
+These data show the receiver model number and option configuration.
+
+ 49234 60708.848 127.127.10.1 93:247:16:51:32.817 ID;OPT;VER
+
+ GPS 2201A model ident (must be "GPS 2200A" or "GPS 2201A")
+ TTY1 rs232 option present (required)
+ TC1 IRIG option present (optional)
+ LORAN LORAN assist option present (optional)
+ IN input buffer option present (optional)
+ OUT1 output buffer option present (required)
+ B.00 data processor software version ("B.00" or later)
+ B.00 signal processor software version ("B.00" or later)
+ 28-Apr-93 software version date ("28-Apr-93" or later)
+
+Internal time/frequency data
+
+These data determine the deviations of the receiver oscillator with
+respect to satellite time.
+
+ 49234 60564.846 127.127.10.1 93:247:16:49:08.816 ITF
+
+ COCO current mode (must be "COCO")
+ 0 code coast mode (must be zero)
+ +6.6152E-08 code sigma (s)
+ -3.5053E-08 code delta t (s)
+ -4.0361E-11 deltat/t
+ -6.4746E-11 oscillator ageing rate
+ 500.00 loop time constant
+ 4.984072 electrical tuning (V)
+
+GPS/LORAN ensemble data (requires LORAN assist option LORAN)
+
+These data determine the deviations and weights to calculate ensemble
+time from GPS and LORAN data.
+
+ 49234 60596.852 127.127.10.1 93:247:16:49:40.812 LORAN ENSEMBLE
+
+ +9.06E-08 GPS t (s)
+ +3.53E-08 GPS sigma (s)
+ .532 GPS weight
+ +3.71E-08 LORAN t (s)
+ +3.76E-08 LORAN sigma (s)
+ .468 LORAN weight
+ +6.56E-08 ensemble t
+ +6.94E-08 ensemble sigma (s)
+
+LORAN stationkeeping data (requires LORAN assist option LORAN)
+
+These data determine which stations of the LORAN chain are being
+tracked, together with individual signal/noise ratios, deviations and
+weights.
+
+ 49234 60532.850 127.127.10.1 93:247:16:48:36.820 LORAN TDATA
+
+ M station identifier; data follows
+ OK status (must be "OK" for tracking)
+ 0 cw flag
+ 0 sw flag
+ 1162.17 time of arrival
+ -4.6 snr (-30.0 if not "OK" status)
+ 1.67E-07 2-sample phase-time deviation
+ .507 weight (included only if "OK" status)
+ W AQ 0 0 3387.80 -31.0 station identifier and data
+ X OK 0 0 1740.27 -11.2 2.20E-07 .294 station identifier and data
+ Y OK 0 0 2180.71 -4.6 2.68E-07 .198 station identifier and data
+ Z CV 0 0 3392.94 -30.0 station identifier and data
+
+Oscillator status and environment
+
+These data determine the receiver oscillator type, mode, status and
+environment. Nominal operating conditions are shown below.
+
+ 49234 60628.847 127.127.10.1 93:247:16:50:12.817 OSC;ET;TEMP
+
+ 1121 Software Control oscillator model and mode (must be
+ "Software Control")
+ Locked status (must be "Locked")
+ 4.979905 electrical tuning (V)
+ 44.81 oscillator cavity temperature
+
+Receiver position, status and offsets
+
+These data determine the receiver position and elevation, together with
+programmable delay corrections for the antenna cable and receiver.
+
+ 49234 60788.847 127.127.10.1 93:247:16:52:52.817 POS;PPS;PPSOFF
+
+ +39:40:48.425 receiver latitude (N)
+ -075:45:02.392 receiver longitude (E)
+ +74.09 receiver elevation (m)
+ Stored position status (must be "Stored")
+ UTC PPS/PPM alignment (must be "UTC")
+ 0 receiver delay (ns) (should be zero for calibrated
+ receiver)
+ 200 cable delay (ns)
+ 0 user time bias (ns) (must be zero)
+
+Satellite tracking status
+
+These data determine how many satellites are being tracked. At the
+present state of constellation development, there should be at least
+three visible satellites in view. Much of the time the maximum of
+seven are being tracked; rarely this number drops to two.
+
+ 49234 60612.850 127.127.10.1 93:247:16:49:56.820 TRSTAT
+
+ 24 T satellite prn and status (T = track, A = acquire)
+ 16 A 13 T 20 T 18 T 07 T 12 T list continued
+
+UTC leap-second information
+
+These data determine when the next leap second is to occur. The exact
+method to use is obscure.
+
+ 49234 60548.847 127.127.10.1 93:247:16:48:52.818 UTC
+
+ -1.2107E-08 A0 term (s)
+ -1.2790E-13 A1 term (s)
+ +9.0000E+00 current leap seconds (s)
+ +2.0480E+05 time for leap seconds (s)
+ +2.0100E+02 week number for delta leap (weeks)
+ +1.9100E+02 week number for future leap (weeks)
+ +4.0000E+00 day number for future leap (days)
+ +9.0000E+00 future leap seconds (s)
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/usr.sbin/xntpd/scripts/stats/README.timecodes b/usr.sbin/xntpd/scripts/stats/README.timecodes
new file mode 100644
index 0000000..00b5ba5
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README.timecodes
@@ -0,0 +1,149 @@
+Radio Timecode Formats (README.timecodes)
+
+Following are examples of the serial timecode formats used by various
+timecode receivers as given in the instruction manuals. These examples
+are intended only for illustration and not as the basis of system
+design. The following symbols are used to identify the timecode
+character that begins a subfield. The values given after this symbol
+represent the character offset from the beginning of the timecode string
+as edited to remove control characters.
+
+C on-time character (start bit)
+Y year of century
+T time of day
+D day of year or month/day
+A alarm indicator (format specific)
+Q quality indicator (format specific)
+<LF> ASCII line feed (hex 0a)
+<CR> ASCII carriage return (hex 0d)
+<SP> ASCII space (hex 20)
+
+In order to promote uniform behavior in the various implementations, it
+is useful to have a common interpretation of alarm conditions and signal
+quality. When the alarm indicator it on, the receiver is not operating
+correctly or has never synchronized to the broadcast signal. When the
+alarm indicator is off and the quality indicator is on, the receiver has
+synchronized to the broadcast signal, then lost the signal and is
+coasting on its internal oscillator.
+
+In the following uppercase letters, punctuation marks and spaces <SP>
+stand for themselves; lowercase letters stand for fields as described.
+Special characters other than <LF>, <CR> and <SP> are preceded by ^.
+
+Spectracom 8170 and Netclock/2 WWV Synchonized Clock (format 0)
+
+"<CR><LF>i ddd hh:mm:ss TZ=zz<CR><LF>"
+ C A D T
+
+ poll: ?; offsets: Y = none, D = 3, T = 7, A = 0, Q = none
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ ddd = day of year
+ hh:mm:ss = hours, minutes, seconds
+ zz = timezone offset (hours from UTC)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours
+
+ example: " 216 15:36:43 TZ=0"
+ A D T
+
+Netclock/2 WWV Synchonized Clock (format 2)
+
+"<CR><LF>iqyy ddd hh:mm:ss.fff ld"
+ C AQY D T
+
+ poll: ?; offsets: Y = 2, D = 5, T = 9, A = 0, Q = 1
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ q = quality indicator (<SP> < 1ms, A < 10 ms, B < 100 ms, C < 500
+ ms, D > 500 ms)
+ yy = year (as broadcast)
+ ddd = day of year
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ l = leap-second warning (L indicates leap at end of month)
+ d = standard/daylight time indicator (<SP> standard, D daylight)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours; unlock condition is indicated by
+ other than <SP> at Q, with time since last lock indicated by the
+ letter code A < 13 min, B < 1.5 hr, C < 7 hr, D > 7 hr.
+
+ example: " 92 216 15:36:43.640 D"
+ AQ D T
+
+TrueTime 468-DC Satellite Synchronized Clock (and other TrueTime
+receivers)
+
+"<CR><LF><^A>ddd:hh:mm:ssq<CR>"
+ D T QC
+
+ poll: none; offsets: Y = none, D = 0, T = 4, A = 12, Q = 12
+ hh:mm:ss = hours, minutes, seconds
+ q = quality/alarm indicator (<SP> = locked, ? = alarm)
+
+ Note: alarm condition is indicated by ? at A, which occurs during
+ initial synchronization and when received signal is lost for an
+ extended period; unlock condition is indicated by other than <SP>
+ at Q
+
+ example: "216:15:36:43 "
+ D T Q
+
+Heath GC-1000 Most Accurate Clock (WWV/H)
+
+"<CR>hh:mm:ss.f dd/mm/yy<CR>"
+ C T A D
+
+ poll: none; offsets: Y = none, D = 15, T = 0, A = 9, Q = none
+ hh:mm:ss = hours, minutes, seconds
+ f = deciseconds (? when out of spec)
+ dd/mm = day, month
+ yy = year of century (from DIPswitches)
+
+ Note: 0?:??:??.? is displayed before synch is first established and
+ hh:mm:ss.? once synch is established and then lost again for about
+ a day.
+
+ example: "15:36:43.6 04/08/91"
+ T A D Y
+
+PST/Traconex 1020 Time Source (WWV/H) (firmware revision V4.01)
+
+"frdzycchhSSFTttttuuxx<CR>" "ahh:mm:ss.fffs<CR>" "yy/dd/mm/ddd<CR>"
+ A Q T Y D
+
+ poll: "QMQDQT"; offsets: Y = 0, D = 3 T = 1,, A = 11, Q = 13
+ f = frequency enable (O = all frequencies enabled)
+ r = baud rate (3 = 1200, 6 = 9600)
+ d = features indicator (@ = month/day display enabled)
+ z = time zone (0 = UTC)
+ y = year (5 = 1991)
+ cc = WWV propagation delay (52 = 22 ms)
+ hh = WWVH propagation delay (81 = 33 ms)
+ SS = status (80 or 82 = operating correctly)
+ F = current receive frequency (1-5 = 2.5, 5, 10, 15, 20 MHz)
+ T = transmitter (C = WWV, H = WWVH)
+ tttt = time since last update (minutes)
+ uu = flush character (03 = ^C)
+ xx = 94 (unknown) (firmware revision X4.01.999 only)
+
+ a = AM/PM indicator (A = AM, P = PM, <SP> - 24-hour format)
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ s = daylight-saving indicator (<SP> standard, D daylight)
+
+ yy = year of century (from DIPswitches)
+ dd/mm/ddd = day of month, month of year, day of year
+
+ Note: The alarm condition is indicated by other than ? at A, which
+ occurs during initial synchronization and when received signal is
+ lost for an extended period. A receiver unlock condition is
+ indicated by other than "0000" in the tttt subfield at Q.
+
+ example: "O3@055281824C00000394 91/08/04/216 15:36:43.640"
+ T Y D T
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/usr.sbin/xntpd/scripts/stats/clock.awk b/usr.sbin/xntpd/scripts/stats/clock.awk
new file mode 100644
index 0000000..c9d1455
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/clock.awk
@@ -0,0 +1,341 @@
+# awk program to scan clockstat files and report errors/statistics
+#
+# usage: awk -f check.awk clockstats
+#
+# This program works for Spectracom 8170/Netclock-2 receiver, Austron
+# 2200A/2201A receiver and IRIG audio decoder. It is easily adapted to
+# other receivers as required. See README.austron file for additional
+# information on Austron receiver.
+#
+BEGIN {
+ etf_min = osc_vmin = osc_tmin = 1e9
+ etf_max = osc_vmax = osc_tmax = -1e9
+}
+#
+# scan all records in file
+#
+{
+ #
+ # select WWVB records
+ # see summary for decode
+ #
+ if (NF >= 4 && $3 == "127.127.4.1") {
+ if ($4 == "SIGNAL" || NF > 7)
+ printf "%s\n", $0
+ else {
+ wwvb_count++
+ if ($4 ~ /\?/)
+ wwvb_x++
+ else if ($4 ~ /A/)
+ wwvb_a++
+ else if ($4 ~ /B/)
+ wwvb_b++
+ else if ($4 ~ /C/)
+ wwvb_c++
+ else if ($4 ~ /D/)
+ wwvb_d++
+ }
+ continue
+ }
+ #
+ # select IRIG records
+ # see summary for decode
+ #
+ if (NF >= 4 && $3 == "127.127.6.0") {
+ irig_count++
+ if ($5 ~ /\?/)
+ irig_error++
+ continue
+ }
+ #
+ # select LORAN ENSEMBLE records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $6 == "ENSEMBLE") {
+ ensemble_count++
+ if ($9 <= 0)
+ ensemble_badgps++
+ else if ($12 <= 0)
+ ensemble_badloran++
+ else {
+ if ($13 > 200e-9 || $13 < -200e-9)
+ ensemble_200++
+ else if ($13 > 100e-9 || $13 < -100e-9)
+ ensemble_100++
+ ensemble_mean += $13
+ ensemble_rms += $13 * $13
+ }
+ continue
+ }
+ #
+ # select LORAN TDATA records
+ # see summary for decode; note that signal quality log is simply
+ # copied to output
+ #
+ else if (NF >= 7 && $6 == "TDATA") {
+ tdata_count++
+ for (i = 7; i < NF; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m += $i
+ tdata_m++
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w += $i
+ tdata_w++
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x += $i
+ tdata_x++
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y += $i
+ tdata_y++
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z += $i
+ tdata_z++
+ }
+ }
+ continue
+ }
+ #
+ # select ITF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ITF" && $12 >= 100) {
+ itf_count++
+ if ($9 > 200e-9 || $9 < -200e-9)
+ itf_200++
+ else if ($9 > 100e-9 || $9 < -100e-9)
+ itf_100++
+ itf_mean += $9
+ itf_rms += $9 * $9
+ itf_var += $10 * $10
+ continue
+ }
+ #
+ # select ETF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ETF" && $13 >= 100) {
+ etf_count++
+ if ($6 > etf_max)
+ etf_max = $6
+ else if ($6 < etf_min)
+ etf_min = $6
+ etf_mean += $6
+ etf_rms += $6 * $6
+ etf_var += $9 * $9
+ continue
+ }
+ #
+ # select TRSTAT records
+ # see summary for decode
+ #
+ else if (NF >= 5 && $5 == "TRSTAT") {
+ trstat_count++
+ j = 0
+ for (i = 6; i <= NF; i++)
+ if ($i == "T")
+ j++
+ trstat_sat[j]++
+ continue
+ }
+ #
+ # select ID;OPT;VER records
+ #
+ # config GPS 2201A TTY1 TC1 LORAN IN OUT1 B.00 B.00 28-Apr-93
+ #
+ # GPS 2201A receiver model
+ # TTY1 rs232 moduel
+ # TC1 IRIG module
+ # LORAN LORAN assist module
+ # IN input module
+ # OUT1 output module
+ # B.00 B.00 firmware revision
+ # 28-Apr-9 firmware date3
+ #
+ else if (NF >= 5 && $5 == "ID;OPT;VER") {
+ id_count++
+ id_temp = ""
+ for (i = 6; i <= NF; i++)
+ id_temp = id_temp " " $i
+ if (id_string != id_temp)
+ printf "config%s\n", id_temp
+ id_string = id_temp
+ continue
+ }
+ #
+ # select POS;PPS;PPSOFF records
+ #
+ # position +39:40:48.425 -075:45:02.392 +74.09 Stored UTC 0 200 0
+ #
+ # +39:40:48.425 position north latitude
+ # -075:45:02.392 position east longitude
+ # +74.09 elevation (meters)
+ # Stored position is stored
+ # UTC time is relative to UTC
+ # 0 200 0 PPS offsets
+ #
+ else if (NF >= 5 && $5 == "POS;PPS;PPSOFF") {
+ pos_count++
+ pos_temp = ""
+ for (i = 6; i <= NF; i++)
+ pos_temp = pos_temp " " $i
+ if (pos_string != pos_temp)
+ printf "position%s\n", pos_temp
+ pos_string = pos_temp
+ continue
+ }
+ #
+ # select OSC;ET;TEMP records
+ #
+ # loop 1121 Software Control Locked
+ #
+ # 1121 oscillator type
+ # Software Control loop is under software control
+ # Locked loop is locked
+ #
+ else if (NF >= 5 && $5 == "OSC;ET;TEMP") {
+ osc_count++
+ osc_temp = $6 " " $7 " " $8 " " $9
+ if (osc_status != osc_temp)
+ printf "loop %s\n", osc_temp
+ osc_status = osc_temp
+ if ($10 > osc_vmax)
+ osc_vmax = $10
+ if ($10 < osc_vmin)
+ osc_vmin = $10
+ if ($11 > osc_tmax)
+ osc_tmax = $11
+ if ($11 < osc_tmin)
+ osc_tmin = $11
+ continue
+ }
+ #
+ # select UTC records
+ # these ain't ready yet
+ #
+ else if (NF >= 5 && $5 == "UTC") {
+ utc_count++
+ utc_temp = ""
+ for (i = 6; i <= NF; i++)
+ utc_temp = utc_temp " " $i
+ if (utc_string != utc_temp)
+# printf "utc%s\n", utc_temp
+ utc_string = utc_temp
+ continue
+ }
+} END {
+#
+# ensemble summary data
+#
+# ensemble record count
+# badgps gps data unavailable
+# badloran loran data unavailable
+# rms ensemble rms error (ns)
+# >200 ensemble error >200 ns
+# >100 100 ns < ensemble error < 200 ns
+#
+ if (ensemble_count > 0) {
+ ensemble_mean /= ensemble_count
+ ensemble_rms = sqrt(ensemble_rms / ensemble_count - ensemble_mean * ensemble_mean) * 1e9
+ printf "ensemble %d, badgps %d, badloran %d, rms %.1f, >200 %d, >100 %d\n", ensemble_count, ensemble_badgps, ensemble_badloran, ensemble_rms, ensemble_200, ensemble_100
+ }
+#
+# wwvb summary data
+#
+# wwvb record count
+# ? unsynchronized
+# >1 error > 1 ms
+# >10 error > 10 ms
+# >100 error > 100 ms
+# >500 error > 500 ms
+#
+ if (wwvb_count > 0)
+ printf "wwvb %d, ? %d, >1 %d, >10 %d, >100 %d, >500 %d\n", wwvb_count, wwvb_x, wwvb_a, wwvb_b, wwvb_c, wwvb_d
+#
+# irig summary data
+#
+# irig record count
+# err error count
+#
+ if (irig_count > 0)
+ printf "irig %d, err %d\n", irig_count, irig_error
+#
+# tdata summary data
+#
+# tdata record count
+# m M master OK-count, mean level (dB)
+# w W slave OK-count, mean level (dB)
+# x X slave OK-count, mean level (dB)
+# y Y slave OK-count, mean level (dB)
+# z Z slave OK-count, mean level (dB)
+#
+ if (tdata_count > 0 ) {
+ if (tdata_m > 0)
+ m /= tdata_count
+ if (tdata_x > 0)
+ w /= tdata_count
+ if (tdata_x > 0)
+ x /= tdata_count
+ if (tdata_y > 0)
+ y /= tdata_count
+ if (tdata_z > 0)
+ z /= tdata_count
+ printf "tdata %d, m %d %.1f, w %d %.1f, x %d %.1f, y %d %.1f, z %d %.1f\n", tdata_count, tdata_m, m, tdata_w, w, tdata_x, x, tdata_y, y, tdata_z, z
+ }
+#
+# itf summary data
+#
+# itf record count
+# rms itf rms error (ns)
+# >200 itf error > 200 ns
+# >100 itf error > 100 ns
+# var Allan variance
+#
+ if (itf_count > 1) {
+ itf_mean /= itf_count
+ itf_rms = sqrt(itf_rms / itf_count - itf_mean * itf_mean) * 1e9
+ itf_var = sqrt(itf_var / (2 * (itf_count - 1)))
+ printf "itf %d, rms %.1f, >200 %d, >100 %d, var %.2e\n", itf_count, itf_rms, itf_200, itf_100, itf_var
+ }
+#
+# etf summary data
+#
+# etf record count
+# mean etf mean (ns)
+# rms etf rms error (ns)
+# max etf maximum (ns)
+# min etf minimum (ns)
+# var Allan variance
+#
+ if (etf_count > 0) {
+ etf_mean /= etf_count
+ etf_rms = sqrt(etf_rms / etf_count - etf_mean * etf_mean)
+ etf_var = sqrt(etf_var / (2 * (etf_count - 1)))
+ printf "etf %d, mean %.1f, rms %.1f, max %d, min %d, var %.2e\n", etf_count, etf_mean, etf_rms, etf_max, etf_min, etf_var
+ }
+#
+# trstat summary data
+#
+# trstat record count
+# sat histogram of tracked satellites (0 - 7)
+#
+ if (trstat_count > 0)
+ printf "trstat %d, sat %d %d %d %d %d %d %d %d\n", trstat_count, trstat_sat[0], trstat_sat[1], trstat_sat[2], trstat_sat[2], trstat_sat[3], trstat_sat[4], trstat_sat[5], trstat_sat[6], trstat_sat[7]
+#
+# osc summary data
+#
+# osc record count
+# control control midrange (V) +/- deviation (mV)
+# temp oven temperature midrange +/- deviation (deg C)
+#
+ if (osc_count > 0)
+ printf "osc %d, control %.3f+/-%.3f, temp %.1f+/-%.2f\n", osc_count, (osc_vmax + osc_vmin) / 2, (osc_vmax - osc_vmin) / 2 * 1e3, (osc_tmax + osc_tmin) / 2, (osc_tmax - osc_tmin) / 2
+}
diff --git a/usr.sbin/xntpd/scripts/stats/clock.sh b/usr.sbin/xntpd/scripts/stats/clock.sh
new file mode 100755
index 0000000..1866d55
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/clock.sh
@@ -0,0 +1,17 @@
+#!/bin/csh
+#
+# Script to summarize clockstats files
+#
+set x = `ls clockstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f clock.awk $dayfile
+ awk -f itf.awk $dayfile >>itf
+ awk -f etf.awk $dayfile >>etf
+ awk -f ensemble.awk $dayfile >>ensemble
+ awk -f tdata.awk $dayfile >>tdata
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/dupe.awk b/usr.sbin/xntpd/scripts/stats/dupe.awk
new file mode 100644
index 0000000..317c2a4
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/dupe.awk
@@ -0,0 +1,8 @@
+#
+# delete duplicate lines
+#
+{
+ if (old != $0)
+ printf "%s\n", $0
+ old = $0
+}
diff --git a/usr.sbin/xntpd/scripts/stats/ensemble.S b/usr.sbin/xntpd/scripts/stats/ensemble.S
new file mode 100644
index 0000000..32a4dba
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/ensemble.S
@@ -0,0 +1,5 @@
+ensemble <- scan(file1, list(day=0, sec=0, gps=0, gpsw=0, loran=0, loranw=0, ensemble=0, std=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck = 0.03, mar = c(2, 2, 1, 1))
+plot(ensemble$sec, ensemble$ensemble, type="l", xlab=paste("MJD", ensemble$day, "Time (s)"), ylab="Ensemble Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/ensemble.awk b/usr.sbin/xntpd/scripts/stats/ensemble.awk
new file mode 100644
index 0000000..136b33d
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/ensemble.awk
@@ -0,0 +1,17 @@
+# program to produce loran ensemble statistics from clockstats files
+#
+# usage: awk -f ensemble.awk clockstats
+#
+# format of input record (time values in seconds)
+# 49165 8.628 127.127.10.1 93:178:00:00:07.241 LORAN ENSEMBLE
+# -6.43E-08 +5.02E-08 .091 +5.98E-08 +1.59E-08 .909 +4.85E-08 +3.52E-08
+#
+# format of output record (time values in nanoseconds)
+# MJD sec GPS wgt LORAN wgt avg sigma
+# 49165 8.628 -64.3 0.091 59.8 0.909 48.5 35.2
+#
+# select LORAN ENSEMBLE records with valid format and weights
+{
+ if (NF >= 14 && $6 == "ENSEMBLE" && $9 > 0 && $12 > 0)
+ printf "%5s %9.3f %7.1f %6.3f %7.1f %6.3f %7.1f %7.1f\n", $1, $2, $7*1e9, $9, $10*1e9, $12, $13*1e9, $14*1e9
+}
diff --git a/usr.sbin/xntpd/scripts/stats/etf.S b/usr.sbin/xntpd/scripts/stats/etf.S
new file mode 100644
index 0000000..9b9c68b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/etf.S
@@ -0,0 +1,15 @@
+options(digits=4)
+file2 <- "etf_summary"
+etf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+r <- lsfit(etf$sec, etf$offset)
+count<-length(etf$sec)
+mean<-r$coef[[1]]
+std<-sqrt(var(r$residuals))
+slope<-r$coef[[2]] * 1000
+cat("\n", file=file2 , append=TRUE, fill=FALSE, sep="")
+cat(file1, "\n", file=file2, append=TRUE, fill=FALSE, sep="")
+cat("etf1 ", count, ", T ", mean, " ns, R ", slope, " ps/s, std ", std, " us\n", file=file2, append=TRUE, fill=FALSE, sep="")
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(etf$sec, etf$offset, type="l", xlab=paste("MJD", etf$day, "Time (s)"), ylab="External Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/etf.awk b/usr.sbin/xntpd/scripts/stats/etf.awk
new file mode 100644
index 0000000..8e6e334
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/etf.awk
@@ -0,0 +1,19 @@
+# program to produce external time/frequence statistics from clockstats files
+#
+# usage: awk -f etf.awk clockstats
+#
+# format of input record
+# 49165 40.473 127.127.10.1 93:178:00:00:39.238 ETF
+# +175.0 +176.8 2.0 +3.729E-11 +1.000E-10 +3.511E-11 4.005E-13 500
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49165 40.473 175.0 3.729e-11
+#
+# select ETF records with valid format
+{
+ if (NF >= 9 && $5 == "ETF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $6, $9
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/itf.S b/usr.sbin/xntpd/scripts/stats/itf.S
new file mode 100644
index 0000000..56c8c8d
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/itf.S
@@ -0,0 +1,5 @@
+itf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(itf$sec, itf$offset, type="l", xlab=paste("MJD", itf$day, "Time (s)"), ylab="Internal Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/itf.awk b/usr.sbin/xntpd/scripts/stats/itf.awk
new file mode 100644
index 0000000..2b21c5b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/itf.awk
@@ -0,0 +1,19 @@
+# program to produce intewrnal time/frequence statistics from clockstats files
+#
+# usage: awk -f itf.awk clockstats
+#
+# format of input record
+# 49227 67.846 127.127.10.1 93:240:00:00:51.816 ITF
+# COCO 0 +2.0579E-07 -3.1037E-08 -7.7723E-11 +6.5455E-10 500.00 4.962819
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49227 67.846 +2.0579E-07 -7.7723E-11
+#
+# select ITF records with valid format
+{
+ if (NF >= 10 && $5 == "ITF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $8 * 1e9, $10
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/loop.S b/usr.sbin/xntpd/scripts/stats/loop.S
new file mode 100644
index 0000000..8e564b6
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.S
@@ -0,0 +1,7 @@
+options(digits=4)
+loop <- scan(file1, list(day=0, sec=0, offset=0, freq=0, tc=0))
+loop$offset <- loop$offset * 1e6
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(loop$sec, loop$offset, type="l", xlab=paste("MJD", loop$day, "Time (s)"), ylab="PLL Offset (us)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/loop.awk b/usr.sbin/xntpd/scripts/stats/loop.awk
new file mode 100644
index 0000000..470b27c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.awk
@@ -0,0 +1,41 @@
+# awk program to scan loopstats files and report errors/statistics
+#
+# usage: awk -f loop.awk loopstats
+#
+# format of loopstats record
+# MJD sec time (s) freq (ppm) tc
+# 49235 3.943 0.000016 22.4716 0
+#
+BEGIN {
+ loop_tmax = loop_fmax = -1e9
+ loop_tmin = loop_fmin = 1e9
+}
+#
+# scan all records in file
+#
+{
+ if (NF >= 5) {
+ loop_count++
+ if ($3 > loop_tmax)
+ loop_tmax = $3
+ if ($3 < loop_tmin)
+ loop_tmin = $3
+ if ($4 > loop_fmax)
+ loop_fmax = $4
+ if ($4 < loop_fmin)
+ loop_fmin = $4
+ loop_time += $3
+ loop_time_rms += $3 * $3
+ loop_freq += $4
+ loop_freq_rms += $4 * $4
+ }
+} END {
+ if (loop_count > 0) {
+ loop_time /= loop_count
+ loop_time_rms = sqrt(loop_time_rms / loop_count - loop_time * loop_time)
+ loop_freq /= loop_count
+ loop_freq_rms = sqrt(loop_freq_rms / loop_count - loop_freq * loop_freq)
+ printf "loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n", loop_count, (loop_tmax + loop_tmin) / 2 * 1e6, (loop_tmax - loop_tmin) / 2 * 1e6, loop_time_rms * 1e6, (loop_fmax + loop_fmin) / 2, (loop_fmax - loop_fmin) / 2, loop_freq_rms
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/loop.sh b/usr.sbin/xntpd/scripts/stats/loop.sh
new file mode 100755
index 0000000..619eeb8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.sh
@@ -0,0 +1,13 @@
+#!/bin/csh
+#
+# Script to summarize loopstats files
+#
+set x = `ls loopstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f loop.awk $dayfile
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/peer.awk b/usr.sbin/xntpd/scripts/stats/peer.awk
new file mode 100644
index 0000000..4cb48cd
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/peer.awk
@@ -0,0 +1,57 @@
+# awk program to scan peerstats files and report errors/statistics
+#
+# usage: awk -f peer.awk peerstats
+#
+# format of peerstats record
+# MJD sec ident stat offset (s) delay (s) disp (s)
+# 49235 11.632 128.4.2.7 f414 -0.000041 0.21910 0.00084
+#
+BEGIN {
+ n = 0
+ MAXDISTANCE = 1.0
+}
+#
+# scan all records in file
+#
+{
+ if (NF >= 7 && ($7 + $6 / 2) < MAXDISTANCE) {
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($3 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $3
+ peer_tmax[i] = peer_dist[i] = -1e9
+ peer_tmin[i] = 1e9
+ n++
+ }
+ peer_count[i]++
+ if ($5 > peer_tmax[i])
+ peer_tmax[i] = $5
+ if ($5 < peer_tmin[i])
+ peer_tmin[i] = $5
+ dist = $7 + $6 / 2
+ if (dist > peer_dist[i])
+ peer_dist[i] = dist
+ peer_time[i] += $5
+ peer_time_rms[i] += $5 * $5
+ peer_delay[i] += $6
+ peer_disp[i] += $7
+ }
+} END {
+ printf " ident cnt mean rms max delay dist disp\n"
+ printf "==========================================================================\n"
+ for (i = 0; i < n; i++) {
+ peer_time[i] /= peer_count[i]
+ peer_time_rms[i] = sqrt(peer_time_rms[i] / peer_count[i] - peer_time[i] * peer_time[i])
+ peer_delay[i] /= peer_count[i]
+ peer_disp[i] /= peer_count[i]
+ peer_tmax[i] = peer_tmax[i] - peer_time[i]
+ peer_tmin[i] = peer_time[i] - peer_tmin[i]
+ if (peer_tmin[i] > peer_tmax[i])
+ peer_tmax[i] = peer_tmin[i]
+ printf "%15s%5d%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", peer_ident[i], peer_count[i], peer_time[i] * 1e3, peer_time_rms[i] * 1e3, peer_tmax[i] * 1e3, peer_delay[i] * 1e3, peer_dist[i] * 1e3, peer_disp[i] * 1e3
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/peer.sh b/usr.sbin/xntpd/scripts/stats/peer.sh
new file mode 100755
index 0000000..b5d8d29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/peer.sh
@@ -0,0 +1,13 @@
+#!/bin/csh
+#
+# Script to summarize peerstats files
+#
+set x = `ls peerstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f peer.awk $dayfile
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/psummary.awk b/usr.sbin/xntpd/scripts/stats/psummary.awk
new file mode 100644
index 0000000..5ef8d8e
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/psummary.awk
@@ -0,0 +1,43 @@
+# program to scan peer_summary file and produce summary of daily summaries
+#
+# usage: awk -f psummary.awk peer_summary
+#
+{
+ if (NF < 8 || $1 == "ident")
+ continue
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($1 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $1
+ n++
+ }
+ peer_count[i]++
+ if (($7 - $6 / 2) < 400) {
+ peer_count[i]++
+ peer_mean[i] += $3
+ peer_var[i] += $4 * $4
+ if ($5 > peer_max[i])
+ peer_max[i] = $5
+ if ($5 > 1)
+ peer_1[i]++
+ if ($5 > 5)
+ peer_2[i]++
+ if ($5 > 10)
+ peer_3[i]++
+ if ($5 > 50)
+ peer_4[i]++
+ }
+} END {
+ printf " host cnt mean rms max >1 >5 >10 >50\n"
+ printf "=================================================================\n"
+ for (i = 0; i < n; i++) {
+ if (peer_count[i] <= 0)
+ continue
+ peer_mean[i] /= peer_count[i]
+ peer_var[i] = sqrt(peer_var[i] / peer_count[i])
+ printf "%15s%4d%10.3f%10.3f%10.3f%4d%4d%4d%4d\n", peer_ident[i], peer_count[i], peer_mean[i], peer_var[i], peer_max[i], peer_1[i], peer_2[i], peer_3[i], peer_4[i]
+ }
+}
diff --git a/usr.sbin/xntpd/scripts/stats/rms.awk b/usr.sbin/xntpd/scripts/stats/rms.awk
new file mode 100644
index 0000000..34d612a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/rms.awk
@@ -0,0 +1,41 @@
+# program to scan peer_summary file
+#
+{
+ if (NF < 8 || $1 == "ident")
+ continue
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($1 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $1
+ n++
+ }
+ peer_count[i]++
+ if (($7 - $6 / 2) < 400) {
+ peer_count[i]++
+ peer_mean[i] += $3
+ peer_var[i] += $4 * $4
+ if ($5 > peer_max[i])
+ peer_max[i] = $5
+ if ($5 > 1)
+ peer_1[i]++
+ if ($5 > 5)
+ peer_2[i]++
+ if ($5 > 10)
+ peer_3[i]++
+ if ($5 > 50)
+ peer_4[i]++
+ }
+} END {
+ printf " host cnt mean sd max >1 >5 >10 >50\n"
+ printf "=================================================================\n"
+ for (i = 0; i < n; i++) {
+ if (peer_count[i] <= 0)
+ continue
+ peer_mean[i] /= peer_count[i]
+ peer_var[i] = sqrt(peer_var[i] / peer_count[i])
+ printf "%15s%4d%10.3f%10.3f%10.3f%4d%4d%4d%4d\n", peer_ident[i], peer_count[i], peer_mean[i], peer_var[i], peer_max[i], peer_1[i], peer_2[i], peer_3[i], peer_4[i]
+ }
+}
diff --git a/usr.sbin/xntpd/scripts/stats/summary.sh b/usr.sbin/xntpd/scripts/stats/summary.sh
new file mode 100755
index 0000000..19b64a0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/summary.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# Script to summarize ipeerstats, loopstats and clockstats files
+#
+# This script can be run from a cron job once per day, week or month. It
+# runs the file-specific summary script and appends the summary data to
+# designated files.
+#
+DATE=`date +%Y%m%d`
+SIN=S.in
+SOUT=S.out
+LOOP=loop_summary
+PEER=peer_summary
+CLOCK=clock_summary
+
+rm -f $SIN $SOUT
+S=0
+if [ -f `which S | cut -f1 -d" "` ]; then
+ S=1
+fi
+#
+# Summarize loopstats files
+#
+for f in loopstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$LOOP
+ echo $f >>$LOOP
+ awk -f loop.awk $f >>$LOOP
+ if [ $S ]; then
+ echo "file1<-"\"${f}\" >>$SIN
+ echo "source("\""loop.S"\"")" >>$SIN
+ fi
+ rm -f $f
+ fi
+done
+
+#
+# Summarize peerstats files
+#
+for f in peerstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$PEER
+ echo $f >>$PEER
+ awk -f peer.awk $f >>$PEER
+ rm -f $f
+ fi
+done
+
+#
+# Summarize clockstats files
+#
+for f in clockstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$CLOCK
+ echo $f >>$CLOCK
+ awk -f clock.awk $f >>$CLOCK
+ if [ -f /dev/gps* ]; then
+ awk -f itf.awk $f >itf.$d
+ awk -f etf.awk $f >etf.$d
+ awk -f ensemble.awk $f >ensemble.$d
+ awk -f tdata.awk $f >tdata.$d
+ fi
+ rm -f $f
+ fi
+done
+
+#
+# Process clockstat files with S and generate PostScript plots
+#
+for f in itf etf ensemble tdata; do
+ for d in ${f}.????????; do
+ if [ -f $d ]; then
+ if [ $S ]; then
+ echo "file1<-"\"${d}\" >>$SIN
+ echo "source("\"${f}.S\"")" >>$SIN
+ echo "unix("\""rm ${d}"\"")" >>$SIN
+ else
+ rm -f $d
+ fi
+ fi
+ done
+done
+if [ -f $SIN ]; then
+ S BATCH $SIN $SOUT
+fi
diff --git a/usr.sbin/xntpd/scripts/stats/tdata.S b/usr.sbin/xntpd/scripts/stats/tdata.S
new file mode 100644
index 0000000..f360a24
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/tdata.S
@@ -0,0 +1,5 @@
+tdata <- scan(file1, list(day=0, sec=0, m=0, w=0, x=0, y=0, z=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(tdata$sec, tdata$m, type="l", xlab=paste("MJD", tdata$day, "Time (s)"), ylab="LORAN-M SNR (dB)")
diff --git a/usr.sbin/xntpd/scripts/stats/tdata.awk b/usr.sbin/xntpd/scripts/stats/tdata.awk
new file mode 100644
index 0000000..04d7e6a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/tdata.awk
@@ -0,0 +1,45 @@
+# program to produce loran tdata statistics from clockstats files
+#
+# usage: awk -f tdata.awk clockstats
+#
+# format of input record (missing replaced by -40.0)
+# 49228 36.852 127.127.10.1 93:241:00:00:20.812 LORAN TDATA
+# M OK 0 0 1169.14 -7.4 3.16E-07 .424
+# W CV 0 0 3329.30 -16.4 1.81E-06
+# X OK 0 0 1737.19 -10.5 3.44E-07 .358
+# Y OK 0 0 2182.07 -9.0 4.41E-07 .218
+#
+# format of output record (signal values are in dB)
+# MJD sec time M W X Y Z
+# 49228 36.852 175.0 -7.4 -16.4 -10.5 -9.0
+#
+# select LORAN TDATA records with valid format
+{
+ if (NF >= 7 && $6 == "TDATA") {
+ m = w = x = y = z = -40.0
+ for (i = 7; i < NF - 5; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m = $i
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w = $i
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x = $i
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y = $i
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z = $i
+ }
+ }
+ printf "%5s %9.3f %6.1f %6.1f %6.1f %6.1f %6.1f\n", $1, $2, m, w, x, y, z
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/support/README b/usr.sbin/xntpd/scripts/support/README
new file mode 100644
index 0000000..812965b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/README
@@ -0,0 +1,73 @@
+The bin and etc directories contain several scripts (sh and perl) that
+should ease startup and configuration of NTP sites.
+
+ bin/monl is a monitoring script that prints out new, current and
+ old customers of an NTP timeserver when monitoring is
+ in effect.
+ monl has following options:
+ -i <regexp> (regular expression matchin IP addres to be ignored
+ -d <directory> where the current state is kept (default /tmp)
+ -v debug output
+ -n do not translate IP addresses into hostnames
+ <host> host to be analyzed
+
+ monl uses xntpdc for information gathering and is thus
+ limited to the NTP version xntpdc is compiled for.
+
+ bin/mvstats moves compresses and removes statistics files (useful mainly
+ for reference servers
+
+ etc/install creates the locally needed directories for NTP (if not residung in /etc)
+
+ etc/rc starts up daemon with configuration file and key file
+ etc/cron cron called monitor statistic (uses bin/monl)
+ etc/crontab crontab prototype for reference time servers
+ etc/setup sh script sourced by the other scripts for variable setup
+
+YOU MUST EDIT THESE FILES TO REFLECT YOUR LOCAL SETUP !
+
+READ THIS BEFORE USING THE STARTUP SCRIPTS
+
+The startupscript etc/rc has been written for Suns and HPs. They are not
+guaranteed to work elsewhere. Following assumptions have been made:
+
+ All NTP related files reside in ONE directory having following structure:
+
+ bin/* - all executables (daemon, control, date, scripts)
+ etc/* - startup scripts and cron scripts
+ conf/* - NTP configuration files
+
+The variable NTPROOT (etc/rc, etc/install) must be edited to reflect
+the NTP directory (e.g. /usr/local/NTP)
+
+NTP config files are located via Suns arch command and have the name
+conf/`arch`.`arch -k`.
+These are the default configurations (usually clients). If a file with the name
+conf/`arch`.`arch -k`.`hostname` is present this file will be preferred (Reference host,
+gateway). If the arch command is not available no-arch is used. The arch command
+is usually a shell script which echoes a string unique the the current machine
+architecture.
+
+The tickadj command has its own conf/tickconf file which is used to set host
+specific tickadj values. The line with DEFAULT specifies the default tickadj
+parameters, all other lines consists of <hostname> <hostid>
+<tickadj parameters>. These lines need only be entered if the specified host
+needs parameters different from the default parameters.
+
+Reference clock support is provided for DCF77. If you need to initialize
+certain things for reference clock support (e.g. loading STREAMS modules),
+you need to edit etc/rc.
+
+The current config files of Erlangen are included in the conf directory.
+They are just for reference, but might help you a bit in setting up a
+synchronisation network.
+
+The advantage of keeping all config files centralized is the easier
+administration.
+
+We replicate the NTP directory via NFS and rdist.
+
+When you have set up the local config files (YOUR OWN!) you can call
+<NTPROOT>/etc/rc for daemon startup.
+
+For more information: time@informatik.uni-erlangen.de
diff --git a/usr.sbin/xntpd/scripts/support/bin/monl b/usr.sbin/xntpd/scripts/support/bin/monl
new file mode 100755
index 0000000..f0c48db
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/bin/monl
@@ -0,0 +1,213 @@
+#!/local/bin/perl
+
+%service = ( 0, "unspec",
+ 1, "Active",
+ 2, "Passive",
+ 3, "Client",
+ 4, "Server",
+ 5, "Broadcast",
+ 6, "Control",
+ 7, "Private" );
+%nc = ();
+@ignpat = ();
+$noname = 0;
+$verbose = 0;
+$retries = 5;
+$lastkey = 0;
+
+sub timedelta {
+ local($tm, $days, $h, $m, $s);
+
+ $tm = @_[$[];
+ $days = 0;
+ $days = sprintf("%dd+", $days) if $days = int($tm / (60*60*24));
+ $days = "" unless $days;
+ $tm = $tm % (60*60*24);
+ $h = int($tm / (60*60));
+ $tm = $tm % (60*60);
+ $m = int($tm / 60);
+ $s = $tm % 60;
+
+ return sprintf("%s%02d:%02d:%02d", $days, $h, $m, $s);
+}
+
+sub listentry {
+ local($host, $mode) = split("$;" , @_[$[]);
+ local($count, $version, $firsttime) = split("$;" , $_[$[+1]);
+ local($name);
+
+ if (grep($host =~ m/$_/, @ignpat))
+ {
+ print "ignored $host ...\n" if $verbose;
+ return;
+ }
+
+ return if ! $count;
+
+ if (defined($nc{$host}))
+ {
+ $name = $nc{$host};
+ }
+ else
+ {
+ if ($noname)
+ {
+ $nc{$host} = $name = $host;
+ }
+ else
+ {
+ $name = (gethostbyaddr(pack("C4", split(/\./, $host)), 2))[$[];
+ $nc{$host} = $name = $host if ! defined($name);
+ }
+ }
+
+ printf ($fmt, ($lastkey eq $host) ? "" : $name, $service{$mode}, $count, $version, &timedelta($firsttime), $firsttime / $count);
+
+ if (@_[$[+2])
+ {
+ $hostcnt++ if $lastkey ne $host;
+ $packcnt += $count;
+ $maxtime = $firsttime if $firsttime > $maxtime;
+ }
+
+ $lastkey = $host;
+}
+
+while ($ARGV[$[] =~ /^-[nvid]$/)
+ {
+ if ($ARGV[$[] eq "-i")
+ {
+ shift;
+ push(@ignpat, shift) unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-d")
+ {
+ shift;
+ $dir = shift unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-n")
+ {
+ shift;
+ $noname = 1;
+ }
+ elsif ($ARGV[$[] eq "-v")
+ {
+ shift;
+ $verbose = 1;
+ }
+ }
+
+$dir = "/tmp" unless defined($dir);
+$gone = 60*60*48;
+$fmt = "%48s %10s %7d %7d %13s %14.3f\n";
+$sfmt = "%48s %10s %7s %7s %13s %14s\n";
+@lbl = ("Host", "Mode", "Count", "Version", "Time active", "Packetinterval");
+
+if (!defined($ARGV[$[]))
+ {
+ $hostname = `hostname`;
+ chop($hostname);
+ unshift(@ARGV, $hostname);
+ }
+
+foreach $hostname (@ARGV)
+ {
+ $dbmfile = $dir . "/monlstats-" . $hostname;
+ $monl = "xntpdc -c 'hostnames no' -c monl $hostname | tail +3 |";
+ $hostcnt = 0;
+ $packcnt = 0;
+ $maxtime = 0;
+ %Seen = ();
+ %New = ();
+ %Old = ();
+
+ print "Monitor Status of $hostname\n\n";
+
+ $cnt = $retries;
+ do
+ {
+ open(MONL, $monl) || die("$monl failed $!");
+ @monlout = <MONL>;
+ close(MONL);
+ } while (! @monlout && $cnt--);
+
+ if (! @monlout)
+ {
+ print "not available.\n";
+ next;
+ }
+
+ dbmopen(Clients, $dbmfile, 0644) || die("dbmopen(.., $dbmfile, ...): $!");
+
+ foreach (@monlout)
+ {
+ chop;
+ split;
+ ($host, $count, $mode, $version, $lasttime, $firsttime) =
+ (@_[$[, $[+2 .. $[+4, $#_-1,$#_]);
+
+ $Seen{$host, $mode} = 1;
+
+ if (!defined($Clients{$host, $mode}))
+ {
+ if ($lasttime <= $gone)
+ {
+ ## got a new one
+ $Clients{$host, $mode} = $New{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ else
+ {
+ ## throw out the old ones
+ if ($lasttime > $gone)
+ {
+ $Old{$host, $mode} = $Clients{$host, $mode};
+ delete $Clients{$host, $mode};
+ }
+ else
+ {
+ $Clients{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ }
+
+ grep(($Seen{$_} || ($Old{$_} = delete $Clients{$_})), keys(%Clients));
+
+ if (grep(($tmp = $_ , !grep($tmp =~ m/$_/, @ignpat)), keys(%New)))
+ {
+ print "New customers\n";
+ print "-------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $New{$_}, 1), sort(keys(%New)) );
+ print "\n";
+ }
+
+
+ if (grep((!defined($New{$_}) && ($tmp = $_, !grep($tmp =~ m/$_/, @ignpat))), keys(%Clients)))
+ {
+ print "Current customers\n";
+ print "-----------------\n";
+ printf $sfmt, @lbl;
+ grep( defined($New{$_}) || &listentry($_, $Clients{$_}, 1) , sort(keys(%Clients)) );
+ print "\n";
+ }
+
+ if (grep(($tmp = $_, !grep($tmp =~ m/$_/, @ignpat)), keys(%Old)))
+ {
+ print "Discarded customers\n";
+ print "-------------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $Old{$_}, 0) , sort(keys(%Old)) );
+ print "\n";
+ }
+
+ dbmclose(Clients);
+
+ print "\nSummary:\n";
+ print "--------\n";
+ printf("Elapsed time: %13s\n", &timedelta($maxtime));
+ printf(" Hosts: %13d\n", $hostcnt);
+ printf(" Packets: %13d\n", $packcnt);
+ printf(" Rate: %13.2f\n", $packcnt / $maxtime) if $maxtime;
+ print "\n";
+ }
diff --git a/usr.sbin/xntpd/scripts/support/bin/mvstats b/usr.sbin/xntpd/scripts/support/bin/mvstats
new file mode 100755
index 0000000..e33dc792
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/bin/mvstats
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# mvstats,v 3.1 1993/07/06 01:10:24 jbj Exp
+#
+# mvstats is called by cron for keeping the log files together
+# usually only used on reference hosts
+#
+# Files reside in /var/NTP
+# Files older than 2 days will be compressed,
+# Files older than 64 days will be removed.
+#
+# mvstats,v
+# Revision 3.1 1993/07/06 01:10:24 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:24 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+cd /var/NTP
+find . ! -name '*.Z' -mtime +2 -exec compress -f {} \;
+find . -mtime +64 -exec rm -f {} \;
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp300.hp300 b/usr.sbin/xntpd/scripts/support/conf/hp300.hp300
new file mode 100644
index 0000000..7b18758
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp300.hp300
@@ -0,0 +1,70 @@
+#
+# FAU NTP client configuration file
+#
+# hp300.hp300,v 3.1 1993/07/06 01:10:27 jbj Exp
+#
+# hp300.hp300,v
+# Revision 3.1 1993/07/06 01:10:27 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:29 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/09/24 06:10:46 kardel
+# authdelay adjust
+#
+# Revision 1.1 1992/09/24 06:09:23 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000436 # hp300
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp700.hp700 b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700
new file mode 100644
index 0000000..911ff10
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700
@@ -0,0 +1,67 @@
+#
+# FAU NTP client configuration file
+#
+# hp700.hp700,v 3.1 1993/07/06 01:10:29 jbj Exp
+#
+# hp700.hp700,v
+# Revision 3.1 1993/07/06 01:10:29 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:31 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/09/24 06:09:02 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000016 # hp700
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47 b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47
new file mode 100644
index 0000000..80c72a6
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47
@@ -0,0 +1,71 @@
+#
+# FAU NTP client configuration file
+#
+# hp700.hp700.faui47,v 3.1 1993/07/06 01:10:30 jbj Exp
+#
+# hp700.hp700.faui47,v
+# Revision 3.1 1993/07/06 01:10:30 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:33 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/09/24 14:53:10 kirschni
+# Initial revision
+#
+# Revision 1.1 1992/09/24 06:09:02 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+broadcast 131.188.54.255
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000016 # hp700
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp800.hp800 b/usr.sbin/xntpd/scripts/support/conf/hp800.hp800
new file mode 100644
index 0000000..58f4706
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp800.hp800
@@ -0,0 +1,70 @@
+#
+# FAU NTP client configuration file
+#
+# hp800.hp800,v 3.1 1993/07/06 01:10:31 jbj Exp
+#
+# hp800.hp800,v
+# Revision 3.1 1993/07/06 01:10:31 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:35 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/09/24 06:10:46 kardel
+# authdelay adjust
+#
+# Revision 1.1 1992/09/24 06:09:23 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000088 # hp800
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.conf b/usr.sbin/xntpd/scripts/support/conf/ntp.conf
new file mode 100644
index 0000000..06f5482
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.conf
@@ -0,0 +1,36 @@
+#
+# peers - local synch setup
+#
+#server ntps1-0 key 0 version 2
+#server ntps1-1 key 0 version 2
+#server ntps2-0 key 0 version 2
+#server ntps2-1 key 0 version 2
+broadcastclient yes
+#broadcastdelay # use default, until we measure something
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000629
+requestkey 65634
+controlkey 65635
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nopeer nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # allow refclocks
+restrict 127.0.0.1 mask 255.255.255.255 # allow local config
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify# allow local hosts
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.keys b/usr.sbin/xntpd/scripts/support/conf/ntp.keys
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.keys
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb b/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun3.sun3 b/usr.sbin/xntpd/scripts/support/conf/sun3.sun3
new file mode 100644
index 0000000..06f5482
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun3.sun3
@@ -0,0 +1,36 @@
+#
+# peers - local synch setup
+#
+#server ntps1-0 key 0 version 2
+#server ntps1-1 key 0 version 2
+#server ntps2-0 key 0 version 2
+#server ntps2-1 key 0 version 2
+broadcastclient yes
+#broadcastdelay # use default, until we measure something
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000629
+requestkey 65634
+controlkey 65635
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nopeer nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # allow refclocks
+restrict 127.0.0.1 mask 255.255.255.255 # allow local config
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify# allow local hosts
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01
new file mode 100644
index 0000000..8927535
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01
@@ -0,0 +1,83 @@
+#
+# NTP v3 configuration file for faui01
+#
+# sun4.sun4.faui01,v 3.1 1993/07/06 01:10:37 jbj Exp
+#
+# sun4.sun4.faui01,v
+# Revision 3.1 1993/07/06 01:10:37 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:44 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.3 1992/10/15 10:56:01 kardel
+# -60 has 0 broadcasts now
+#
+# Revision 1.2 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.1 1992/06/09 13:40:44 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -14 # kernel fix - HIREZ timer
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# get time from local network - hope this is reasonably stable
+#
+broadcastclient yes
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.0 key 0 version 3
+broadcast 131.188.61.0 version 3 # inf1-net.revue (still on 2)
+broadcast 131.188.62.0 version 3 # inf4-net1.revue (still on 2)
+
+#
+# Statistics
+#
+monitor yes
+#statfile /var/NTP/statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-0
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10
new file mode 100644
index 0000000..3be93a9
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10
@@ -0,0 +1,176 @@
+#
+# NTP v3 configuration file for faui45
+#
+# sun4.sun4.faui10,v 3.1 1993/07/06 01:10:38 jbj Exp
+#
+# sun4.sun4.faui10,v
+# Revision 3.1 1993/07/06 01:10:38 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:31 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:46 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.11 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.10 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.9 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.8 1992/08/14 21:51:04 kardel
+# local clock is now preferred peer
+#
+# Revision 1.7 1992/07/19 14:19:26 kardel
+# fixed broadcasts
+#
+# Revision 1.6 1992/07/17 17:12:43 kardel
+# new statistics support
+#
+# Revision 1.5 1992/07/10 07:46:03 kardel
+# added loopstats statistic file
+#
+# Revision 1.4 1992/06/26 07:30:32 kardel
+# update for reference clock support
+#
+# Revision 1.3 1992/05/18 13:51:04 kardel
+# precision fix
+#
+# Revision 1.2 1992/03/30 11:16:07 kardel
+# ntps1-1 version 3
+#
+# Revision 1.1 1992/01/14 12:30:21 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# We want to provide timed service too, thus (startup script magic)
+# TIMED
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-0 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+# US Servers
+#
+server truechimer.cso.uiuc.edu version 2
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.31.0 version 3 # inf1-net.revue
+broadcast 131.188.34.0 version 3 # inf4-net1.revue
+broadcast 131.188.44.0 version 3 # inf4-net2.revue
+broadcast 131.188.1.255 version 3 # revue.revue
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.30.1 mask 255.255.255.255 # ntps1-1
+#
+# external trust
+#
+restrict 130.126.174.40 mask 255.255.255.255 nomodify # truechimer.cso.uiuc.edu
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45
new file mode 100644
index 0000000..57e77f2
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45
@@ -0,0 +1,228 @@
+#
+# NTP v3 configuration file for faui45
+#
+# sun4.sun4.faui45,v 3.1 1993/07/06 01:10:39 jbj Exp
+#
+# sun4.sun4.faui45,v
+# Revision 3.1 1993/07/06 01:10:39 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:33 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:48 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.28 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.27 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.26 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.25 1992/09/04 12:48:44 kardel
+# dcn1 -> churchy
+#
+# Revision 1.24 1992/08/14 21:42:20 kardel
+# local clock is now preferred peer
+#
+# Revision 1.23 1992/07/17 17:11:51 kardel
+# new statistics support
+#
+# Revision 1.22 1992/07/05 22:41:18 root
+# using default module settings now
+#
+# Revision 1.21 1992/07/02 11:47:26 root
+# loop statistics added
+#
+# Revision 1.20 1992/06/26 07:30:51 kardel
+# corrected examples
+#
+# Revision 1.19 1992/06/18 16:56:05 kardel
+# running timed too (startup script magic)
+#
+# Revision 1.18 1992/06/18 13:58:45 kardel
+# precision adjusted (us resolution)
+# clock definition explanation
+#
+# Revision 1.17 1992/06/13 12:49:35 root
+# allowed ntps1-1
+#
+# Revision 1.16 1992/06/07 11:44:41 kardel
+# switch to PPS support for dcf77-0
+#
+# Revision 1.15 1992/06/03 14:02:58 kardel
+# new version (fausup notrust)
+#
+# Revision 1.14 1992/05/18 13:49:45 kardel
+# first precision update due to kernel patch
+#
+# Revision 1.13 1992/05/18 13:48:36 kardel
+# more updates
+#
+# Revision 1.12 1992/03/24 08:43:49 kardel
+# now trusting netserv.rz.uni-karlsruhe.de
+#
+# Revision 1.11 1992/03/23 15:03:43 kardel
+# sunmanager.lrz-muenchen.de is a peer
+#
+# Revision 1.10 1992/03/12 22:49:53 kardel
+# well, got to switch fudge too
+#
+# Revision 1.9 1992/03/12 22:47:07 kardel
+# adjust for next xntpv3 alpha release
+#
+# Revision 1.8 1992/02/07 11:07:35 kardel
+# switched to Meinberg PZF 535/OCXO
+#
+# Revision 1.7 1992/01/21 15:11:38 kardel
+# netserv & sunmanager must be configured server (botch on other side)
+#
+# Revision 1.6 1992/01/17 17:54:34 kardel
+# added ntps2-0, ntps2-1 to unrestricted list
+#
+# Revision 1.5 1992/01/10 10:49:03 kardel
+# Authentication correction
+#
+# Revision 1.4 1992/01/10 08:08:06 kardel
+# peer apple.com added
+# ntps1-1 added to restrictionlist
+#
+# Revision 1.3 1991/12/19 10:23:56 kardel
+# peers on STRATUM 1
+# add mailszrz
+#
+# Revision 1.2 1991/12/19 09:57:29 kardel
+# upgrade NTP V3
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# We want to provide timed service too, thus (startup script magic)
+# TIMED
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 2 # to be upgrade to version 3
+peer ntps2-0 key 0 version 2 # to be upgrade to version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+#
+# US Servers
+#
+peer apple.com version 2
+server churchy.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 2 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 2 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 2 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.41 mask 255.255.255.255 # ntps1-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.1.5 mask 255.255.255.255 nomodify # churchy.udel.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c
new file mode 100644
index 0000000..e1ff902
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c
@@ -0,0 +1,63 @@
+#
+# FAU NTP client configuration file
+#
+# sun4.sun4c,v 3.1 1993/07/06 01:10:41 jbj Exp
+#
+# sun4.sun4c,v
+# Revision 3.1 1993/07/06 01:10:41 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:50 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000144 # sun4c
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer
new file mode 100644
index 0000000..78d3ea8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer
@@ -0,0 +1,174 @@
+#
+# NTP v3 configuration file for Lucifer
+#
+# sun4.sun4c.Lucifer,v 3.1 1993/07/06 01:10:42 jbj Exp
+#
+# sun4.sun4c.Lucifer,v
+# Revision 3.1 1993/07/06 01:10:42 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:35 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:52 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.8 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.7 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.6 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.5 1992/08/14 21:52:02 kardel
+# local clock is now preferred peer
+#
+# Revision 1.4 1992/07/17 17:15:06 kardel
+# adedd new statistics support
+#
+# Revision 1.3 1992/07/12 16:50:16 kardel
+# new peers, restrictions, statistics, no timed
+#
+# Revision 1.2 1992/07/10 07:01:44 kardel
+# authdelay fixed
+#
+# Revision 1.1 1992/07/10 07:00:30 kardel
+# Initial revision
+#
+#
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+# ELV DCF7000 48
+
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 3
+peer ntps1-2 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+peer apple.com version 2
+server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000144 # sun4c
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 3 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 3 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.1.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.34.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.44.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m
new file mode 100644
index 0000000..cf1e283
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m
@@ -0,0 +1,69 @@
+#
+# FAU NTP client configuration file
+#
+# sun4.sun4m,v 3.1 1993/07/06 01:10:43 jbj Exp
+#
+# sun4.sun4m,v
+# Revision 3.1 1993/07/06 01:10:43 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:55 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/10/05 12:48:44 kardel
+# sun4m authdelay
+#
+# Revision 1.1 1992/10/05 12:48:07 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000033 # sun4c
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42
new file mode 100644
index 0000000..acc919c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42
@@ -0,0 +1,152 @@
+#
+# NTP v3 configuration file for faui42
+#
+# sun4.sun4m.faui42,v 3.1 1993/07/06 01:10:44 jbj Exp
+#
+# sun4.sun4m.faui42,v
+# Revision 3.1 1993/07/06 01:10:44 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:36 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:57 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.6 1992/09/15 16:19:10 kardel
+# preferrred peer
+#
+# Revision 1.5 1992/09/15 15:57:36 kardel
+# Stratum 1 again (may the Patches be with us...)
+#
+# Revision 1.4 1992/06/30 08:52:38 kardel
+# sun4m machine don't have a clock (SunOS4.1.2)
+# soory - just Stratum 2
+#
+# Revision 1.3 1992/06/18 13:58:45 kardel
+# precision adjusted (us resolution)
+# clock definition explanation
+#
+# Revision 1.2 1992/06/13 11:42:49 kardel
+# restrictions changed
+#
+# Revision 1.1 1992/06/13 11:27:11 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+#
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-0 key 0 version 2 # to be upgrade to version 3
+peer ntps2-0 key 0 version 2 # to be upgrade to version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+#server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+#peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+#peer apple.com version 2
+#server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000047
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.40.0 key 0 version 3 # inf4-net2.revue (still on 2)
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 nomodify # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m
new file mode 100644
index 0000000..2c75f67
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m
@@ -0,0 +1,165 @@
+#
+# NTP v3 configuration file for Lucifer
+#
+# sun4.sun4m.faui45m,v 3.1 1993/07/06 01:10:45 jbj Exp
+#
+# sun4.sun4m.faui45m,v
+# Revision 3.1 1993/07/06 01:10:45 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:38 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:59 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.7 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.6 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.5 1992/08/14 21:52:02 kardel
+# local clock is now preferred peer
+#
+# Revision 1.4 1992/07/17 17:15:06 kardel
+# adedd new statistics support
+#
+# Revision 1.3 1992/07/12 16:50:16 kardel
+# new peers, restrictions, statistics, no timed
+#
+# Revision 1.2 1992/07/10 07:01:44 kardel
+# authdelay fixed
+#
+# Revision 1.1 1992/07/10 07:00:30 kardel
+# Initial revision
+#
+#
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+# ELV DCF7000 48
+
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 3
+peer ntps1-2 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+peer apple.com version 2
+server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000033 # sun4m
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 3 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 3 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.1.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.34.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.44.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/tickconf b/usr.sbin/xntpd/scripts/support/conf/tickconf
new file mode 100644
index 0000000..b17dbe8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/tickconf
@@ -0,0 +1,19 @@
+DEFAULT -A -p -s -q
+Lucifer 55406cfa -a 1 -p -s -q -t 10001
+faui45 24000f9b -a 1 -p -s -q
+faui10 2440213c -a 1 -p -s -q
+faui1b 54001418 -A -p -s -q -t 10001
+faui4p 5100344d -A -p -s -q -t 9999
+faui02g 1200be20 -A -p -s -q -t 9999
+faui02e 1200bbab -A -p -s -q -t 9999
+faui02f 1200bedb -A -p -s -q -t 9999
+faui03b 1200b92b -A -p -s -q -t 9999
+faui45m 726001ac -A -p -s -q -t 10001
+faui45o 72600272 -A -p -s -q -t 10001
+faui45p 7260028f -A -p -s -q -t 10001
+faui45r 72400cc7 -A -p -s -q -t 10001
+faui45s 726045be -A -p -s -q -t 10001
+faui45v 72604487 -A -p -s -q -t 10001
+faui45x 726044eb -A -p -s -q -t 10001
+faui45y 7260476d -A -p -s -q -t 10001
+faui45z 726045a1 -A -p -s -q -t 10001
diff --git a/usr.sbin/xntpd/scripts/support/etc/cron b/usr.sbin/xntpd/scripts/support/etc/cron
new file mode 100755
index 0000000..07ed189
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/cron
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# cron,v 3.1 1993/07/06 01:10:50 jbj Exp
+#
+# called by cron for statistics gathering
+#
+# cron,v
+# Revision 3.1 1993/07/06 01:10:50 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:18 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+PATH="${PATH}:/local/NTP/bin"
+export PATH
+monl -d /local/NTP/monitor -i '127\.0\.0\.1' faui10 faui45 lucifer rackety.udel.edu
diff --git a/usr.sbin/xntpd/scripts/support/etc/crontab b/usr.sbin/xntpd/scripts/support/etc/crontab
new file mode 100644
index 0000000..2b2d19c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/crontab
@@ -0,0 +1,8 @@
+#
+# NTP statistics periodic cleanup - REFERENCE SERVER ONLY
+#
+#55 23 * * * sh /local/NTP/etc/mvstats
+#
+# gather NTP client statistics - REFERENCE SERVER ONLY
+#
+0 8,18 * * * /local/NTP/etc/cron 2>/dev/null | /usr/ucb/mail -s "NTP statistics" time@informatik.uni-erlangen.de
diff --git a/usr.sbin/xntpd/scripts/support/etc/install b/usr.sbin/xntpd/scripts/support/etc/install
new file mode 100755
index 0000000..169a7e5
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/install
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# install,v 3.1 1993/07/06 01:10:53 jbj Exp
+#
+# install,v
+# Revision 3.1 1993/07/06 01:10:53 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:21 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/06/18 14:50:08 kardel
+# Initial revision
+#
+#
+NTPROOT=/local/NTP # SITE SPECIFIC: where NTP resides
+#
+# where the local NTP state files reside (xntp.drift) ussualle /etc
+# this directory must not be shared as machine dependent data ist stored there
+#
+NTPDIR="/+private/local/NTP"
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+Mkdir() {
+ p=""
+ IFS="/"
+ set -- $@
+ IFS='
+'
+ for pnc do
+ if [ ! -d "$p/$pnc" ]; then
+ ECHO -n "creating directory $p/$pnc"
+ if mkdir "$p/$pnc"; then
+ ECHO ""
+ else
+ ECHO " - FAILED"
+ break;
+ fi
+ fi
+ p="$p/$pnc"
+ done
+}
+
+if [ ! -d "$NTPDIR" ]; then
+ ECHO "installing NTP private data area ($NTPDIR)"
+ if Mkdir "$NTPDIR"; then
+ chmod 755 "$NTPDIR"
+ ECHO "$NTPDIR created."
+ fi
+else
+ ECHO "NTP already installed."
+ if [ -f "$NTPDIR/xntp.drift" ]; then
+ ECHO "currently saved drift value:" `cat "$NTPDIR/xntp.drift"`
+ fi
+fi
+
diff --git a/usr.sbin/xntpd/scripts/support/etc/rc b/usr.sbin/xntpd/scripts/support/etc/rc
new file mode 100755
index 0000000..ef8834a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/rc
@@ -0,0 +1,198 @@
+#!/bin/sh
+# NTP time synchronisation
+#
+# /src/NTP/REPOSITORY/v3/supportscripts/etc/rc,v 1.11 1993/07/09 13:17:00 kardel Exp
+#
+# rc,v
+# Revision 1.11 1993/07/09 13:17:00 kardel
+# local NTPROOT
+#
+# Revision 1.10 1993/07/09 11:37:29 kardel
+# Initial restructured version + GPS support
+#
+# Revision 1.9 1993/06/23 14:10:36 kardel
+# June 21st reconcilation
+#
+# Revision 1.7 1993/06/02 12:04:43 kardel
+# May 28th reconcilation & clenaup
+#
+#
+# non reference clock hosts will try to do an ntpdate on NTPSERVERS
+#
+NTPSERVERS="ntps1-0 ntps1-1 ntps2-0 ntps2-1"
+NTPROOT=/local/NTP
+
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+msg=""
+#
+# default configuration files are named $NTPROOT/conf/<ARCH>.<KARCH>
+#
+CF=$NTPROOT/conf/$ARCH.$KARCH # default configuration file
+#
+# Host specific config file (reference clocks) have the hostname tagged on
+#
+CFH="$CF"."$HOSTNAME" # specific configuration file
+#
+# where to find the tickadj command
+#
+KFIX=$NTPROOT/bin/tickadj # kernel variable fix
+#
+# where to find special tickadj parameters
+#
+TC=$NTPROOT/conf/tickconf # special tickadj parameters
+#
+# where to find the keys file (if not found $KEY.dumb will be used)
+#
+KEY=$NTPROOT/conf/ntp.keys # private key file
+#
+# the daemon
+#
+XD=$NTPROOT/bin/xntpd # NTP daemon
+#
+# HP adjtimed
+#
+ADJTIMED=$NTPROOT/bin/adjtimed # HP special (adjtime() emulation)
+#
+# ntpdate command
+#
+NTPDATE=$NTPROOT/bin/ntpdate
+
+#
+# secondary timed support
+# The word "TIMED" must be in the config file for timed to start
+# Note that this times is a special version which does not ever set or
+# adjust the time. Ask time@informatik.uni-erlangen.de for patches
+#
+TIMED=$NTPROOT/bin/timed # timed (Berkeley) secondary time service
+ # here used in a *HARMLESS* version
+ # to provide time to "inferior" systems
+#
+# ISREFHOST is a command that returns exit status 0 for a reference host
+# Site specific: sample for dcf77 is given
+#
+ISREFHOST="[ -f $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -a -f /dev/refclock-0 ]"
+#
+# SETUP_REFCLOCK
+#
+# what to do in order to set up a local reference clock
+# usually this will load a STREAMS module or initialize other things
+# needed
+#
+SETUP_REFCLOCK() {
+ if modstat | grep -s 'PARSE'; then
+ ECHO "loadable PARSER STREAMS module already loaded."
+ else
+ ECHO "attempting to load PARSER STREAMS module..."
+ MDLFILE="/tmp/mdl.$$"
+ if modload $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -o $MDLFILE 2>&1; then
+ modstat
+ else
+ echo WARNING: load FAILED
+ fi | LOG
+ rm -f $MDLFILE
+ unset MDLFILE
+ fi
+}
+
+kargs() {
+ MATCH=NO
+ HOSTID="`(hostid) 2>/dev/null || echo 000000`"
+ if [ -r "$TC" ]; then
+ exec 0< "$TC"
+ while [ "$MATCH" != "YES" ] && read HOST ID PARAM; do
+ if [ "$HOST" = "DEFAULT" ]; then
+ DEFAULT="$ID $PARAM"
+ else
+ if [ "$ID" = "$HOSTID" -o "$HOST" = "$HOSTNAME" ]; then
+ echo "$PARAM"
+ MATCH=YES
+ fi
+ fi
+ done
+ if [ "$MATCH" != "YES" ]; then
+ if [ -z "$DEFAULT" ]; then
+ echo "-A -p -s -q";
+ else
+ echo "$DEFAULT";
+ fi
+ fi
+ else
+ echo "-A -p -s -q";
+ fi
+}
+
+if [ -x $XD ]; then
+ if [ -x "$ADJTIMED" ]; then
+ $ADJTIMED && ECHO "adjusttimesupport: adjtimed."
+ fi
+ #
+ # WARNING: check ps command first, or you might kill things you don't want to
+ #
+ PID="`(ps -efa 2>/dev/null || ps auxww 2>/dev/null || echo "") | grep xntp | grep -v grep | awk '{ print $2 }'`"
+
+ if [ ! -z "$PID" ]; then
+ ECHO "killing old NTP daemon (PID=$PID)"
+ #
+ # enable this after checking for correctness
+ # kill $PID
+ ECHO "should do a kill $PID, if this is the right PID - check rc script"
+ fi
+ #
+ # try an ntpdate when timeservers are configured
+ #
+ if [ ! -z "$NTPSERVERS" -a -x $NTPDATE ]; then
+ ECHO "NTP initial time setting"
+ $NTPDATE -v $NTPSERVERS | LOG
+ fi
+ #
+ # look for reference clock equipment
+ #
+ if $ISREFHOST; then
+ ECHO "REFERENCE CLOCK SUPPORT (initializing...)"
+ SETUP_REFCLOCK
+ fi
+
+ if [ -r "$CFH" ]; then
+ CF="$CFH"
+ else
+ if [ ! -r "$KEY" ]; then
+ KEY="$KEY.dumb"
+ fi
+ fi
+
+ ECHO "NTP configuration file: $CF"
+ ECHO -n "time daemon startup:"
+
+ if [ -r "$CF" ]; then
+ if [ -x "$KFIX" ]; then
+ KARGS="`kargs`"
+ if [ ! -z "$KARGS" ]; then
+ $KFIX $KARGS && ECHO -n "tickadj $KARGS"
+ fi
+ fi
+ $XD -c "$CF" -k "$KEY" && ECHO -n ' xntpd'
+ if [ -x "$TIMED" ] && grep -s TIMED "$CF"; then
+ $TIMED -M -N && ECHO -n ' timed'
+ fi
+ else
+ msg="configuration file ($CF) not present."
+ fi
+else
+ msg="daemon binary ($XD) not present."
+fi
+ECHO "."
+
+if [ "$msg" ]; then
+ NLECHO "WARNING: NO NTP time sychronisation: $msg"
+fi
diff --git a/usr.sbin/xntpd/scripts/support/etc/setup b/usr.sbin/xntpd/scripts/support/etc/setup
new file mode 100755
index 0000000..d4ea75e
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/setup
@@ -0,0 +1,72 @@
+#
+# setup,v 3.1 1993/07/06 01:10:55 jbj Exp
+#
+# /bin/sh sourced file for environment setup
+# expects NTPROOT variable initialized
+#
+# if not set it will be initialized to /usr/local/NTP
+#
+# setup,v
+# Revision 3.1 1993/07/06 01:10:55 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:25 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/12/10 10:14:46 kardel
+# Initial revision
+#
+#
+NTPROOT=${NTPROOT-/usr/local/NTP}
+
+#
+# we so use our own echos, as we somes want to substitute them with a
+# file logging version durin the /etc/rc.local phase
+#
+set `type ECHO`
+
+PATH="${PATH}:$NTPROOT/bin"
+export PATH
+
+if [ "$2" = "is" ]; then
+ :
+else
+ #
+ # find out the way echos work (Rest of rc thinks BSD echo)
+ #
+ ECHOREP="`echo -n x`"
+ if [ "$ECHOREP" = "-n x" ]; then
+ ECHO () {
+ if [ "$1" = "-n" ]; then
+ shift
+ echo "$@\c"
+ else
+ echo "$@"
+ fi
+ }
+ #ECHO "System V style echo"
+ else
+ ECHO () {
+ echo "$@"
+ }
+ #ECHO "BSD style echo"
+ fi
+
+ NLECHO () {
+ echo "$@"
+ }
+
+ LOG () {
+ while read _line; do
+ ECHO "$_line"
+ done
+ }
+ #
+ # carefully find out some configuration Variables
+ #
+ ARCH="`(arch) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $6; }') 2>/dev/null || echo 'no-arch'`"
+ KARCH="`(arch -k) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $5 }') || echo 'no-arch'`"
+ HOSTNAME="`(hostname) 2>/dev/null || uname -n`"
+fi
+
diff --git a/usr.sbin/xntpd/util/Makefile b/usr.sbin/xntpd/util/Makefile
new file mode 100644
index 0000000..e50ae17
--- /dev/null
+++ b/usr.sbin/xntpd/util/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id: Makefile,v 1.8 1998/03/07 09:46:12 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= tickadj
+MAN8= ${.CURDIR}/../doc/tickadj.8
+CLEANFILES+= .version version.c
+
+SRCS= tickadj.c version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion tickadj
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/util/README b/usr.sbin/xntpd/util/README
new file mode 100644
index 0000000..2aedb00
--- /dev/null
+++ b/usr.sbin/xntpd/util/README
@@ -0,0 +1,67 @@
+README file for directory ./util of the NTP Version 3 distribution
+
+This directory contains the sources for the various utility programs. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install these programs.
+
+The ntptime.c program checks the kernel configuration for the NTP user
+interface syscalls ntp_gettime() and ntp_adjtime(). If present, the
+current timekeeping data are displayed. If not, a dissapointment is
+displayed. Do "make ntptime" in this directory to make the thing,
+but be advised that, unless you have installed the kernel support,
+there will probably be missing vital header files. See the README.kern
+file in the doc directory of this distribution for further details.
+
+The jitter.c program can be used to determine the timing jitter due to
+the operating system in a gettimeofday() call. For most systems the
+dominant contribution to the jitter budget is the period of the hardware
+interrupt, usually in the range 1-10 ms. For those systems with microsecond
+counters, such as recent Sun and certain Ultrix systems, the jitter is
+dominated only by the operating system.
+
+The timetrim.c program can be used with SGI machines to implement a
+scheme to discipline the hardware clock frequency. See the source code
+for further information.
+
+The byteorder.c and longsize.c programs are used during the configuration
+process to determine the byte order (little or big endian) and longword
+size (32 or 64 bits). See the ../scripts/makefile.sh script for further
+details.
+
+The testrs6000.c program is used for testing purposes with the IBM
+RS/6000 AIX machines. Bill Jones <jones@chpc.utexas.edu> reports:
+"I could not get a tickadj of less then 40 us to work on a RS6000.
+If you set it less then 40 us do so at your own risk!"
+
+The tickadj.c program can be used to read and set various kernel
+parameters affecting NTP operations. Comes now the rationale for its use.
+
+Then daemon's clock adjustment algorithms depend (too) strongly
+on the internals of the kernel adjtime() call, and expect it to
+match that which comes with Berkeley-flavour operating systems.
+The daemon actually reads a couple of values from your kernel
+using /dev/kmem (ugh!), the value of `tick' and the value of `tickadj'.
+`tick' is expected to be the number of microseconds which are
+added to the system time on timer interrupts when the clock isn't
+being slewed. `tickadj' is the number of microseconds which are
+added or subtracted from tick when the clock is being slewed.
+
+The program tickadj mimics the daemon's handling of these variables.
+If you run it (as root) and it fails or produces bizarre looking
+values you may have to torque ntp_unixclock.c in the daemon code.
+
+You can also use tickadj -a to set tickadj in the running kernel.
+In addition, tickadj -A will compute the value to set based on the
+kernel's value of tick, while the -t flag allows one to set the
+value of tick and the -s flag will set the value of dosynctodr
+to zero. This is an alternative for people who can't change the
+values in the kernel's disk image.
+
+In addition, the -p flag will set the noprintf variable. This will
+suppress any kernel messages. Kernel message can then only be seen via
+syslog(3). This inhibits clockhopping due to kernel printf's.
+
+The target "ntptime" can only be compiled on systems with kernel PLL
+support. This is currently only possible for SunOS4, Ultrix and DECOSF1.
+You need the propriatary header files for that. So there is no need to
+attempt to compile ntptime unless you have the above configuration.
diff --git a/usr.sbin/xntpd/util/byteorder.c b/usr.sbin/xntpd/util/byteorder.c
new file mode 100644
index 0000000..ff7d239
--- /dev/null
+++ b/usr.sbin/xntpd/util/byteorder.c
@@ -0,0 +1,52 @@
+/*
+ * This works on:
+ * Crays
+ * Conven
+ * sparc's
+ * Dec mip machines
+ * Dec alpha machines
+ * RS6000
+ * SGI's
+ */
+
+#include <stdio.h>
+main()
+{
+ int i;
+ int big;
+ union {
+ unsigned long l;
+ char c[sizeof(long)];
+ } u;
+
+#if defined(LONG8)
+ u.l = (((long)0x08070605) << 32) | (long)0x04030201;
+#else
+ u.l = 0x04030201;
+#endif
+ if (sizeof(long) > 4) {
+ if (u.c[0] == 0x08) big = 1;
+ else big = 0;
+ } else {
+ if (u.c[0] == 0x04) big = 1;
+ else big = 0;
+ }
+ for (i=0; i< sizeof(long); i++) {
+ if (big == 1 && (u.c[i] == (sizeof(long) - i))) {
+ continue;
+ } else if (big == 0 && (u.c[i] == (i+1))) {
+ continue;
+ } else {
+ big = -1;
+ break;
+ }
+ }
+
+ if (big == 1) {
+ printf("XNTP_BIG_ENDIAN\n");
+ } else if (big == 0) {
+ printf("XNTP_LITTLE_ENDIAN\n");
+ }
+ exit(0);
+}
+
diff --git a/usr.sbin/xntpd/util/jitter.c b/usr.sbin/xntpd/util/jitter.c
new file mode 100644
index 0000000..7201e87
--- /dev/null
+++ b/usr.sbin/xntpd/util/jitter.c
@@ -0,0 +1,73 @@
+/*
+ * This program can be used to calibrate the clock reading jitter of a
+ * particular CPU and operating system. It first tickles every element
+ * of an array, in order to force pages into memory, then repeatedly calls
+ * gettimeofday() and, finally, writes out the time values for later
+ * analysis. From this you can determine the jitter and if the clock ever
+ * runs backwards.
+ */
+#include <sys/time.h>
+#include <stdio.h>
+
+#define NBUF 10001
+
+main()
+{
+ struct timeval tp, ts, tr;
+ struct timezone tzp;
+ long temp, j, i, gtod[NBUF];
+
+ gettimeofday(&ts, &tzp);
+ ts.tv_usec = 0;
+
+ /*
+ * Force pages into memory
+ */
+ for (i = 0; i < NBUF; i ++)
+ gtod[i] = 0;
+
+ /*
+ * Construct gtod array
+ */
+ for (i = 0; i < NBUF; i ++) {
+ gettimeofday(&tp, &tzp);
+ tr = tp;
+ tr.tv_sec -= ts.tv_sec;
+ tr.tv_usec -= ts.tv_usec;
+ if (tr.tv_usec < 0) {
+ tr.tv_usec += 1000000;
+ tr.tv_sec--;
+ }
+ gtod[i] = tr.tv_sec * 1000000 + tr.tv_usec;
+ }
+
+ /*
+ * Write out gtod array for later processing with S
+ */
+ for (i = 0; i < NBUF - 1; i++) {
+/*
+ printf("%lu\n", gtod[i]);
+*/
+ gtod[i] = gtod[i + 1] - gtod[i];
+ printf("%lu\n", gtod[i]);
+ }
+
+ /*
+ * Sort the gtod array and display deciles
+ */
+ for (i = 0; i < NBUF - 1; i++) {
+ for (j = 0; j <= i; j++) {
+ if (gtod[j] > gtod[i]) {
+ temp = gtod[j];
+ gtod[j] = gtod[i];
+ gtod[i] = temp;
+ }
+ }
+ }
+ fprintf(stderr, "First rank\n");
+ for (i = 0; i < 10; i++)
+ fprintf(stderr, "%10ld%10ld\n", i, gtod[i]);
+ fprintf(stderr, "Last rank\n");
+ for (i = NBUF - 11; i < NBUF - 1; i++)
+ fprintf(stderr, "%10ld%10ld\n", i, gtod[i]);
+}
diff --git a/usr.sbin/xntpd/util/kern.c b/usr.sbin/xntpd/util/kern.c
new file mode 100644
index 0000000..a2a6672
--- /dev/null
+++ b/usr.sbin/xntpd/util/kern.c
@@ -0,0 +1,210 @@
+/*
+ * This program simulates a first-order, type-II phase-lock loop using
+ * actual code segments from modified kernel distributions for SunOS,
+ * Ultrix and OSF/1 kernels. These segments do not use any licensed code.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include "timex.h"
+
+/*
+ * Phase-lock loop definitions
+ */
+#define HZ 100 /* timer interrupt frequency (Hz) */
+#define MAXPHASE 512000 /* max phase error (us) */
+#define MAXFREQ 200 /* max frequency error (ppm) */
+#define TAU 2 /* time constant (shift 0 - 6) */
+#define POLL 16 /* interval between updates (s) */
+#define MAXSEC 1200 /* max interval between updates (s) */
+
+/*
+ * Function declarations
+ */
+void hardupdate();
+void hardclock();
+void second_overflow();
+
+/*
+ * Kernel variables
+ */
+int tick; /* timer interrupt period (us) */
+int fixtick; /* amortization constant (ppm) */
+struct timeval timex; /* ripoff of kernel time variable */
+
+/*
+ * Phase-lock loop variables
+ */
+int time_status = TIME_BAD; /* clock synchronization status */
+long time_offset = 0; /* time adjustment (us) */
+long time_constant = 0; /* pll time constant */
+long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
+long time_precision = 1000000 / HZ; /* clock precision (us) */
+long time_maxerror = MAXPHASE; /* maximum error (us) */
+long time_esterror = MAXPHASE; /* estimated error (us) */
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */
+long time_adj = 0; /* tick adjust (scaled 1 / HZ) */
+long time_reftime = 0; /* time at last adjustment (s) */
+
+/*
+ * Simulation variables
+ */
+double timey = 0; /* simulation time (us) */
+long timez = 0; /* current error (us) */
+long poll_interval = 0; /* poll counter */
+
+/*
+ * Simulation test program
+ */
+void main()
+{
+ tick = 1000000 / HZ;
+ fixtick = 1000000 % HZ;
+ timex.tv_sec = 0;
+ timex.tv_usec = MAXPHASE;
+ time_freq = 0;
+ time_constant = TAU;
+ printf("tick %d us, fixtick %d us\n", tick, fixtick);
+ printf(" time offset freq _offset _freq _adj\n");
+
+ /*
+ * Grind the loop until ^C
+ */
+ while (1) {
+ timey += (double)(1000000) / HZ;
+ if (timey >= 1000000)
+ timey -= 1000000;
+ hardclock();
+ if (timex.tv_usec >= 1000000) {
+ timex.tv_usec -= 1000000;
+ timex.tv_sec++;
+ second_overflow();
+ poll_interval++;
+ if (!(poll_interval % POLL)) {
+ timez = (long)timey - timex.tv_usec;
+ if (timez > 500000)
+ timez -= 1000000;
+ if (timez < -500000)
+ timez += 1000000;
+ hardupdate(timez);
+ printf("%10li%10li%10.2f %08lx %08lx %08lx\n",
+ timex.tv_sec, timez,
+ (double)time_freq / (1 << SHIFT_KF),
+ time_offset, time_freq, time_adj);
+ }
+ }
+ }
+}
+
+/*
+ * This routine simulates the ntp_adjtime() call
+ *
+ * For default SHIFT_UPDATE = 12, offset is limited to +-512 ms, the
+ * maximum interval between updates is 4096 s and the maximum frequency
+ * offset is +-31.25 ms/s.
+ */
+void hardupdate(offset)
+long offset;
+{
+ long ltemp, mtemp;
+
+ time_offset = offset << SHIFT_UPDATE;
+ mtemp = timex.tv_sec - time_reftime;
+ time_reftime = timex.tv_sec;
+ if (mtemp > MAXSEC)
+ mtemp = 0;
+
+ /* ugly multiply should be replaced */
+ if (offset < 0)
+ time_freq -= (-offset * mtemp) >>
+ (time_constant + time_constant);
+ else
+ time_freq += (offset * mtemp) >>
+ (time_constant + time_constant);
+ ltemp = time_tolerance << SHIFT_KF;
+ if (time_freq > ltemp)
+ time_freq = ltemp;
+ else if (time_freq < -ltemp)
+ time_freq = -ltemp;
+ if (time_status == TIME_BAD)
+ time_status = TIME_OK;
+}
+
+/*
+ * This routine simulates the timer interrupt
+ */
+void hardclock()
+{
+ int ltemp, time_update;
+
+ time_update = tick; /* computed by adjtime() */
+ time_phase += time_adj;
+ if (time_phase < -FINEUSEC) {
+ ltemp = -time_phase >> SHIFT_SCALE;
+ time_phase += ltemp << SHIFT_SCALE;
+ time_update -= ltemp;
+ }
+ else if (time_phase > FINEUSEC) {
+ ltemp = time_phase >> SHIFT_SCALE;
+ time_phase -= ltemp << SHIFT_SCALE;
+ time_update += ltemp;
+ }
+ timex.tv_usec += time_update;
+}
+
+/*
+ * This routine simulates the overflow of the microsecond field
+ *
+ * With SHIFT_SCALE = 23, the maximum frequency adjustment is +-256 us
+ * per tick, or 25.6 ms/s at a clock frequency of 100 Hz. The time
+ * contribution is shifted right a minimum of two bits, while the frequency
+ * contribution is a right shift. Thus, overflow is prevented if the
+ * frequency contribution is limited to half the maximum or 15.625 ms/s.
+ */
+void second_overflow()
+{
+ int ltemp;
+
+ time_maxerror += time_tolerance;
+ if (time_offset < 0) {
+ ltemp = -time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset += ltemp;
+ time_adj = -(ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE));
+ } else {
+ ltemp = time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset -= ltemp;
+ time_adj = ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ }
+ if (time_freq < 0)
+ time_adj -= -time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
+ else
+ time_adj += time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
+ time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ);
+
+ /* ugly divide should be replaced */
+ if (timex.tv_sec % 86400 == 0) {
+ switch (time_status) {
+
+ case TIME_INS:
+ timex.tv_sec--; /* !! */
+ time_status = TIME_OOP;
+ break;
+
+ case TIME_DEL:
+ timex.tv_sec++;
+ time_status = TIME_OK;
+ break;
+
+ case TIME_OOP:
+ time_status = TIME_OK;
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/util/longsize.c b/usr.sbin/xntpd/util/longsize.c
new file mode 100644
index 0000000..bb884ba
--- /dev/null
+++ b/usr.sbin/xntpd/util/longsize.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+main()
+{
+ if (sizeof(long) == 8) {
+ printf("-DLONG8\n");
+ } else if (sizeof(long) == 4) {
+ printf("-DLONG4\n");
+ }
+ exit(0);
+}
diff --git a/usr.sbin/xntpd/util/ntptime.c b/usr.sbin/xntpd/util/ntptime.c
new file mode 100644
index 0000000..38d9a4a
--- /dev/null
+++ b/usr.sbin/xntpd/util/ntptime.c
@@ -0,0 +1,236 @@
+/*
+ * NTP test program
+ *
+ * This program tests to see if the NTP user interface routines
+ * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
+ * If so, each of these routines is called to display current timekeeping
+ * data.
+ *
+ * For more information, see the README.kern file in the doc directory
+ * of the xntp3 distribution.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "sys/timex.h"
+#include "ntp_stdlib.h"
+
+#ifndef SYS_DECOSF1
+#define BADCALL -1 /* this is supposed to be a bad syscall */
+#endif /* SYS_DECOSF1 */
+
+#ifdef KERNEL_PLL
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#else /* KERNEL_PLL */
+#define SYS_ntp_adjtime NTP_SYSCALL_ADJ
+#define SYS_ntp_gettime NTP_SYSCALL_GET
+#endif /* KERNEL_PLL */
+
+/*
+ * Function prototypes
+ */
+extern int sigvec P((int, struct sigvec *, struct sigvec *));
+extern int syscall P((int, void *, ...));
+void pll_trap P((void));
+
+static struct sigvec newsigsys; /* new sigvec status */
+static struct sigvec sigsys; /* current sigvec status */
+static int pll_control; /* (0) daemon, (1) kernel loop */
+
+static char* progname;
+static char optargs[] = "ce:f:hm:o:rs:t:";
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ int status;
+ struct ntptimeval ntv;
+ struct timex ntx, _ntx;
+ int times[20];
+ double ftemp, gtemp;
+ l_fp ts;
+ int c;
+ int errflg = 0;
+ int cost = 0;
+ int rawtime = 0;
+
+ memset((char *)&ntx, 0, sizeof(ntx));
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, optargs)) != EOF) switch (c) {
+ case 'c':
+ cost++;
+ break;
+ case 'e':
+ ntx.modes |= MOD_ESTERROR;
+ ntx.esterror = atoi(ntp_optarg);
+ break;
+ case 'f':
+ ntx.modes |= MOD_FREQUENCY;
+ ntx.freq = (int) (atof(ntp_optarg) *
+ (1 << SHIFT_USEC));
+ if (ntx.freq < (-100 << SHIFT_USEC)
+ || ntx.freq > ( 100 << SHIFT_USEC)) errflg++;
+ break;
+ case 'm':
+ ntx.modes |= MOD_MAXERROR;
+ ntx.maxerror = atoi(ntp_optarg);
+ break;
+ case 'o':
+ ntx.modes |= MOD_OFFSET;
+ ntx.offset = atoi(ntp_optarg);
+ break;
+ case 'r':
+ rawtime++;
+ break;
+ case 's':
+ ntx.modes |= MOD_STATUS;
+ ntx.status = atoi(ntp_optarg);
+ if (ntx.status < 0 || ntx.status > 4) errflg++;
+ break;
+ case 't':
+ ntx.modes |= MOD_TIMECONST;
+ ntx.constant = atoi(ntp_optarg);
+ if (ntx.constant < 0 || ntx.constant > MAXTC)
+ errflg++;
+ break;
+ default:
+ errflg++;
+ }
+ if (errflg || (ntp_optind != argc)) {
+ (void) fprintf(stderr,
+ "usage: %s [-%s]\n\n\
+ -c display the time taken to call ntp_gettime (us)\n\
+ -e esterror estimate of the error (us)\n\
+ -f frequency Frequency error (-100 .. 100) (ppm)\n\
+ -h display this help info\n\
+ -m maxerror max possible error (us)\n\
+ -o offset current offset (ms)\n\
+ -r print the unix and NTP time raw\n\
+ -l leap Set the leap bits\n\
+ -t timeconstant log2 of PLL time constant (0 .. %d)\n",
+ progname, optargs, MAXTC);
+ exit(2);
+ }
+
+
+ /*
+ * Test to make sure the sigvec() works in case of invalid
+ * syscall codes.
+ */
+ newsigsys.sv_handler = pll_trap;
+ newsigsys.sv_mask = 0;
+ newsigsys.sv_flags = 0;
+ if (sigvec(SIGSYS, &newsigsys, &sigsys)) {
+ perror("sigvec() fails to save SIGSYS trap");
+ exit(1);
+ }
+
+#ifdef BADCALL
+ /*
+ * Make sure the trapcatcher works.
+ */
+ pll_control = 1;
+ (void)syscall(BADCALL, &ntv); /* dummy parameter f. ANSI compilers */
+ if (pll_control)
+ printf("sigvec() failed to catch an invalid syscall\n");
+#endif
+
+ if (cost) {
+ for (c = 0; c < sizeof times / sizeof times[0]; c++) {
+ (void)ntp_gettime(&ntv);
+ if (pll_control < 0)
+ break;
+ times[c] = ntv.time.tv_usec;
+ }
+ if (pll_control >= 0) {
+ printf("[ us %06d:", times[0]);
+ for (c = 1; c < sizeof times / sizeof times[0]; c++)
+ printf(" %d", times[c] - times[c - 1]);
+ printf(" ]\n");
+ }
+ }
+ (void)ntp_gettime(&ntv);
+ _ntx.modes = 0; /* Ensure nothing is set */
+ (void)ntp_adjtime(&_ntx);
+ if (pll_control < 0) {
+ printf("NTP user interface routines are not configured in this kernel.\n");
+ goto lexit;
+ }
+
+ /*
+ * Fetch timekeeping data and display.
+ */
+ status = ntp_gettime(&ntv);
+ if (status < 0)
+ perror("ntp_gettime() call fails");
+ else {
+ printf("ntp_gettime() returns code %d\n", status);
+ TVTOTS(&ntv.time, &ts);
+ ts.l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+ ts.l_ui += JAN_1970;
+ ts.l_uf &= TS_MASK;
+ printf(" time %s, (.%06d),\n",
+ prettydate(&ts), ntv.time.tv_usec);
+ printf(" maximum error %ld us, estimated error %ld us.\n",
+ ntv.maxerror, ntv.esterror);
+ if (rawtime) printf(" ntptime=%x.%x unixtime=%x.%06d %s",
+ ts.l_ui, ts.l_uf, ntv.time.tv_sec, ntv.time.tv_usec,
+ ctime(&ntv.time.tv_sec));
+ }
+ status = ntp_adjtime(&ntx);
+ if (status < 0)
+ perror((errno == EPERM) ?
+ "Must be root to set kernel values\nntp_adjtime() call fails" :
+ "ntp_adjtime() call fails");
+ else {
+ printf("ntp_adjtime() returns code %d\n", status);
+ ftemp = ntx.freq;
+ ftemp /= (1 << SHIFT_USEC);
+ printf(" modes %04x, offset %ld us, frequency %.3f ppm, interval %d s,\n",
+ ntx.modes, ntx.offset, ftemp, 1 << ntx.shift);
+ printf(" maximum error %ld us, estimated error %ld us,\n",
+ ntx.maxerror, ntx.esterror);
+ ftemp = ntx.tolerance;
+ ftemp /= (1 << SHIFT_USEC);
+ printf(" status %04x, time constant %ld, precision %ld us, tolerance %.0f ppm,\n",
+ ntx.status, ntx.constant, ntx.precision, ftemp);
+ if (ntx.shift == 0)
+ return;
+ ftemp = ntx.ppsfreq;
+ ftemp /= (1 << SHIFT_USEC);
+ gtemp = ntx.stabil;
+ gtemp /= (1 << SHIFT_USEC);
+ printf(" pps frequency %.3f ppm, stability %.3f ppm, jitter %ld us,\n",
+ ftemp, gtemp, ntx.jitter);
+ printf(" intervals %ld, jitter exceeded %ld, stability exceeded %ld, errors %ld.\n",
+ ntx.calcnt, ntx.jitcnt, ntx.stbcnt, ntx.errcnt);
+ }
+
+ /*
+ * Put things back together the way we found them.
+ */
+lexit: if (sigvec(SIGSYS, &sigsys, (struct sigvec *)NULL)) {
+ perror("sigvec() fails to restore SIGSYS trap");
+ exit(1);
+ }
+ exit(0);
+}
+
+/*
+ * pll1_trap - trap processor for undefined syscalls
+ */
+void
+pll_trap()
+{
+ pll_control--;
+}
diff --git a/usr.sbin/xntpd/util/precision.c b/usr.sbin/xntpd/util/precision.c
new file mode 100644
index 0000000..64fe336
--- /dev/null
+++ b/usr.sbin/xntpd/util/precision.c
@@ -0,0 +1,150 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "ntp_unixtime.h"
+
+#define DEFAULT_SYS_PRECISION -99
+
+int default_get_resolution();
+int default_get_precision();
+
+int
+main() {
+ printf("log2(resolution) = %d, log2(precision) = %d\n",
+ default_get_resolution(),
+ default_get_precision());
+ return 0;
+}
+
+/* Find the resolution of the system clock by watching how the current time
+ * changes as we read it repeatedly.
+ *
+ * struct timeval is only good to 1us, which may cause problems as machines
+ * get faster, but until then the logic goes:
+ *
+ * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
+ * probably use the "unused" low order bits as a counter (to force time to be
+ * a strictly increaing variable), incrementing it each time any process
+ * requests the time [[ or maybe time will stand still ? ]].
+ *
+ * SO: the logic goes:
+ *
+ * IF the difference from the last time is "small" (< MINSTEP)
+ * THEN this machine is "counting" with the low order bits
+ * ELIF this is not the first time round the loop
+ * THEN this machine *WAS* counting, and has now stepped
+ * ELSE this machine has resolution < time to read clock
+ *
+ * SO: if it exits on the first loop, assume "full accuracy" (1us)
+ * otherwise, take the log2(observered difference, rounded UP)
+ *
+ * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
+ * and the first loop, it doesn't stop too early.
+ * Making it even greater allows MINSTEP to be reduced, assuming that the
+ * chance of MINSTEP-1 other processes getting in and calling gettimeofday
+ * between this processes's calls.
+ * Reducing MINSTEP may be necessary as this sets an upper bound for the time
+ * to actually call gettimeofday.
+ */
+
+#define DUSECS 1000000
+#define HUSECS (1024 * 1024)
+#define MINSTEP 5 /* some systems increment uS on each call */
+ /* Don't use "1" as some *other* process may read too*/
+ /*We assume no system actually *ANSWERS* in this time*/
+#define MAXSTEP 20000 /* maximum clock increment (us) */
+#define MINLOOPS 5 /* minimum number of step samples */
+#define MAXLOOPS HUSECS /* Assume precision < .1s ! */
+
+int default_get_resolution()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ int minsteps = MINLOOPS; /* need at least this many steps */
+
+ gettimeofday(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = - --minsteps; i< MAXLOOPS; i++) {
+ gettimeofday(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ if (diff < 0) diff += DUSECS;
+ if (diff > MINSTEP) if (minsteps-- <= 0) break;
+ last = tp.tv_usec;
+ }
+
+ printf("resolution = %ld usec after %d loop%s\n",
+ diff, i, (i==1) ? "" : "s");
+
+ diff = (diff *3)/2;
+ if (i >= MAXLOOPS) {
+ printf(
+ " (Boy this machine is fast ! %d loops without a step)\n",
+ MAXLOOPS);
+ diff = 1; /* No STEP, so FAST machine */
+ }
+ if (i == 0) {
+ printf(
+" (The resolution is less than the time to read the clock -- Assume 1us)\n");
+ diff = 1; /* time to read clock >= resolution */
+ }
+ for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
+ printf(" (Oh dear -- that wasn't expected ! I'll guess !)\n");
+ return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
+}
+
+/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
+
+/*
+ * This routine calculates the differences between successive calls to
+ * gettimeofday(). If a difference is less than zero, the us field
+ * has rolled over to the next second, so we add a second in us. If
+ * the difference is greater than zero and less than MINSTEP, the
+ * clock has been advanced by a small amount to avoid standing still.
+ * If the clock has advanced by a greater amount, then a timer interrupt
+ * has occurred and this amount represents the precision of the clock.
+ * In order to guard against spurious values, which could occur if we
+ * happen to hit a fat interrupt, we do this for MINLOOPS times and
+ * keep the minimum value obtained.
+ */
+int default_get_precision()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ long usec;
+
+ usec = 0;
+ val = MAXSTEP;
+ GETTIMEOFDAY(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = 0; i < MINLOOPS && usec < HUSECS;) {
+ GETTIMEOFDAY(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ last = tp.tv_usec;
+ if (diff < 0)
+ diff += DUSECS;
+ usec += diff;
+ if (diff > MINSTEP) {
+ i++;
+ if (diff < val)
+ val = diff;
+ }
+ }
+ printf("precision = %ld usec after %d loop%s\n",
+ val, i, (i == 1) ? "" : "s");
+ if (usec >= HUSECS) {
+ printf(" (Boy this machine is fast ! usec was %ld)\n",
+ usec);
+ val = MINSTEP; /* val <= MINSTEP; fast machine */
+ }
+ diff = HUSECS;
+ for (i = 0; diff > val; i--)
+ diff >>= 1;
+ return (i);
+}
diff --git a/usr.sbin/xntpd/util/testrs6000.c b/usr.sbin/xntpd/util/testrs6000.c
new file mode 100644
index 0000000..0d4bf4f
--- /dev/null
+++ b/usr.sbin/xntpd/util/testrs6000.c
@@ -0,0 +1,44 @@
+/* Checks for the RS/6000 AIX adjtime() bug, in which if a negative
+ * offset is given, the system gets messed up and never completes the
+ * adjustment. If the problem is fixed, this program will print the
+ * time, sit there for 10 seconds, and exit. If the problem isn't fixed,
+ * the program will print an occasional "result=nnnnnn" (the residual
+ * slew from adjtime()).
+ *
+ * Compile this with bsdcc and run it as root!
+ */
+#include <signal.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+int timeout();
+struct timeval adjustment, result;
+main () {
+ struct itimerval value, oldvalue;
+ int i;
+ time_t curtime;
+ curtime = time(0);
+ printf("Starting: %s", ctime(&curtime));
+ value.it_interval.tv_sec = value.it_value.tv_sec = 1;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ adjustment.tv_sec = 0;
+ adjustment.tv_usec = -2000;
+ signal(SIGALRM, timeout);
+ setitimer(ITIMER_REAL, &value, &oldvalue);
+ for (i=0; i<10; i++) {
+ pause();
+ }
+}
+
+int timeout(sig, code, scp)
+int sig,code;
+struct sigcontext *scp;
+{
+ signal (SIGALRM, timeout);
+ if (adjtime(&adjustment, &result))
+ printf("adjtime call failed\n");
+ if (result.tv_sec != 0 || result.tv_usec != 0) {
+ printf("result.u = %d.%06.6d ", (int) result.tv_sec,
+ (int) result.tv_usec);
+ }
+}
diff --git a/usr.sbin/xntpd/util/tickadj.c b/usr.sbin/xntpd/util/tickadj.c
new file mode 100644
index 0000000..9ba0e65
--- /dev/null
+++ b/usr.sbin/xntpd/util/tickadj.c
@@ -0,0 +1,559 @@
+/*
+ * tickadj - read, and possibly modify, the kernel `tick' and
+ * `tickadj' variables, as well as `dosynctodr'. Note that
+ * this operates on the running kernel only. I'd like to be
+ * able to read and write the binary as well, but haven't
+ * mastered this yet.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+
+#if !defined(SYS_VAX) && !defined(SYS_BSD)
+#include <unistd.h>
+#endif /* SYS_VAX */
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+
+struct timex txc;
+
+int
+main(int argc, char ** argv)
+{
+ if (argc > 2)
+ {
+ fprintf(stderr, "Usage: %s [tick_value]\n", argv[0]);
+ exit(-1);
+ }
+ else if (argc == 2)
+ {
+ if ( (txc.tick = atoi(argv[1])) < 1 )
+ {
+ fprintf(stderr, "Silly value for tick: %s\n", argv[1]);
+ exit(-1);
+ }
+ txc.mode = ADJ_TICK;
+ }
+ else
+ txc.mode = 0;
+
+ if (__adjtimex(&txc) < 0)
+ perror("adjtimex");
+ else
+ printf("tick = %d\n", txc.tick);
+
+ return(0);
+}
+#else /* not Linux... kmem tweaking: */
+
+#include <err.h>
+#include <sys/types.h>
+#ifndef SYS_BSD
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2)
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <a.out.h>
+#include <sys/var.h>
+#else
+#include <nlist.h>
+#endif
+
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#if defined(HAVE_GETBOOTFILE)
+#include <paths.h>
+#endif
+
+#ifdef RS6000
+#undef hz
+#endif /* RS6000 */
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+#if !defined(_SC_CLK_TCK)
+#include <unistd.h>
+#endif
+#endif
+
+#if defined(SYS_PTX) || defined(SYS_IX86OSF1)
+#define L_SET SEEK_SET
+#endif
+
+#define KMEM "/dev/kmem"
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+int debug;
+
+int dokmem = 1;
+int writetickadj = 0;
+int writeopttickadj = 0;
+int unsetdosync = 0;
+int writetick = 0;
+int quiet = 0;
+int setnoprintf = 0;
+
+char *kmem = KMEM;
+char *kernel = NULL;
+char *file = NULL;
+int fd = -1;
+
+static char * getoffsets P((char *, unsigned long *, unsigned long *, unsigned long *, unsigned long *));
+static int openfile P((char *, int));
+static void writevar P((int, unsigned long, int));
+static void readvar P((int, unsigned long, int *));
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: tickadj [-Adkpqs] [-a newadj] [-t newtick]\n");
+ exit(2);
+}
+
+/*
+ * main - parse arguments and handle options
+ */
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ unsigned long tickadj_offset;
+ unsigned long tick_offset;
+ unsigned long dosync_offset;
+ unsigned long noprintf_offset;
+ int tickadj;
+ int tick;
+ int dosynctodr;
+ int noprintf;
+ int hz, hz_hundredths;
+ int recommend_tickadj;
+ long tmp;
+ int openfile();
+ char *getoffsets();
+ void readvar();
+ void writevar();
+
+ while ((c = ntp_getopt(argc, argv, "a:Adkqpst:")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'k':
+ dokmem = 1;
+ break;
+ case 'p':
+ setnoprintf = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'a':
+ writetickadj = atoi(ntp_optarg);
+ if (writetickadj <= 0) {
+ warnx("unlikely value for tickadj: %s",
+ ntp_optarg);
+ errflg++;
+ }
+ break;
+ case 'A':
+ writeopttickadj = 1;
+ break;
+ case 's':
+ unsetdosync = 1;
+ break;
+ case 't':
+ writetick = atoi(ntp_optarg);
+ if (writetick <= 0) {
+ warnx("unlikely value for tick: %s",
+ ntp_optarg);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind != argc)
+ usage();
+ kernel = getoffsets(kernel, &tick_offset,
+ &tickadj_offset, &dosync_offset, &noprintf_offset);
+
+ if (debug) {
+ (void) printf("tick offset = %lu\n", tick_offset);
+ (void) printf("tickadj offset = %lu\n", tickadj_offset);
+ (void) printf("dosynctodr offset = %lu\n", dosync_offset);
+ (void) printf("noprintf offset = %lu\n", noprintf_offset);
+ }
+
+ if (setnoprintf && (noprintf_offset == 0)) {
+ warnx("no noprintf kernel variable");
+ errflg++;
+ }
+
+ if (unsetdosync && (dosync_offset == 0)) {
+ warnx("no dosynctodr kernel variable");
+ errflg++;
+ }
+
+ if (writeopttickadj && (tickadj_offset == 0)) {
+ warnx("no tickadj kernel variable");
+ errflg++;
+ }
+
+ if (writetick && (tick_offset == 0)) {
+ warnx("no tick kernel variable");
+ errflg++;
+ }
+
+
+ if (tickadj_offset != 0)
+ readvar(fd, tickadj_offset, &tickadj);
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+ tick = 1000000/sysconf(_SC_CLK_TCK);
+#else
+ readvar(fd, tick_offset, &tick);
+#endif
+
+ if (dosync_offset != 0)
+ readvar(fd, dosync_offset, &dosynctodr);
+ if (noprintf_offset != 0)
+ readvar(fd, noprintf_offset, &noprintf);
+ (void) close(fd);
+
+ if (unsetdosync && dosync_offset == 0)
+ errx(1, "can't find dosynctodr in namelist");
+
+ if (!quiet) {
+ (void) printf("tick = %d us",tick);
+ if (tickadj_offset != 0)
+ (void) printf(", tickadj = %d us", tickadj);
+ if (dosync_offset != 0)
+ (void) printf(", dosynctodr is %s", dosynctodr ? "on" : "off");
+ (void) printf("\n");
+ if (noprintf_offset != 0)
+ (void) printf("kernel level printf's: %s\n", noprintf ? "off" : "on");
+ }
+
+ if (tick <= 0)
+ errx(1, "the value of tick is silly!");
+
+ hz = (int)(1000000L / (long)tick);
+ hz_hundredths = (int)((100000000L / (long)tick) - ((long)hz * 100L));
+ if (!quiet)
+ (void) printf("calculated hz = %d.%02d Hz\n", hz,
+ hz_hundredths);
+ tmp = (long) tick * 500L;
+ recommend_tickadj = (int)(tmp / 1000000L);
+ if (tmp % 1000000L > 0)
+ recommend_tickadj++;
+
+#if defined(RS6000)
+ if (recommend_tickadj < 40) recommend_tickadj = 40;
+#endif
+
+ if ((!quiet) && (tickadj_offset != 0))
+ (void) printf("recommended value of tickadj = %d us\n",
+ recommend_tickadj);
+
+ if (writetickadj == 0 && !writeopttickadj &&
+ !unsetdosync && writetick == 0 && !setnoprintf)
+ exit(errflg ? 1 : 0);
+
+ if (writetickadj == 0 && writeopttickadj)
+ writetickadj = recommend_tickadj;
+
+ fd = openfile(file, O_WRONLY);
+
+ if (setnoprintf && (dosync_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "setting noprintf: ");
+ (void) fflush(stderr);
+ }
+ writevar(fd, noprintf_offset, 1);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if ((writetick > 0) && (tick_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "writing tick, value %d: ",
+ writetick);
+ (void) fflush(stderr);
+ }
+ writevar(fd, tick_offset, writetick);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if ((writetickadj > 0) && (tickadj_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "writing tickadj, value %d: ",
+ writetickadj);
+ (void) fflush(stderr);
+ }
+ writevar(fd, tickadj_offset, writetickadj);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if (unsetdosync && (dosync_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "zeroing dosynctodr: ");
+ (void) fflush(stderr);
+ }
+ writevar(fd, dosync_offset, 0);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+ (void) close(fd);
+ exit(errflg ? 1 : 0);
+}
+
+/*
+ * getoffsets - read the magic offsets from the specified file
+ */
+static char *
+getoffsets(filex, tick_off, tickadj_off, dosync_off, noprintf_off)
+ char *filex;
+ unsigned long *tick_off;
+ unsigned long *tickadj_off;
+ unsigned long *dosync_off;
+ unsigned long *noprintf_off;
+{
+ char **kname, *knm;
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DEF
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {"tick"},
+ {""},
+ };
+#endif
+
+#ifdef NeXT
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+#define X_DEF
+ static struct nlist nl[] =
+ { {{"_tickadj"}},
+ {{"_tick"}},
+ {{"_dosynctodr"}},
+ {{"_noprintf"}},
+ {{""}},
+ };
+#endif
+
+#if defined(SYS_SVR4) || defined(SYS_PTX)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+#define X_DEF
+ static struct nlist nl[] =
+ { {{"tickadj"}},
+ {{"tick"}},
+ {{"doresettodr"}},
+ {{"noprintf"}},
+ {{""}},
+ };
+#endif /* SYS_SVR4 */
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+#ifndef SOLARIS_HRTIME
+#define X_TICKADJ 0
+#endif
+#define X_DOSYNC 1
+#define X_NOPRINTF 2
+#define X_DEF
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {"dosynctodr"},
+ {"noprintf"},
+ {""},
+ };
+
+#if defined(RS6000)
+ int i;
+#endif
+#endif
+
+#if defined(SYS_HPUX)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DEF
+ static struct nlist nl[] =
+#ifdef hp9000s300
+ { {"_tickadj"},
+ {"_old_tick"},
+#else
+ { {"tickadj"},
+ {"old_tick"},
+#endif
+ {""},
+ };
+#endif
+
+#if !defined(X_DEF)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+ static struct nlist nl[] =
+ { {"_tickadj"},
+ {"_tick"},
+ {"_dosynctodr"},
+ {"_noprintf"},
+ {""},
+ };
+#endif
+#ifndef HAVE_GETBOOTFILE
+ static char *kernels[] = {
+ "/kernel",
+ "/vmunix",
+ "/unix",
+ "/mach",
+ "/kernel/unix",
+ "/386bsd",
+ "/netbsd",
+ NULL
+ };
+#endif
+ struct stat stbuf;
+
+#ifdef HAVE_GETBOOTFILE
+ /* XXX bogus cast to avoid `const' poisoning. */
+ kname = &knm;
+ *kname = (char *)getbootfile();
+ if (stat(*kname, &stbuf) == -1 || nlist(*kname, nl) == -1)
+ *kname = NULL;
+#else
+ for (kname = kernels; *kname != NULL; kname++) {
+ if (stat(*kname, &stbuf) == -1)
+ continue;
+ if (nlist(*kname, nl) >= 0)
+ break;
+ }
+#endif
+ if (*kname == NULL)
+ errx(1, "nlist fails: can't find/read kernel boot file name");
+
+ if (dokmem)
+ file = kmem;
+ else
+ file = kernel;
+
+ fd = openfile(file, O_RDONLY);
+#if defined(RS6000)
+ /*
+ * Go one more round of indirection.
+ */
+ for (i=0; i<(sizeof(nl)/sizeof(struct nlist)); i++) {
+ if (nl[i].n_value) {
+ readvar(fd, nl[i].n_value, &nl[i].n_value);
+ }
+ }
+#endif
+ *tickadj_off = 0;
+ *tick_off = 0;
+ *dosync_off = 0;
+ *noprintf_off = 0;
+
+#if defined(X_TICKADJ)
+ *tickadj_off = nl[X_TICKADJ].n_value;
+#endif
+
+#if defined(X_TICK)
+ *tick_off = nl[X_TICK].n_value;
+#endif
+
+#if defined(X_DOSYNC)
+ *dosync_off = nl[X_DOSYNC].n_value;
+#endif
+
+#if defined(X_NOPRINTF)
+ *noprintf_off = nl[X_NOPRINTF].n_value;
+#endif
+ return *kname;
+}
+
+#undef X_TICKADJ
+#undef X_TICK
+#undef X_DOSYNC
+#undef X_NOPRINTF
+
+
+/*
+ * openfile - open the file, check for errors
+ */
+static int
+openfile(name, mode)
+ char *name;
+ int mode;
+{
+ int fd;
+
+ fd = open(name, mode);
+ if (fd < 0)
+ err(1, "open %s", name);
+ return fd;
+}
+
+
+/*
+ * writevar - write a variable into the file
+ */
+static void
+writevar(fd, off, var)
+ int fd;
+ unsigned long off;
+ int var;
+{
+
+ if (lseek(fd, off, L_SET) == -1)
+ err(1, "lseek fails");
+ if (write(fd, (char *)&var, sizeof(int)) != sizeof(int))
+ err(1, "write fails");
+}
+
+
+/*
+ * readvar - read a variable from the file
+ */
+static void
+readvar(fd, off, var)
+ int fd;
+ unsigned long off;
+ int *var;
+{
+ int i;
+
+ if (lseek(fd, off, L_SET) == -1)
+ err(1, "lseek fails");
+ i = read(fd, (char *)var, sizeof(int));
+ if (i < 0)
+ err(1, "read fails");
+ if (i != sizeof(int))
+ errx(1, "read expected %d, got %d", (int)sizeof(int), i);
+}
+#endif /* not Linux */
diff --git a/usr.sbin/xntpd/util/timetrim.c b/usr.sbin/xntpd/util/timetrim.c
new file mode 100644
index 0000000..7b9413f
--- /dev/null
+++ b/usr.sbin/xntpd/util/timetrim.c
@@ -0,0 +1,85 @@
+/*
+ * timetrim.c
+ *
+ * "timetrim" allows setting and adjustment of the system clock frequency
+ * trim parameter on Silicon Graphics machines. The trim value native
+ * units are nanoseconds per second (10**-9), so a trim value of 1 makes
+ * the system clock step ahead 1 nanosecond more per second than a value
+ * of zero. Xntpd currently uses units of 2**-20 secs for its frequency
+ * offset (drift) values; to convert to a timetrim value, multiply by
+ * 1E9 / 2**20 (about 954).
+ *
+ * "timetrim" with no arguments just prints out the current kernel value.
+ * With a numeric argument, the kernel value is set to the supplied value.
+ * The "-i" flag causes the supplied value to be added to the kernel value.
+ * The "-n" option causes all input and output to be in xntpd units rather
+ * than timetrim native units.
+ *
+ * Note that there is a limit of +-3000000 (0.3%) on the timetrim value
+ * which is (silently?) enforced by the kernel.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/syssgi.h>
+
+#define abs(X) (((X) < 0) ? -(X) : (X))
+#define USAGE "usage: timetrim [-n] [[-i] value]\n"
+#define SGITONTP(X) ((double)(X) * 1048576.0/1.0e9)
+#define NTPTOSGI(X) ((LONG)((X) * 1.0e9/1048576.0))
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char *rem;
+ int c, incremental = 0, ntpunits = 0;
+ LONG timetrim;
+ double value, strtod();
+
+ while (--argc && **++argv == '-' && isalpha(argv[0][1])) {
+ switch (argv[0][1]) {
+ case 'i':
+ incremental++;
+ break;
+ case 'n':
+ ntpunits++;
+ break;
+ default:
+ fprintf(stderr, USAGE);
+ exit(1);
+ }
+ }
+
+ if (syssgi(SGI_GETTIMETRIM, &timetrim) < 0) {
+ perror("syssgi");
+ exit(2);
+ }
+
+ if (argc == 0) {
+ if (ntpunits)
+ fprintf(stdout, "%0.5lf\n", SGITONTP(timetrim));
+ else
+ fprintf(stdout, "%ld\n", timetrim);
+ } else if (argc != 1) {
+ fprintf(stderr, USAGE);
+ exit(1);
+ } else {
+ value = strtod(argv[0], &rem);
+ if (*rem != '\0') {
+ fprintf(stderr, USAGE);
+ exit(1);
+ }
+ if (ntpunits)
+ value = NTPTOSGI(value);
+ if (incremental)
+ timetrim += value;
+ else
+ timetrim = value;
+ if (syssgi(SGI_SETTIMETRIM, timetrim) < 0) {
+ perror("syssgi");
+ exit(2);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/Makefile b/usr.sbin/xntpd/xntpd/Makefile
new file mode 100644
index 0000000..909e8dd
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/Makefile
@@ -0,0 +1,36 @@
+#
+# $Id: Makefile,v 1.12 1998/03/07 09:46:15 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../parse)
+LIBPARSE= ${.OBJDIR}/../parse/libparse.a
+.else
+LIBPARSE= ${.CURDIR}/../parse/libparse.a
+.endif
+
+DPADD= ${LIBNTP} ${LIBPARSE} ${LIBKVM}
+LDADD= ${LIBNTP} ${LIBPARSE} -lkvm
+
+PROG= xntpd
+MAN8= ${.CURDIR}/../doc/xntpd.8
+CLEANFILES+= .version version.c
+
+SRCS= ntp_config.c ntp_control.c ntp_io.c ntp_leap.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_unixclock.c ntp_util.c ntpd.c ntp_intres.c \
+ ntp_filegen.c version.c
+
+# refclocks
+SRCS+= refclock_acts.c refclock_as2201.c refclock_atom.c refclock_chu.c \
+ refclock_conf.c refclock_datum.c refclock_goes.c refclock_gpstm.c \
+ refclock_heath.c refclock_leitch.c refclock_local.c refclock_moto.c \
+ refclock_msfees.c refclock_mx4200.c refclock_nmea.c refclock_omega.c \
+ refclock_parse.c refclock_pst.c refclock_trak.c refclock_wwvb.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion xntpd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/xntpd/README b/usr.sbin/xntpd/xntpd/README
new file mode 100644
index 0000000..4551276
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/README
@@ -0,0 +1,6 @@
+README file for directory ./xntpd of the NTP Version 3 distribution
+
+This directory contains the sources for the xntpd daemon for Unix. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/xntpd/minpoll b/usr.sbin/xntpd/xntpd/minpoll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/minpoll
diff --git a/usr.sbin/xntpd/xntpd/ntp_config.c b/usr.sbin/xntpd/xntpd/ntp_config.c
new file mode 100644
index 0000000..bc88b5a
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_config.c
@@ -0,0 +1,1717 @@
+/*
+ k ntp_config.c - read and apply configuration information
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines are used to read the configuration file at
+ * startup time. An entry in the file must fit on a single line.
+ * Entries are processed as multiple tokens separated by white space
+ * Lines are considered terminated when a '#' is encountered. Blank
+ * lines are ignored.
+ */
+
+/*
+ * Configuration file name
+ */
+#ifndef CONFIG_FILE
+#if defined(__bsdi__)
+#define CONFIG_FILE "/usr/local/etc/xntp.conf"
+#else
+#define CONFIG_FILE "/etc/ntp.conf"
+#endif
+#endif /* CONFIG_FILE */
+
+/*
+ * We understand the following configuration entries and defaults.
+ *
+ * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * precision -7
+ * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ]
+ * broadcastclient
+ * multicastclient [224.0.1.1]
+ * broadcastdelay 0.0102
+ * authenticate yes|no XXX depredated
+ * monitor yes|no XXX depredated
+ * authdelay 0.00842
+ * restrict [ addr ] [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery
+ * driftfile file_name
+ * keys file_name
+ * statsdir /var/NTP/
+ * filegen peerstats [ file peerstats ] [ type day ] [ link ]
+ * clientlimit [ n ]
+ * clientperiod [ 3600 ]
+ * trustedkey [ key ]
+ * requestkey [ key]
+ * controlkey [ key ]
+ * trap [ addr ]
+ * fudge [ addr ] [ stratum ] [ refid ] ...
+ * pidfile [ ]
+ * setvar [ ]
+ * enable auth|bclient|pll|pps|monitor|stats
+ * disable auth|bclient|pll|pps|monitor|stats
+ * phone ...
+ */
+
+/*
+ * Types of entries we understand.
+ */
+#define CONFIG_UNKNOWN 0
+
+#define CONFIG_PEER 1
+#define CONFIG_SERVER 2
+#define CONFIG_PRECISION 3
+#define CONFIG_DRIFTFILE 4
+#define CONFIG_BROADCAST 5
+#define CONFIG_BROADCASTCLIENT 6
+#define CONFIG_AUTHENTICATE 7
+#define CONFIG_KEYS 8
+#define CONFIG_MONITOR 9
+#define CONFIG_AUTHDELAY 10
+#define CONFIG_RESTRICT 11
+#define CONFIG_BDELAY 12
+#define CONFIG_TRUSTEDKEY 13
+#define CONFIG_REQUESTKEY 14
+#define CONFIG_CONTROLKEY 15
+#define CONFIG_TRAP 16
+#define CONFIG_FUDGE 17
+#define CONFIG_RESOLVER 18
+#define CONFIG_STATSDIR 19
+#define CONFIG_FILEGEN 20
+#define CONFIG_STATISTICS 21
+#define CONFIG_PIDFILE 22
+#define CONFIG_SETVAR 23
+#define CONFIG_CLIENTLIMIT 24
+#define CONFIG_CLIENTPERIOD 25
+#define CONFIG_MULTICASTCLIENT 26
+#define CONFIG_ENABLE 27
+#define CONFIG_DISABLE 28
+#define CONFIG_PHONE 29
+
+#define CONF_MOD_VERSION 1
+#define CONF_MOD_KEY 2
+#define CONF_MOD_MINPOLL 3
+#define CONF_MOD_MAXPOLL 4
+#define CONF_MOD_PREFER 5
+#define CONF_MOD_TTL 6
+#define CONF_MOD_MODE 7
+
+#define CONF_RES_MASK 1
+#define CONF_RES_IGNORE 2
+#define CONF_RES_NOSERVE 3
+#define CONF_RES_NOTRUST 4
+#define CONF_RES_NOQUERY 5
+#define CONF_RES_NOMODIFY 6
+#define CONF_RES_NOPEER 7
+#define CONF_RES_NOTRAP 8
+#define CONF_RES_LPTRAP 9
+#define CONF_RES_NTPPORT 10
+#define CONF_RES_LIMITED 11
+
+#define CONF_TRAP_PORT 1
+#define CONF_TRAP_INTERFACE 2
+
+#define CONF_FDG_TIME1 1
+#define CONF_FDG_TIME2 2
+#define CONF_FDG_STRATUM 3
+#define CONF_FDG_REFID 4
+#define CONF_FDG_FLAG1 5
+#define CONF_FDG_FLAG2 6
+#define CONF_FDG_FLAG3 7
+#define CONF_FDG_FLAG4 8
+
+#define CONF_FGEN_FILE 1
+#define CONF_FGEN_TYPE 2
+#define CONF_FGEN_FLAG_LINK 3
+#define CONF_FGEN_FLAG_NOLINK 4
+#define CONF_FGEN_FLAG_ENABLE 5
+#define CONF_FGEN_FLAG_DISABLE 6
+
+/*
+ * Translation table - keywords to function index
+ */
+struct keyword {
+ char *text;
+ int keytype;
+};
+
+/*
+ * Command keywords
+ */
+static struct keyword keywords[] = {
+ { "peer", CONFIG_PEER },
+ { "server", CONFIG_SERVER },
+ { "precision", CONFIG_PRECISION },
+ { "driftfile", CONFIG_DRIFTFILE },
+ { "broadcast", CONFIG_BROADCAST },
+ { "broadcastclient", CONFIG_BROADCASTCLIENT },
+ { "multicastclient", CONFIG_MULTICASTCLIENT },
+ { "authenticate", CONFIG_AUTHENTICATE },
+ { "keys", CONFIG_KEYS },
+ { "monitor", CONFIG_MONITOR },
+ { "authdelay", CONFIG_AUTHDELAY },
+ { "restrict", CONFIG_RESTRICT },
+ { "broadcastdelay", CONFIG_BDELAY },
+ { "trustedkey", CONFIG_TRUSTEDKEY },
+ { "requestkey", CONFIG_REQUESTKEY },
+ { "controlkey", CONFIG_CONTROLKEY },
+ { "trap", CONFIG_TRAP },
+ { "fudge", CONFIG_FUDGE },
+ { "statsdir", CONFIG_STATSDIR },
+ { "filegen", CONFIG_FILEGEN },
+ { "statistics", CONFIG_STATISTICS },
+ { "pidfile", CONFIG_PIDFILE },
+ { "setvar", CONFIG_SETVAR },
+ { "clientlimit", CONFIG_CLIENTLIMIT },
+ { "clientperiod", CONFIG_CLIENTPERIOD },
+ { "enable", CONFIG_ENABLE },
+ { "disable", CONFIG_DISABLE },
+ { "phone", CONFIG_PHONE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "peer", "server", "broadcast" modifier keywords
+ */
+static struct keyword mod_keywords[] = {
+ { "version", CONF_MOD_VERSION },
+ { "key", CONF_MOD_KEY },
+ { "minpoll", CONF_MOD_MINPOLL },
+ { "maxpoll", CONF_MOD_MAXPOLL },
+ { "prefer", CONF_MOD_PREFER },
+ { "mode", CONF_MOD_MODE }, /* reference clocks */
+ { "ttl", CONF_MOD_TTL }, /* NTP peers */
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "restrict" modifier keywords
+ */
+static struct keyword res_keywords[] = {
+ { "mask", CONF_RES_MASK },
+ { "ignore", CONF_RES_IGNORE },
+ { "noserve", CONF_RES_NOSERVE },
+ { "notrust", CONF_RES_NOTRUST },
+ { "noquery", CONF_RES_NOQUERY },
+ { "nomodify", CONF_RES_NOMODIFY },
+ { "nopeer", CONF_RES_NOPEER },
+ { "notrap", CONF_RES_NOTRAP },
+ { "lowpriotrap", CONF_RES_LPTRAP },
+ { "ntpport", CONF_RES_NTPPORT },
+ { "limited", CONF_RES_LIMITED },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "trap" modifier keywords
+ */
+static struct keyword trap_keywords[] = {
+ { "port", CONF_TRAP_PORT },
+ { "interface", CONF_TRAP_INTERFACE },
+ { "", CONFIG_UNKNOWN }
+};
+
+
+/*
+ * "fudge" modifier keywords
+ */
+static struct keyword fudge_keywords[] = {
+ { "time1", CONF_FDG_TIME1 },
+ { "time2", CONF_FDG_TIME2 },
+ { "stratum", CONF_FDG_STRATUM },
+ { "refid", CONF_FDG_REFID },
+ { "flag1", CONF_FDG_FLAG1 },
+ { "flag2", CONF_FDG_FLAG2 },
+ { "flag3", CONF_FDG_FLAG3 },
+ { "flag4", CONF_FDG_FLAG4 },
+ { "", CONFIG_UNKNOWN }
+};
+
+
+/*
+ * "filegen" modifier keywords
+ */
+static struct keyword filegen_keywords[] = {
+ { "file", CONF_FGEN_FILE },
+ { "type", CONF_FGEN_TYPE },
+ { "link", CONF_FGEN_FLAG_LINK },
+ { "nolink", CONF_FGEN_FLAG_NOLINK },
+ { "enable", CONF_FGEN_FLAG_ENABLE },
+ { "disable", CONF_FGEN_FLAG_DISABLE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "type" modifier keywords
+ */
+static struct keyword fgen_types[] = {
+ { "none", FILEGEN_NONE },
+ { "pid", FILEGEN_PID },
+ { "day", FILEGEN_DAY },
+ { "week", FILEGEN_WEEK },
+ { "month", FILEGEN_MONTH },
+ { "year", FILEGEN_YEAR },
+ { "age", FILEGEN_AGE },
+ { "", CONFIG_UNKNOWN}
+};
+
+/*
+ * "enable", "disable" modifier keywords
+ */
+static struct keyword flags_keywords[] = {
+ { "auth", PROTO_AUTHENTICATE },
+ { "bclient", PROTO_BROADCLIENT },
+ { "pll", PROTO_PLL },
+ { "pps", PROTO_PPS },
+ { "monitor", PROTO_MONITOR },
+ { "stats", PROTO_FILEGEN },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * Limits on things
+ */
+#define MAXTOKENS 20 /* 20 tokens on line */
+#define MAXLINE 1024 /* maximum length of line */
+#define MAXPHONE 5 /* maximum number of phone strings */
+#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */
+
+
+/*
+ * Miscellaneous macros
+ */
+#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
+#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * File descriptor used by the resolver save routines, and temporary file
+ * name.
+ */
+static FILE *res_fp;
+static char res_file[20]; /* enough for /tmp/xntpXXXXXX\0 */
+#define RES_TEMPFILE "/tmp/xntpXXXXXX"
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+#ifdef DEBUG
+extern int debug;
+#endif
+extern char *FindConfig();
+ char *progname;
+ char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */
+static char *xntp_options = "abc:dD:e:f:k:l:mp:r:s:t:v:V:";
+
+/*
+ * Function prototypes
+ */
+static int gettokens P((FILE *, char *, char **, int *));
+static int matchkey P((char *, struct keyword *));
+static int getnetnum P((char *, struct sockaddr_in *, int));
+static void save_resolve P((char *, int, int, int, int, int, int, u_long));
+static void do_resolve_internal P((void));
+static void abort_resolve P((void));
+static RETSIGTYPE catchchild P((int));
+
+/*
+ * getstartup - search through the options looking for a debugging flag
+ */
+void
+getstartup(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef DEBUG
+ int errflg;
+ int c;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ debug = 0; /* no debugging by default */
+
+ /*
+ * This is a big hack. We don't really want to read command line
+ * configuration until everything else is initialized, since
+ * the ability to configure the system may depend on storage
+ * and the like having been initialized. Except that we also
+ * don't want to initialize anything until after detaching from
+ * the terminal, but we won't know to do that until we've
+ * parsed the command line. Do that now, crudely, and do it
+ * again later. Our ntp_getopt() is explicitly reusable, by the
+ * way. Your own mileage may vary.
+ */
+ errflg = 0;
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, xntp_options)) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'D':
+ debug = strtol(ntp_optarg, 0, 0);
+ printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug);
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr, "usage: %s [ -abd ] [ -c config_file ] [ -e encryption delay ]\n", progname);
+ (void) fprintf(stderr, "\t\t[ -f frequency file ] [ -k key file ] [ -l log file ]\n");
+ (void) fprintf(stderr, "\t\t[ -p pid file ] [ -r broadcast delay ] [ -s status directory ]\n");
+ (void) fprintf(stderr, "\t\t[ -t trusted key ] [ -v sys variable ] [ -V default sys variable ]\n");
+ exit(2);
+ }
+ ntp_optind = 0; /* reset ntp_optind to restart ntp_getopt */
+
+ if (debug) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+#endif /* DEBUG */
+}
+
+/*
+ * getconfig - get command line options and read the configuration file
+ */
+void
+getconfig(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ int errflg;
+ int peerversion;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ u_long peerkey;
+ int peerflags;
+ int hmode;
+ struct sockaddr_in peeraddr;
+ struct sockaddr_in maskaddr;
+ FILE *fp;
+ char line[MAXLINE];
+ char *(tokens[MAXTOKENS]);
+ int ntokens;
+ int tok;
+ struct interface *localaddr;
+ char *config_file;
+ struct refclockstat clock;
+ int have_keyfile;
+ char keyfile[MAXFILENAME];
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ extern char *Version;
+ extern u_long info_auth_keyid;
+ FILEGEN *filegen;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+#ifdef DEBUG
+ debug = 0;
+#endif /* DEBUG */
+ config_file = CONFIG_FILE;
+ progname = argv[0];
+ res_fp = NULL;
+ have_keyfile = 0;
+ memset((char *)sys_phone, 0, sizeof(sys_phone));
+
+ /*
+ * install a non default variable with this daemon version
+ */
+ (void) sprintf(line, "daemon_version=\"%s\"", Version);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, xntp_options)) != EOF) {
+ switch (c) {
+ case 'a':
+ proto_config(PROTO_AUTHENTICATE, 1);
+ break;
+
+ case 'b':
+ proto_config(PROTO_BROADCLIENT, 1);
+ break;
+
+ case 'c':
+ config_file = ntp_optarg;
+ break;
+
+ case 'd':
+#ifdef DEBUG
+ debug++;
+#else
+ errflg++;
+#endif /* DEBUG */
+ break;
+ case 'D':
+#ifdef DEBUG
+ debug = strtol(ntp_optarg, 0, 0);
+ printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug);
+#endif /* DEBUG */
+ break;
+
+ case 'e':
+ do {
+ l_fp tmp;
+
+ if (!atolfp(ntp_optarg, &tmp)) {
+ syslog(LOG_ERR,
+ "command line encryption delay value %s undecodable",
+ ntp_optarg);
+ errflg++;
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "command line encryption delay value %s is unlikely",
+ ntp_optarg);
+ errflg++;
+ } else {
+ proto_config(PROTO_AUTHDELAY, tmp.l_f);
+ }
+ } while (0);
+ break;
+
+ case 'f':
+ stats_config(STATS_FREQ_FILE, ntp_optarg);
+ break;
+
+ case 'k':
+ getauthkeys(ntp_optarg);
+ if ((int)strlen(ntp_optarg) >= MAXFILENAME) {
+ syslog(LOG_ERR,
+ "key file name too long (>%d, sigh), no name resolution possible",
+ MAXFILENAME);
+ } else {
+ have_keyfile = 1;
+ (void)strcpy(keyfile, ntp_optarg);
+ }
+ break;
+
+ case 'm':
+ proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP));
+ break;
+
+ case 'p':
+ stats_config(STATS_PID_FILE, ntp_optarg);
+ break;
+
+ case 'r':
+ do {
+ l_fp tmp;
+
+ if (!atolfp(ntp_optarg, &tmp)) {
+ syslog(LOG_ERR,
+ "command line broadcast delay value %s undecodable",
+ ntp_optarg);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "command line broadcast delay value %s is unlikely",
+ ntp_optarg);
+ } else {
+ proto_config(PROTO_BROADDELAY, tmp.l_f);
+ }
+ } while (0);
+ break;
+
+ case 's':
+ stats_config(STATS_STATSDIR, ntp_optarg);
+ break;
+
+ case 't':
+ do {
+ u_long tkey;
+
+ tkey = atol(ntp_optarg);
+ if (tkey <= 0 || tkey > NTP_MAXKEY) {
+ syslog(LOG_ERR,
+ "command line trusted key %s is unlikely",
+ ntp_optarg);
+ } else {
+ authtrust(tkey, 1);
+ }
+ } while (0);
+ break;
+
+ case 'v':
+ case 'V':
+ set_sys_var(ntp_optarg, strlen(ntp_optarg)+1,
+ RW | ((c == 'V') ? DEF : 0));
+ break;
+
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr,
+ "usage: %s [ -bd ] [ -c config_file ]\n", progname);
+ exit(2);
+ }
+
+ if ((fp = fopen(FindConfig(config_file), "r")) == NULL) {
+ /*
+ * Broadcast clients can sometimes run without
+ * a configuration file.
+ */
+ return;
+ }
+
+ while ((tok = gettokens(fp, line, tokens, &ntokens))
+ != CONFIG_UNKNOWN) {
+ switch(tok) {
+ case CONFIG_PEER:
+ case CONFIG_SERVER:
+ case CONFIG_BROADCAST:
+ if (tok == CONFIG_PEER)
+ hmode = MODE_ACTIVE;
+ else if (tok == CONFIG_SERVER)
+ hmode = MODE_CLIENT;
+ else
+ hmode = MODE_BROADCAST;
+
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "No address for %s, line ignored",
+ tokens[0]);
+ break;
+ }
+
+ if (!getnetnum(tokens[1], &peeraddr, 0)) {
+ errflg = -1;
+ } else {
+ errflg = 0;
+
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+ syslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ ntoa(&peeraddr));
+ break;
+ }
+ }
+
+ peerversion = NTP_VERSION;
+ minpoll = NTP_MINDPOLL;
+ maxpoll = NTP_MAXDPOLL;
+ peerkey = 0;
+ peerflags = 0;
+ ttl = 0;
+ for (i = 2; i < ntokens; i++)
+ switch (matchkey(tokens[i], mod_keywords)) {
+ case CONF_MOD_VERSION:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "peer/server version requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if ((u_char)peerversion > NTP_VERSION
+ || (u_char)peerversion < NTP_OLDVERSION) {
+ syslog(LOG_ERR,
+ "inappropriate version number %s, line ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_KEY:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "key: argument required");
+ errflg = 1;
+ break;
+ }
+ peerkey = atol(tokens[++i]);
+ peerflags |= FLAG_AUTHENABLE;
+ break;
+
+ case CONF_MOD_MINPOLL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "minpoll: argument required");
+ errflg = 1;
+ break;
+ }
+ minpoll = atoi(tokens[++i]);
+ if (minpoll < NTP_MINPOLL)
+ minpoll = NTP_MINPOLL;
+ break;
+
+ case CONF_MOD_MAXPOLL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "maxpoll: argument required"
+);
+ errflg = 1;
+ break;
+ }
+ maxpoll = atoi(tokens[++i]);
+ if (maxpoll > NTP_MAXPOLL)
+ maxpoll = NTP_MAXPOLL;
+ break;
+
+ case CONF_MOD_PREFER:
+ peerflags |= FLAG_PREFER;
+ break;
+
+ case CONF_MOD_TTL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "ttl: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONF_MOD_MODE:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "mode: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = 1;
+ break;
+ }
+ if (minpoll > maxpoll) {
+ syslog(LOG_ERR, "config error: minpoll > maxpoll");
+ errflg = 1;
+ }
+ if (errflg == 0) {
+ if (peer_config(&peeraddr,
+ (struct interface *)0, hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl, peerkey)
+ == 0) {
+ syslog(LOG_ERR,
+ "configuration of %s failed",
+ ntoa(&peeraddr));
+ }
+ } else if (errflg == -1) {
+ save_resolve(tokens[1], hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl, peerkey);
+ }
+ break;
+
+ case CONFIG_PRECISION:
+ if (ntokens >= 2) {
+ i = atoi(tokens[1]);
+ if (i >= 0 || i < -25)
+ syslog(LOG_ERR,
+ "unlikely precision %s, line ignored",
+ tokens[1]);
+ else
+ proto_config(PROTO_PRECISION, i);
+ }
+ break;
+
+ case CONFIG_DRIFTFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_FREQ_FILE, tokens[1]);
+ else
+ stats_config(STATS_FREQ_FILE, (char *)0);
+ break;
+
+ case CONFIG_PIDFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_PID_FILE, tokens[1]);
+ else
+ stats_config(STATS_PID_FILE, (char *)0);
+ break;
+
+ case CONFIG_BROADCASTCLIENT:
+ proto_config(PROTO_BROADCLIENT, 1);
+ break;
+
+ case CONFIG_MULTICASTCLIENT:
+ if (ntokens > 1) {
+ for (i = 1; i < ntokens; i++) {
+ if (getnetnum(tokens[i], &peeraddr, 1))
+ proto_config(PROTO_MULTICAST_ADD,
+ peeraddr.sin_addr.s_addr);
+ }
+ } else
+ proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP));
+ break;
+
+ case CONFIG_AUTHENTICATE:
+ errflg = 0;
+ if (ntokens >= 2) {
+ if (STREQ(tokens[1], "yes"))
+ proto_config(PROTO_AUTHENTICATE, 1);
+ else if (STREQ(tokens[1], "no"))
+ proto_config(PROTO_AUTHENTICATE, 0);
+ else
+ errflg++;
+ } else {
+ errflg++;
+ }
+
+ if (errflg)
+ syslog(LOG_ERR,
+ "should be `authenticate yes|no'");
+ break;
+
+ case CONFIG_KEYS:
+ if (ntokens >= 2) {
+ getauthkeys(tokens[1]);
+ if ((int)strlen(tokens[1]) >= MAXFILENAME) {
+ syslog(LOG_ERR,
+ "key file name too long (>%d, sigh), no name resolution possible",
+ MAXFILENAME);
+ } else {
+ have_keyfile = 1;
+ (void)strcpy(keyfile, tokens[1]);
+ }
+ }
+ break;
+
+ case CONFIG_MONITOR:
+ errflg = 0;
+ if (ntokens >= 2) {
+ if (STREQ(tokens[1], "yes"))
+ mon_start(MON_ON);
+ else if (STREQ(tokens[1], "no"))
+ mon_stop(MON_ON);
+ else
+ errflg++;
+ } else {
+ errflg++;
+ }
+
+ if (errflg)
+ syslog(LOG_ERR,
+ "should be `monitor yes|no'");
+ break;
+
+ case CONFIG_AUTHDELAY:
+ if (ntokens >= 2) {
+ l_fp tmp;
+
+ if (!atolfp(tokens[1], &tmp)) {
+ syslog(LOG_ERR,
+ "authdelay value %s undecodable",
+ tokens[1]);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "authdelay value %s is unlikely",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_AUTHDELAY, tmp.l_f);
+ }
+ }
+ break;
+
+ case CONFIG_RESTRICT:
+ if (ntokens < 2) {
+ syslog(LOG_ERR, "restrict requires an address");
+ break;
+ }
+ if (STREQ(tokens[1], "default"))
+ peeraddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion as flags, peerkey as mflags. Ick.
+ */
+ peerversion = 0;
+ peerkey = 0;
+ errflg = 0;
+ maskaddr.sin_addr.s_addr = ~0;
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i], res_keywords)) {
+ case CONF_RES_MASK:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "mask keyword needs argument");
+ errflg++;
+ break;
+ }
+ i++;
+ if (!getnetnum(tokens[i], &maskaddr, 1))
+ errflg++;
+ break;
+
+ case CONF_RES_IGNORE:
+ peerversion |= RES_IGNORE;
+ break;
+
+ case CONF_RES_NOSERVE:
+ peerversion |= RES_DONTSERVE;
+ break;
+
+ case CONF_RES_NOTRUST:
+ peerversion |= RES_DONTTRUST;
+ break;
+
+ case CONF_RES_NOQUERY:
+ peerversion |= RES_NOQUERY;
+ break;
+
+ case CONF_RES_NOMODIFY:
+ peerversion |= RES_NOMODIFY;
+ break;
+
+ case CONF_RES_NOPEER:
+ peerversion |= RES_NOPEER;
+ break;
+
+ case CONF_RES_NOTRAP:
+ peerversion |= RES_NOTRAP;
+ break;
+
+ case CONF_RES_LPTRAP:
+ peerversion |= RES_LPTRAP;
+ break;
+
+ case CONF_RES_NTPPORT:
+ peerkey |= RESM_NTPONLY;
+ break;
+
+ case CONF_RES_LIMITED:
+ peerversion |= RES_LIMITED;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+ }
+ if (SRCADR(&peeraddr) == htonl(INADDR_ANY))
+ maskaddr.sin_addr.s_addr = 0;
+ if (!errflg)
+ restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr,
+ (int)peerkey, peerversion);
+ break;
+
+ case CONFIG_BDELAY:
+ if (ntokens >= 2) {
+ l_fp tmp;
+
+ if (!atolfp(tokens[1], &tmp)) {
+ syslog(LOG_ERR,
+ "broadcastdelay value %s undecodable",
+ tokens[1]);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "broadcastdelay value %s is unlikely",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_BROADDELAY, tmp.l_f);
+ }
+ }
+ break;
+
+ case CONFIG_TRUSTEDKEY:
+ for (i = 1; i < ntokens; i++) {
+ u_long tkey;
+
+ tkey = atol(tokens[i]);
+ if (tkey == 0) {
+ syslog(LOG_ERR,
+ "trusted key %s unlikely",
+ tokens[i]);
+ } else {
+ authtrust(tkey, 1);
+ }
+ }
+ break;
+
+ case CONFIG_REQUESTKEY:
+ if (ntokens >= 2) {
+ u_long rkey;
+
+ if (!atouint(tokens[1], &rkey)) {
+ syslog(LOG_ERR,
+ "%s is undecodeable as request key",
+ tokens[1]);
+ } else if (rkey == 0) {
+ syslog(LOG_ERR,
+ "%s makes a poor request keyid",
+ tokens[1]);
+ } else {
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "set info_auth_key to %lu\n", rkey);
+#endif
+ info_auth_keyid = rkey;
+ }
+ }
+ break;
+
+ case CONFIG_CONTROLKEY:
+ if (ntokens >= 2) {
+ u_long ckey;
+ extern u_long ctl_auth_keyid;
+
+ ckey = atol(tokens[1]);
+ if (ckey == 0) {
+ syslog(LOG_ERR,
+ "%s makes a poor control keyid",
+ tokens[1]);
+ } else {
+ ctl_auth_keyid = ckey;
+ }
+ }
+ break;
+
+ case CONFIG_TRAP:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no address for trap command, line ignored");
+ break;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion for port number. Barf.
+ */
+ errflg = 0;
+ peerversion = 0;
+ localaddr = 0;
+ for (i = 2; i < ntokens-1; i++)
+ switch (matchkey(tokens[i], trap_keywords)) {
+ case CONF_TRAP_PORT:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "trap port requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if (peerversion <= 0
+ || peerversion > 32767) {
+ syslog(LOG_ERR,
+ "invalid port number %s, trap ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_TRAP_INTERFACE:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "trap interface requires an argument");
+ errflg = 1;
+ break;
+ }
+
+ if (!getnetnum(tokens[++i],
+ &maskaddr, 1)) {
+ errflg = 1;
+ break;
+ }
+
+ localaddr = findinterface(&maskaddr);
+ if (localaddr == NULL) {
+ syslog(LOG_ERR,
+ "can't find interface with address %s",
+ ntoa(&maskaddr));
+ errflg = 1;
+ }
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+
+ if (!errflg) {
+ extern struct interface *any_interface;
+
+ if (peerversion != 0)
+ peeraddr.sin_port = htons(peerversion);
+ else
+ peeraddr.sin_port = htons(TRAPPORT);
+ if (localaddr == NULL)
+ localaddr = any_interface;
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ syslog(LOG_ERR,
+ "can't set trap for %s, no resources",
+ ntoa(&peeraddr));
+ }
+ break;
+
+ case CONFIG_FUDGE:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no address for fudge command, line ignored");
+ break;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ if (!ISREFCLOCKADR(&peeraddr)) {
+ syslog(LOG_ERR,
+ "%s is inappropriate address for the fudge command, line ignored",
+ ntoa(&peeraddr));
+ break;
+ }
+
+ memset((char *)&clock, 0, sizeof clock);
+ errflg = 0;
+ for (i = 2; i < ntokens-1; i++) {
+ switch (c = matchkey(tokens[i],
+ fudge_keywords)) {
+ case CONF_FDG_TIME1:
+ if (!atolfp(tokens[++i],
+ &clock.fudgetime1)) {
+ syslog(LOG_ERR,
+ "fudge %s time1 value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVETIME1;
+ break;
+
+ case CONF_FDG_TIME2:
+ if (!atolfp(tokens[++i],
+ &clock.fudgetime2)) {
+ syslog(LOG_ERR,
+ "fudge %s time2 value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVETIME2;
+ break;
+
+ case CONF_FDG_STRATUM:
+ if (!atoint(tokens[++i],
+ (long *)&clock.fudgeval1)) {
+ syslog(LOG_ERR,
+ "fudge %s stratum value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVEVAL1;
+ break;
+
+ case CONF_FDG_REFID:
+ strncpy((char *)&clock.fudgeval2,
+ tokens[++i], 4);
+ clock.haveflags |= CLK_HAVEVAL2;
+ break;
+
+ case CONF_FDG_FLAG1:
+ case CONF_FDG_FLAG2:
+ case CONF_FDG_FLAG3:
+ case CONF_FDG_FLAG4:
+ if (!atouint(tokens[++i], &peerkey)
+ || peerkey > 1) {
+ syslog(LOG_ERR,
+ "fudge %s flag value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ switch(c) {
+ case CONF_FDG_FLAG1:
+ c = CLK_FLAG1;
+ clock.haveflags|=CLK_HAVEFLAG1;
+ break;
+ case CONF_FDG_FLAG2:
+ c = CLK_FLAG2;
+ clock.haveflags|=CLK_HAVEFLAG2;
+ break;
+ case CONF_FDG_FLAG3:
+ c = CLK_FLAG3;
+ clock.haveflags|=CLK_HAVEFLAG3;
+ break;
+ case CONF_FDG_FLAG4:
+ c = CLK_FLAG4;
+ clock.haveflags|=CLK_HAVEFLAG4;
+ break;
+ }
+ if (peerkey == 0)
+ clock.flags &= ~c;
+ else
+ clock.flags |= c;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = -1;
+ break;
+ }
+ }
+
+#ifdef REFCLOCK
+ /*
+ * If reference clock support isn't defined the
+ * fudge line will still be accepted and syntax
+ * checked, but will essentially do nothing.
+ */
+ if (!errflg) {
+ refclock_control(&peeraddr, &clock,
+ (struct refclockstat *)0);
+ }
+#endif
+ break;
+
+ case CONFIG_STATSDIR:
+ if (ntokens >= 2) {
+ stats_config(STATS_STATSDIR,tokens[1]);
+ }
+ break;
+
+ case CONFIG_STATISTICS:
+ for (i = 1; i < ntokens; i++) {
+ filegen = filegen_get(tokens[i]);
+
+ if (filegen == NULL) {
+ syslog(LOG_ERR,
+ "no statistics named %s available",
+ tokens[i]);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("enabling filegen for %s statistics \"%s%s\"\n",
+ tokens[i], filegen->prefix, filegen->basename);
+#endif
+ filegen->flag |= FGEN_FLAG_ENABLED;
+ }
+ break;
+
+ case CONFIG_FILEGEN:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no id for filegen command, line ignored");
+ break;
+ }
+
+ filegen = filegen_get(tokens[1]);
+ if (filegen == NULL) {
+ syslog(LOG_ERR,
+ "unknown filegen \"%s\" ignored",
+ tokens[1]);
+ break;
+ }
+ /*
+ * peerversion is (ab)used for filegen file (index)
+ * peerkey is (ab)used for filegen type
+ * peerflags is (ab)used for filegen flags
+ */
+ peerversion = 0;
+ peerkey = filegen->type;
+ peerflags = filegen->flag;
+ errflg = 0;
+
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i], filegen_keywords)) {
+ case CONF_FGEN_FILE:
+ if (i >= ntokens - 1) {
+ syslog(LOG_ERR,
+ "filegen %s file requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerversion = ++i;
+ break;
+ case CONF_FGEN_TYPE:
+ if (i >= ntokens -1) {
+ syslog(LOG_ERR,
+ "filegen %s type requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerkey = matchkey(tokens[++i], fgen_types);
+ if (peerkey == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "filegen %s unknown type \"%s\"",
+ tokens[1], tokens[i]);
+ errflg = i;
+ break;
+ }
+ break;
+
+ case CONF_FGEN_FLAG_LINK:
+ peerflags |= FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_NOLINK:
+ peerflags &= ~FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_ENABLE:
+ peerflags |= FGEN_FLAG_ENABLED;
+ break;
+
+ case CONF_FGEN_FLAG_DISABLE:
+ peerflags &= ~FGEN_FLAG_ENABLED;
+ break;
+ }
+ }
+ if (!errflg) {
+ filegen_config(filegen, tokens[peerversion],
+ (u_char)peerkey, (u_char)peerflags);
+ }
+ break;
+
+ case CONFIG_SETVAR:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for setvar command - line ignored");
+ } else {
+ set_sys_var(tokens[1], strlen(tokens[1])+1, RW |
+ ((((ntokens > 2) && !strcmp(tokens[2],
+ "default"))) ? DEF : 0));
+ }
+ break;
+
+ case CONFIG_CLIENTLIMIT:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for clientlimit command - line ignored");
+ } else {
+ u_long i;
+ if (!atouint(tokens[1], &i) || !i) {
+ syslog(LOG_ERR,
+ "illegal value for clientlimit command - line ignored");
+ } else {
+ extern u_long client_limit;
+ char bp[80];
+
+#ifdef DEBUG
+ if (debug)
+ sprintf(bp, "client_limit=%lu", i);
+#endif
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit = i;
+ }
+ }
+ break;
+
+ case CONFIG_CLIENTPERIOD:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for clientperiod command - line ignored");
+ } else {
+ u_long i;
+
+ if (!atouint(tokens[1], &i) || i < 64) {
+ syslog(LOG_ERR,
+ "illegal value for clientperiod command - line ignored");
+ } else {
+ extern u_long client_limit_period;
+ char bp[80];
+
+ sprintf(bp, "client_limit_period=%ld", i);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit_period = i;
+ }
+ }
+ break;
+
+ case CONFIG_ENABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "enable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 1L);
+ }
+ break;
+
+ case CONFIG_DISABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "disable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 0L);
+ }
+ break;
+
+ case CONFIG_PHONE:
+ for (i = 1; i < ntokens && i < MAXPHONE; i++) {
+ (void)strncpy(sys_phone[i - 1],
+ tokens[i], MAXDIAL);
+ }
+ sys_phone[i - 1][0] = '\0';
+ break;
+ }
+ }
+ (void) fclose(fp);
+
+ if (res_fp != NULL) {
+ /*
+ * Need name resolution
+ */
+ do_resolve_internal();
+ }
+}
+
+
+
+/*
+ * gettokens - read a line and return tokens
+ */
+static int
+gettokens(fp, line, tokenlist, ntokens)
+ FILE *fp;
+ char *line;
+ char **tokenlist;
+ int *ntokens;
+{
+ register char *cp;
+ register int eol;
+ register int ntok;
+ register int quoted = 0;
+
+ /*
+ * Find start of first token
+ */
+again:
+ while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
+ cp = line;
+ while (ISSPACE(*cp))
+ cp++;
+ if (!ISEOL(*cp))
+ break;
+ }
+ if (cp == NULL) {
+ *ntokens = 0;
+ return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */
+ }
+
+ /*
+ * Now separate out the tokens
+ */
+ eol = 0;
+ ntok = 0;
+ while (!eol) {
+ tokenlist[ntok++] = cp;
+ while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
+ quoted ^= (*cp++ == '"');
+
+ if (ISEOL(*cp)) {
+ *cp = '\0';
+ eol = 1;
+ } else { /* must be space */
+ *cp++ = '\0';
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ eol = 1;
+ }
+ if (ntok == MAXTOKENS)
+ eol = 1;
+ }
+
+ /*
+ * Return the match
+ */
+ *ntokens = ntok;
+ ntok = matchkey(tokenlist[0], keywords);
+ if (ntok == CONFIG_UNKNOWN)
+ goto again;
+ return ntok;
+}
+
+
+
+/*
+ * matchkey - match a keyword to a list
+ */
+static int
+matchkey(word, keys)
+ register char *word;
+ register struct keyword *keys;
+{
+ for (;;) {
+ if (keys->keytype == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "configure: keyword \"%s\" unknown, line ignored",
+ word);
+ return CONFIG_UNKNOWN;
+ }
+ if (STRSAME(word, keys->text))
+ return keys->keytype;
+ keys++;
+ }
+}
+
+
+/*
+ * getnetnum - return a net number (this is crude, but careful)
+ */
+static int
+getnetnum(num, addr, complain)
+ char *num;
+ struct sockaddr_in *addr;
+ int complain;
+{
+ register char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ char buf[80]; /* will core dump on really stupid stuff */
+ u_long netnum;
+
+/* XXX ELIMINATE replace with decodenetnum */
+ cp = num;
+ netnum = 0;
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != '\0')
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ netnum <<= 8;
+ netnum += temp;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("getnetnum %s step %d buf %s temp %d netnum %lu\n",
+ num, i, buf, temp, netnum);
+#endif
+ }
+
+ if (i < 4) {
+ if (complain)
+ syslog(LOG_ERR,
+ "configure: \"%s\" not valid host number, line ignored",
+ num);
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "configure: \"%s\" not valid host number, line ignored\n",
+ num);
+#endif
+ return 0;
+ }
+
+ /*
+ * make up socket address. Clear it out for neatness.
+ */
+ memset((char *)addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(NTP_PORT);
+ addr->sin_addr.s_addr = htonl(netnum);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("getnetnum given %s, got %s (%lx)\n",
+ num, ntoa(addr), netnum);
+#endif
+ return 1;
+}
+
+
+/*
+ * catchchild - receive the resolver's exit status
+ */
+static RETSIGTYPE
+catchchild(sig)
+int sig;
+{
+ /*
+ * We only start up one child, and if we're here
+ * it should have already exited. Hence the following
+ * shouldn't hang. If it does, please tell me.
+ */
+ (void) wait(0);
+}
+
+
+/*
+ * save_resolve - save configuration info into a file for later name resolution
+ */
+static void
+save_resolve(name, mode, version, minpoll, maxpoll, flags, ttl, keyid)
+ char *name;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long keyid;
+{
+ if (res_fp == NULL) {
+ (void) strcpy(res_file, RES_TEMPFILE);
+ (void) mktemp(res_file);
+ res_fp = fopen(res_file, "w");
+ if (res_fp == NULL) {
+ syslog(LOG_ERR, "open failed for %s: %m", res_file);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug) {
+ printf("resolving %s\n", name);
+ }
+#endif
+
+ (void) fprintf(res_fp, "%s %d %d %d %d %d %d %lu\n", name, mode,
+ version, minpoll, maxpoll, flags, ttl, keyid);
+}
+
+
+/*
+ * abort_resolve - terminate the resolver stuff and delete the file
+ */
+static void
+abort_resolve()
+{
+ /*
+ * In an ideal world we would might reread the file and
+ * log the hosts which aren't getting configured. Since
+ * this is too much work, however, just close and delete
+ * the temp file.
+ */
+ if (res_fp != NULL)
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+ (void) unlink(res_file);
+}
+
+
+#define KEY_TYPE_MD5 4
+
+/*
+ * do_resolve_internal - start up the resolver function (not program)
+ */
+static void
+do_resolve_internal()
+{
+ int i;
+
+ extern u_long req_keyid; /* request keyid */
+ extern char *req_file; /* name of the file with config info */
+ extern u_long info_auth_keyid;
+
+ if (res_fp == NULL) {
+ /* belch */
+ syslog(LOG_ERR,
+ "internal error in do_resolve_internal: res_fp == NULL");
+ exit(1);
+ }
+
+ /* we are done with this now */
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+ /* find a keyid */
+ if (info_auth_keyid == 0)
+ req_keyid = 65535;
+ else
+ req_keyid = info_auth_keyid;
+
+ /* if doesn't exist, make up one at random */
+ if (!authhavekey(req_keyid)) {
+ char rankey[9];
+ struct timeval now;
+
+ /* generate random key */
+ GETTIMEOFDAY(&now, (struct timezone *)0);
+ srand(now.tv_sec * now.tv_usec);
+ for (i = 0; i < 8; i++)
+ rankey[i] = (rand() % 255) + 1;
+ rankey[8] = 0;
+ authusekey(req_keyid, KEY_TYPE_MD5, rankey);
+ }
+
+ /* save keyid so we will accept config requests with it */
+ info_auth_keyid = req_keyid;
+ req_file = res_file; /* set up pointer to res file */
+ (void) signal_no_reset(SIGCHLD, catchchild);
+
+ i = fork();
+ if (i == 0) {
+ /*
+ * this used to close everything
+ * I don't think this is necessary
+ */
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ ntp_intres();
+
+ /*
+ * If we got here, the intres code screwed up.
+ * Print something so we don't die without complaint
+ */
+ syslog(LOG_ERR, "call to ntp_intres lost");
+ abort_resolve();
+ exit(1);
+ }
+ if (i == -1) {
+ syslog(LOG_ERR, "fork() failed, can't start ntp_intres");
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ abort_resolve();
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_control.c b/usr.sbin/xntpd/xntpd/ntp_control.c
new file mode 100644
index 0000000..af4365c
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_control.c
@@ -0,0 +1,2690 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+ u_short flags; /* flags word */
+ void (*handler)(); /* routine to handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+static u_short ctlclkstatus P((struct refclockstat *));
+static void ctl_flushpkt P((int));
+static void ctl_putdata P((char *, int, int));
+static void ctl_putstr P((char *, char *, int));
+static void ctl_putlfp P((char *, l_fp *));
+static void ctl_putfp P((char *, s_fp));
+static void ctl_putufp P((char *, u_fp));
+static void ctl_putuint P((char *, u_long));
+static void ctl_puthex P((char *, u_long));
+static void ctl_putint P((char *, long));
+static void ctl_putts P((char *, l_fp *));
+static void ctl_putadr P((char *, u_long));
+static void ctl_putid P((char *, char *));
+static void ctl_putarray P((char *, s_fp *, int));
+static void ctl_putsys P((int));
+static void ctl_putpeer P((int, struct peer *));
+#ifdef REFCLOCK
+static void ctl_putclock P((int, struct refclockstat *, int));
+#endif /* REFCLOCK */
+static struct ctl_var *ctl_getitem P((struct ctl_var *, char **));
+static u_long count_var P((struct ctl_var *));
+static void control_unspec P((struct recvbuf *, int));
+static void read_status P((struct recvbuf *, int));
+static void read_variables P((struct recvbuf *, int));
+static void write_variables P((struct recvbuf *, int));
+static void read_clock_status P((struct recvbuf *, int));
+static void write_clock_status P((struct recvbuf *, int));
+static void set_trap P((struct recvbuf *, int));
+static void unset_trap P((struct recvbuf *, int));
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_in *, struct interface *));
+
+static struct ctl_proc control_codes[] = {
+ { CTL_OP_UNSPEC, NOAUTH, control_unspec },
+ { CTL_OP_READSTAT, NOAUTH, read_status },
+ { CTL_OP_READVAR, NOAUTH, read_variables },
+ { CTL_OP_WRITEVAR, AUTH, write_variables },
+ { CTL_OP_READCLOCK, NOAUTH, read_clock_status },
+ { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
+ { CTL_OP_SETTRAP, NOAUTH, set_trap },
+ { CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
+ { NO_REQUEST, 0 }
+};
+
+/*
+ * System variable values. The array can be indexed by
+ * the variable index to find the textual name.
+ */
+static struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "poll" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_OFFSET, RO, "phase" }, /* 10 */
+ { CS_DRIFT, RO, "freq" }, /* 11 */
+ { CS_COMPLIANCE, RO, "error" }, /* 12 */
+ { CS_CLOCK, RO, "clock" }, /* 13 */
+ { CS_LEAPIND, RW, "leapindicator" }, /* 14 */
+ { CS_LEAPWARNING, RW, "leapwarning" }, /* 15 */
+ { CS_PROCESSOR, RO, "processor" }, /* 16 */
+ { CS_SYSTEM, RO, "system" }, /* 17 */
+ { CS_KEYID, RO, "keyid" }, /* 18 */
+ { CS_REFSKEW, RO, "refskew" }, /* 19 */
+ { CS_VARLIST, RO, "sys_var_list" },/* 20 */
+ { 0, EOV, "" }
+};
+
+static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
+
+/*
+ * System variables we print by default (in fuzzball order, more-or-less)
+ */
+static u_char def_sys_var[] = {
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_PEERID,
+ CS_REFID,
+ CS_REFTIME,
+ CS_POLL,
+ CS_CLOCK,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_COMPLIANCE,
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xmt" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_VALID, RO, "valid" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_DISPERSION,RO, "dispersion" }, /* 26 */
+ { CP_KEYID, RO, "keyid" }, /* 27 */
+ { CP_FILTDELAY, RO, "filtdelay" }, /* 28 */
+ { CP_FILTOFFSET, RO, "filtoffset" }, /* 29 */
+ { CP_PMODE, RO, "pmode" }, /* 30 */
+ { CP_RECEIVED, RO, "received" }, /* 31 */
+ { CP_SENT, RO, "sent" }, /* 32 */
+ { CP_FILTERROR, RO, "filterror" }, /* 33 */
+ { CP_FLASH, RO, "flash" }, /* 34 */
+ { CP_DISP, PADDING,"" }, /* 35 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 36 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_KEYID,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REFTIME,
+ CP_DELAY,
+ CP_OFFSET,
+ CP_DISPERSION,
+ CP_REACH,
+ CP_VALID,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_LEAP,
+ CP_FLASH,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" },/* 13 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device= known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+
+/*
+ * System and processor definitions. These will change for the gizmo board.
+ */
+#ifndef HAVE_UNAME
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX"
+#endif
+#ifndef STR_PROCESSOR
+#define STR_PROCESSOR "unknown"
+#endif
+
+static char str_system[] = STR_SYSTEM;
+static char str_processor[] = STR_PROCESSOR;
+#else
+#include <sys/utsname.h>
+static struct utsname utsname;
+#endif /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send
+ * a copy of each async message to each live one. Traps time
+ * out after an hour, it is up to the trap receipient to
+ * keep resetting it to avoid being timed out.
+ */
+/* ntp_request.c */
+ struct ctl_trap ctl_trap[CTL_MAXTRAPS];
+ int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type.
+ * This list will only be used iff the reference clock driver doesn't
+ * set peer->sstclktype to something different than CTL_SST_TS_UNSPEC.
+ */
+static u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE - default value - driver supplies actual value in peer->sstclktype */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_LF, /* REFCLK_OMEGA_TRUETIME (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* REFCLK_MSF_EES (14) */
+ CTL_SST_TS_UHF, /* REFCLK_GPSTM_TRUETIME (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACT (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_MOTO (21) */
+ CTL_SST_TS_ATOM /* REFCLK_ATOM_PPS (22) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+u_long ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from the main routines
+ */
+extern int debug;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+extern struct peer *assoc_hash[];
+extern int pps_control; /* flag for 1-pps signal present */
+/*
+ * Importations from the protocol module
+ */
+extern u_char sys_leap;
+extern u_char sys_stratum;
+extern s_char sys_precision;
+extern s_fp sys_rootdelay;
+extern u_fp sys_rootdispersion;
+extern u_long sys_refid;
+extern l_fp sys_reftime;
+extern l_fp sys_refskew;
+extern u_char sys_poll;
+extern struct peer *sys_peer;
+/*
+ * Imported from the loop filter module
+ */
+extern l_fp last_offset;
+extern s_fp drift_comp;
+extern u_fp sys_maxd;
+extern int pll_control;
+/*
+ * Imported from the leap module
+ */
+extern u_char leap_indicator;
+extern u_char leap_warning;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static u_short res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_in *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static u_long res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* set to 1 if this is async trap response */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control()
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsname);
+#endif /* HAVE_UNAME */
+
+ ctl_clr_stats();
+
+ ctl_auth_keyid = 0;
+ ctl_sys_last_event = EVNT_UNSPEC;
+ ctl_sys_num_events = 0;
+
+ num_ctl_traps = 0;
+ for (i = 0; i < CTL_MAXTRAPS; i++)
+ ctl_trap[i].tr_flags = 0;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(errcode)
+ int errcode;
+{
+#ifdef DEBUG
+ if (debug >= 4)
+ printf("sending control error %d\n", errcode);
+#endif
+ /*
+ * fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE|CTL_ERROR|(res_opcode & CTL_OP_MASK);
+ rpkt.status = htons((errcode<<8) & 0xff00);
+ rpkt.count = 0;
+
+ /*
+ * send packet and bump counters
+ */
+ if (res_authenticate) {
+ int maclen;
+
+ *(u_long *)((u_char *)&rpkt + CTL_HEADER_LEN)
+ = htonl(res_keyid);
+ maclen =
+ authencrypt(res_keyid, (U_LONG *)&rpkt, CTL_HEADER_LEN);
+ sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN);
+ }
+ numctlerrors++;
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+void
+process_control(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ntp_control *pkt;
+ register int req_count;
+ register int req_data;
+ register struct ctl_proc *cc;
+ int properlen;
+ int maclen;
+
+#ifdef DEBUG
+ if (debug)
+ printf("in process_control()\n");
+#endif
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
+ || pkt->offset != 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid format in control packet\n");
+#endif
+ if (rbufp->recv_length < CTL_HEADER_LEN)
+ numctltooshort++;
+ if (pkt->r_m_e_op & CTL_RESPONSE)
+ numctlinputresp++;
+ if (pkt->r_m_e_op & CTL_MORE)
+ numctlinputfrag++;
+ if (pkt->r_m_e_op & CTL_ERROR)
+ numctlinputerr++;
+ if (pkt->offset != 0)
+ numctlbadoffset++;
+ return;
+ }
+ res_version = PKT_VERSION(pkt->li_vn_mode);
+ if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
+#ifdef DEBUG
+ if (debug)
+ printf("unknown version %d in control packet\n",
+ res_version);
+#endif
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent responses
+ */
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL);
+ res_opcode = pkt->r_m_e_op;
+ rpkt.sequence = pkt->sequence;
+ rpkt.associd = pkt->associd;
+ rpkt.status = 0;
+ res_offset = 0;
+ res_associd = htons(pkt->associd);
+ res_async = 0;
+ res_authenticate = 0;
+ res_keyid = 0;
+ res_authokay = 0;
+ req_count = (int)htons(pkt->count);
+ datanotbinflag = 0;
+ datalinelen = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ /*
+ * We're set up now. Make sure we've got at least
+ * enough incoming data space to match the count.
+ */
+ req_data = rbufp->recv_length - CTL_HEADER_LEN;
+ if (req_data < req_count || rbufp->recv_length & 0x3) {
+ ctl_error(CERR_BADFMT);
+ numctldatatooshort++;
+ return;
+ }
+
+ properlen = req_count + CTL_HEADER_LEN;
+#ifdef DEBUG
+ if (debug >= 2 && (rbufp->recv_length & 0x3) != 0)
+ printf("Packet length %d unrounded\n", rbufp->recv_length);
+#endif
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+
+ if ((rbufp->recv_length & (sizeof(u_long)-1)) == 0
+ && (maclen = (rbufp->recv_length - properlen)) >= MIN_MAC_LEN
+ && maclen <= MAX_MAC_LEN) {
+
+ res_authenticate = 1;
+ res_keyid = ntohl(*(u_long *)((u_char *)pkt + properlen));
+
+#ifdef DEBUG
+ if (debug >= 3)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authhavekey(res_keyid)) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("keyid %lu unknown\n", res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (U_LONG *)pkt,
+ rbufp->recv_length - maclen)) {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authentication failed\n");
+#endif
+ res_keyid = 0;
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("opcode %d, found command handler\n",
+ res_opcode);
+#endif
+ if (cc->flags == AUTH && (!res_authokay
+ || res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(peer)
+ register struct peer *peer;
+{
+ register u_short status;
+
+ status = CTL_PST_SEL_REJECT;
+ if (peer->was_sane != 0)
+ status = CTL_PST_SEL_SANE;
+ if (peer->correct != 0)
+ status = CTL_PST_SEL_CORRECT;
+ if (peer->candidate != 0)
+ status = CTL_PST_SEL_SELCAND;
+ if (peer->select != 0)
+ status = CTL_PST_SEL_SYNCCAND;
+ if (peer == sys_peer) {
+ status = CTL_PST_SEL_DISTSYSPEER;
+ if (peer->synch < NTP_MAXDISTANCE) {
+ status = CTL_PST_SEL_SYSPEER;
+ if (pps_control)
+ status = CTL_PST_SEL_PPS;
+ }
+ }
+ if (peer->flags & FLAG_CONFIG)
+ status |= CTL_PST_CONFIG;
+ if (peer->flags & FLAG_AUTHENABLE) {
+ status |= CTL_PST_AUTHENABLE;
+ if (peer->flags & FLAG_AUTHENTIC)
+ status |= CTL_PST_AUTHENTIC;
+ }
+ if (peer->reach != 0)
+ status |= CTL_PST_REACH;
+
+ return (u_short)CTL_PEER_STATUS(status, peer->num_events,
+ peer->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+static u_short
+ctlclkstatus(clock)
+ struct refclockstat *clock;
+{
+ return ((u_short)(clock->currentstatus) << 8)
+ | (u_short)(clock->lastevent);
+}
+
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus()
+{
+ register u_char clock;
+
+ clock = CTL_SST_TS_UNSPEC;
+ if (sys_peer != 0) {
+ if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
+ clock = sys_peer->sstclktype;
+ if (pps_control)
+ clock |= CTL_SST_TS_PPS;
+ } else {
+ if (sys_peer->refclktype < sizeof(clocktypes))
+ clock = clocktypes[sys_peer->refclktype];
+ if (pps_control)
+ clock |= CTL_SST_TS_PPS;
+ }
+ }
+ return (u_short)CTL_SYS_STATUS(sys_leap, clock,
+ ctl_sys_num_events, ctl_sys_last_event);
+}
+
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(more)
+ int more;
+{
+ int dlen;
+ int sendlen;
+
+ if (!more && datanotbinflag) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ }
+ dlen = datapt - (u_char *)rpkt.data;
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE|more|(res_opcode & CTL_OP_MASK);
+ rpkt.count = htons((u_short)dlen);
+ rpkt.offset = htons(res_offset);
+ if (res_async) {
+ register int i;
+
+ for (i = 0; i < CTL_MAXTRAPS; i++) {
+ if (ctl_trap[i].tr_flags & TRAP_INUSE) {
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ ctl_trap[i].tr_version, MODE_CONTROL);
+ rpkt.sequence = htons(ctl_trap[i].tr_sequence);
+ sendpkt(&ctl_trap[i].tr_addr,
+ ctl_trap[i].tr_localaddr,
+ -4,
+ (struct pkt *)&rpkt, sendlen);
+ if (!more)
+ ctl_trap[i].tr_sequence++;
+ numasyncmsgs++;
+ }
+ }
+ } else {
+ if (res_authenticate) {
+ int maclen;
+ int totlen = sendlen;
+
+ /*
+ * If we are going to authenticate, then there is
+ * an additional requirement that the MAC begin on
+ * a 64 bit boundary.
+ */
+ while (totlen & 7) {
+ *datapt++ = '\0';
+ totlen++;
+ }
+ *(u_long *)datapt = htonl(res_keyid);
+ maclen =
+ authencrypt(res_keyid, (U_LONG *)&rpkt, totlen);
+
+ sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt,
+ totlen + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt,
+ sendlen);
+ }
+ if (more)
+ numctlfrags++;
+ else
+ numctlresponses++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_offset += dlen;
+ datapt = (u_char *)rpkt.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and
+ * starting another if this one is full.
+ */
+static void
+ctl_putdata(dp, dlen, bin)
+ char *dp;
+ int dlen;
+ int bin; /* set to 1 when data is binary */
+{
+ int overhead;
+
+ overhead = 0;
+ if (!bin) {
+ datanotbinflag = 1;
+ overhead = 3;
+ if (datapt != rpkt.data) {
+ *datapt++ = ',';
+ datalinelen++;
+ if ((dlen + datalinelen + 1) >= MAXDATALINELEN) {
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ datalinelen = 0;
+ } else {
+ *datapt++ = ' ';
+ datalinelen++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+
+ memmove((char *)datapt, dp, dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ */
+static void
+ctl_putstr(tag, data, len)
+ char *tag;
+ char *data;
+ int len;
+{
+ register char *cp, *cq;
+ char buffer[400];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ if (len > 0) {
+ *cp++ = '=';
+ *cp++ = '"';
+ if (len > (sizeof(buffer) - (cp - buffer) - 1))
+ len = sizeof(buffer) - (cp - buffer) - 1;
+ memmove(cp, data, len);
+ cp += len;
+ *cp++ = '"';
+ }
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+
+/*
+ * ctl_putlfp - write a tagged, signed l_fp into the response packet
+ */
+static void
+ctl_putlfp(tag, ts)
+ char *tag;
+ l_fp *ts;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = lfptoms(ts, 3);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putfp - write a tagged s_fp number into the response
+ */
+static void
+ctl_putfp(tag, fp)
+ char *tag;
+ s_fp fp;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = fptoms(fp, 2);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putufp - write a tagged u_fp number into the response
+ */
+static void
+ctl_putufp(tag, ufp)
+ char *tag;
+ u_fp ufp;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = ufptoms(ufp, 2);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+static void
+ctl_putuint(tag, uval)
+ char *tag;
+ u_long uval;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%lu", uval);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the response
+ */
+static void
+ctl_puthex(tag, uval)
+ char *tag;
+ u_long uval;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%lx", uval);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+static void
+ctl_putint(tag, ival)
+ char *tag;
+ long ival;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%ld", ival);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+static void
+ctl_putts(tag, ts)
+ char *tag;
+ l_fp *ts;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & 0xffffffffL,
+ ts->l_uf & 0xffffffffL);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putadr - write a dotted quad IP address into the response
+ */
+static void
+ctl_putadr(tag, addr)
+ char *tag;
+ u_long addr;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = numtoa(addr);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putid - write a tagged clock ID into the response
+ */
+static void
+ctl_putid(tag, id)
+ char *tag;
+ char *id;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = id;
+ while (*cq != '\0' && (cq - id) < 4)
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element s_fp array into the response
+ */
+static void
+ctl_putarray(tag, arr, start)
+ char *tag;
+ s_fp *arr;
+ int start;
+{
+ register char *cp, *cq;
+ char buffer[200];
+ int i, ind;
+ int len;
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ /*
+ * Hack. We know the tag is either filtdelay, filtoffset,
+ * or filterror. Space over the shorter words one space.
+ */
+ if ((cp - buffer) < 11)
+ *cp++ = ' ';
+
+ i = start;
+ ind = 0;
+ do {
+ if (i == 0)
+ i = NTP_SHIFT;
+ i--;
+ if (ind) {
+ *cp++ = ' ';
+ } else {
+ ind = 1;
+ }
+ cq = fptoms(arr[i], 2);
+ len = strlen(cq);
+ while (len < 7) {
+ *cp++ = ' ';
+ len++;
+ }
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ } while(i != start);
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(varid)
+ int varid;
+{
+ l_fp tmp;
+
+ switch (varid) {
+ case CS_LEAP:
+ ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
+ break;
+ case CS_STRATUM:
+ ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
+ break;
+ case CS_PRECISION:
+ ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
+ break;
+ case CS_ROOTDELAY:
+ ctl_putfp(sys_var[CS_ROOTDELAY].text, sys_rootdelay);
+ break;
+ case CS_ROOTDISPERSION:
+ ctl_putufp(sys_var[CS_ROOTDISPERSION].text,
+ sys_rootdispersion);
+ break;
+ case CS_REFID:
+ if (sys_stratum > 1)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid);
+ else
+ ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid);
+ break;
+ case CS_REFTIME:
+ ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
+ break;
+ case CS_POLL:
+ ctl_putuint(sys_var[CS_POLL].text, sys_poll);
+ break;
+ case CS_PEERID:
+ if (sys_peer == NULL)
+ ctl_putuint(sys_var[CS_PEERID].text, 0);
+ else
+ ctl_putuint(sys_var[CS_PEERID].text,
+ sys_peer->associd);
+ break;
+ case CS_OFFSET:
+ ctl_putlfp(sys_var[CS_OFFSET].text, &last_offset);
+ break;
+ case CS_DRIFT:
+ ctl_putfp(sys_var[CS_DRIFT].text, drift_comp);
+ break;
+ case CS_COMPLIANCE:
+ ctl_putufp(sys_var[CS_COMPLIANCE].text, sys_maxd);
+ break;
+ case CS_CLOCK:
+ get_systime(&tmp);
+ ctl_putts(sys_var[CS_CLOCK].text, &tmp);
+ break;
+ case CS_LEAPIND:
+ ctl_putuint(sys_var[CS_LEAPIND].text, leap_indicator);
+ break;
+ case CS_LEAPWARNING:
+ ctl_putuint(sys_var[CS_LEAPWARNING].text, leap_warning);
+ break;
+ case CS_PROCESSOR:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
+ sizeof(str_processor) - 1);
+#else
+ ctl_putstr(sys_var[CS_PROCESSOR].text, utsname.machine,
+ strlen(utsname.machine));
+#endif /* HAVE_UNAME */
+ break;
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ ctl_putstr(sys_var[CS_SYSTEM].text, utsname.sysname,
+ strlen(utsname.sysname));
+#endif /* HAVE_UNAME */
+ break;
+ case CS_KEYID:
+ ctl_putuint(sys_var[CS_KEYID].text, 0);
+ break;
+ case CS_REFSKEW:
+ ctl_putlfp(sys_var[CS_REFSKEW].text, &sys_refskew);
+ break;
+ case CS_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *ss, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(sys_var[CS_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, sys_var[CS_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = sys_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = ext_sys_var; k && !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, i);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(varid, peer)
+ int varid;
+ struct peer *peer;
+{
+ switch (varid) {
+ case CP_CONFIG:
+ ctl_putuint(peer_var[CP_CONFIG].text,
+ ((peer->flags & FLAG_CONFIG) != 0));
+ break;
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[CP_AUTHENABLE].text,
+ ((peer->flags & FLAG_AUTHENABLE) != 0));
+ break;
+ case CP_AUTHENTIC:
+ ctl_putuint(peer_var[CP_AUTHENTIC].text,
+ ((peer->flags & FLAG_AUTHENTIC) != 0));
+ break;
+ case CP_SRCADR:
+ ctl_putadr(peer_var[CP_SRCADR].text,
+ peer->srcadr.sin_addr.s_addr);
+ break;
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(peer->srcadr.sin_port));
+ break;
+ case CP_DSTADR:
+ ctl_putadr(peer_var[CP_DSTADR].text,
+ peer->processed ?
+ peer->cast_flags & MDF_BCAST ?
+ peer->dstadr->bcast.sin_addr.s_addr:
+ peer->cast_flags ?
+ peer->dstadr->sin.sin_addr.s_addr ?
+ peer->dstadr->sin.sin_addr.s_addr:
+ peer->dstadr->bcast.sin_addr.s_addr:
+ 8 : 12);
+ break;
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ ntohs(peer->dstadr->sin.sin_port));
+ break;
+ case CP_LEAP:
+ ctl_putuint(peer_var[CP_LEAP].text, peer->leap);
+ break;
+ case CP_HMODE:
+ ctl_putuint(peer_var[CP_HMODE].text, peer->hmode);
+ break;
+ case CP_STRATUM:
+ ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum);
+ break;
+ case CP_PPOLL:
+ ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll);
+ break;
+ case CP_HPOLL:
+ ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll);
+ break;
+ case CP_PRECISION:
+ ctl_putint(peer_var[CP_PRECISION].text, peer->precision);
+ break;
+ case CP_ROOTDELAY:
+ ctl_putfp(peer_var[CP_ROOTDELAY].text, peer->rootdelay);
+ break;
+ case CP_ROOTDISPERSION:
+ ctl_putufp(peer_var[CP_ROOTDISPERSION].text,
+ peer->rootdispersion);
+ break;
+ case CP_REFID:
+ if (peer->stratum > 1)
+ if (peer->flags & FLAG_REFCLOCK)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->srcadr.sin_addr.s_addr);
+ else
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ break;
+ case CP_REFTIME:
+ ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime);
+ break;
+ case CP_ORG:
+ ctl_putts(peer_var[CP_ORG].text, &peer->org);
+ break;
+ case CP_REC:
+ ctl_putts(peer_var[CP_REC].text, &peer->rec);
+ break;
+ case CP_XMT:
+ ctl_putts(peer_var[CP_XMT].text, &peer->xmt);
+ break;
+ case CP_REACH:
+ ctl_puthex(peer_var[CP_REACH].text, peer->reach);
+ break;
+ case CP_FLASH:
+ ctl_puthex(peer_var[CP_FLASH].text, peer->flash);
+ break;
+ case CP_VALID:
+ ctl_putuint(peer_var[CP_VALID].text, peer->valid);
+ break;
+ case CP_TIMER:
+ ctl_putuint(peer_var[CP_TIMER].text,
+ peer->event_timer.event_time - current_time);
+ break;
+ case CP_DELAY:
+ ctl_putfp(peer_var[CP_DELAY].text, peer->delay);
+ break;
+ case CP_OFFSET:
+ ctl_putlfp(peer_var[CP_OFFSET].text, &peer->offset);
+ break;
+ case CP_DISPERSION:
+ ctl_putufp(peer_var[CP_DISPERSION].text, peer->dispersion);
+ break;
+ case CP_KEYID:
+ ctl_putuint(peer_var[CP_KEYID].text, peer->keyid);
+ break;
+ case CP_FILTDELAY:
+ ctl_putarray(peer_var[CP_FILTDELAY].text,
+ peer->filter_delay, (int)peer->filter_nextpt);
+ break;
+ case CP_FILTOFFSET:
+ ctl_putarray(peer_var[CP_FILTOFFSET].text,
+ peer->filter_soffset, (int)peer->filter_nextpt);
+ break;
+ case CP_FILTERROR:
+ ctl_putarray(peer_var[CP_FILTERROR].text,
+ (s_fp *)peer->filter_error, (int)peer->filter_nextpt);
+ break;
+ case CP_PMODE:
+ ctl_putuint(peer_var[CP_PMODE].text, peer->pmode);
+ break;
+ case CP_RECEIVED:
+ ctl_putuint(peer_var[CP_RECEIVED].text, peer->received);
+ break;
+ case CP_SENT:
+ ctl_putuint(peer_var[CP_SENT].text, peer->sent);
+ break;
+ case CP_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(peer_var[CP_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, peer_var[CP_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = peer_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+static void
+ctl_putclock(varid, clock, mustput)
+ int varid;
+ struct refclockstat *clock;
+ int mustput;
+{
+ switch(varid) {
+ case CC_TYPE:
+ if (mustput || clock->clockdesc == NULL
+ || *(clock->clockdesc) == '\0') {
+ ctl_putuint(clock_var[CC_TYPE].text, clock->type);
+ }
+ break;
+ case CC_TIMECODE:
+ ctl_putstr(clock_var[CC_TIMECODE].text, clock->lastcode,
+ (int)clock->lencode);
+ break;
+ case CC_POLL:
+ ctl_putuint(clock_var[CC_POLL].text, clock->polls);
+ break;
+ case CC_NOREPLY:
+ ctl_putuint(clock_var[CC_NOREPLY].text, clock->noresponse);
+ break;
+ case CC_BADFORMAT:
+ ctl_putuint(clock_var[CC_BADFORMAT].text, clock->badformat);
+ break;
+ case CC_BADDATA:
+ ctl_putuint(clock_var[CC_BADDATA].text, clock->baddata);
+ break;
+ case CC_FUDGETIME1:
+ if (mustput || (clock->haveflags & CLK_HAVETIME1))
+ ctl_putlfp(clock_var[CC_FUDGETIME1].text,
+ &clock->fudgetime1);
+ break;
+ case CC_FUDGETIME2:
+ if (mustput || (clock->haveflags & CLK_HAVETIME2))
+ ctl_putlfp(clock_var[CC_FUDGETIME2].text,
+ &clock->fudgetime2);
+ break;
+ case CC_FUDGEVAL1:
+ if (mustput || (clock->haveflags & CLK_HAVEVAL1))
+ ctl_putint(clock_var[CC_FUDGEVAL1].text,
+ clock->fudgeval1);
+ break;
+ case CC_FUDGEVAL2:
+ if (mustput || (clock->haveflags & CLK_HAVEVAL2))
+ if (clock->fudgeval1 > 1)
+ ctl_putadr(clock_var[CC_FUDGEVAL2].text,
+ clock->fudgeval2);
+ else
+ ctl_putid(clock_var[CC_FUDGEVAL2].text,
+ (char *)&clock->fudgeval2);
+ break;
+ case CC_FLAGS:
+ if (mustput || (clock->haveflags &
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)))
+ ctl_putuint(clock_var[CC_FLAGS].text, clock->flags);
+ break;
+ case CC_DEVICE:
+ if (clock->clockdesc == NULL || *(clock->clockdesc) == '\0') {
+ if (mustput)
+ ctl_putstr(clock_var[CC_DEVICE].text, "", 0);
+ } else {
+ ctl_putstr(clock_var[CC_DEVICE].text, clock->clockdesc,
+ strlen(clock->clockdesc));
+ }
+ break;
+ case CC_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *ss, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(clock_var[CC_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, clock_var[CC_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = clock_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = clock->kv_list; k && !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, i);
+ s += i;
+ *s = '\0';
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+#endif
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+static struct ctl_var *
+ctl_getitem(var_list, data)
+ struct ctl_var *var_list;
+ char **data;
+{
+ register struct ctl_var *v;
+ register char *cp, *tp;
+ static struct ctl_var eol = { 0, EOV, };
+ static char buf[128];
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' || isspace(*reqpt))) {
+ reqpt++;
+ }
+
+ if (reqpt >= reqend)
+ return 0;
+
+ if (var_list == (struct ctl_var *)0)
+ return &eol;
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ v = var_list;
+ cp = reqpt;
+ while (!(v->flags & EOV)) {
+ if (!(v->flags & PADDING) && *cp == *(v->text)) {
+ tp = v->text;
+ while (*tp != '\0' && *tp != '=' && cp < reqend && *cp == *tp) {
+ cp++;
+ tp++;
+ }
+ if ((*tp == '\0') || (*tp == '=')) {
+ while (cp < reqend && isspace(*cp))
+ cp++;
+ if (cp == reqend || *cp == ',') {
+ buf[0] = '\0';
+ *data = buf;
+ if (cp < reqend)
+ cp++;
+ reqpt = cp;
+ return v;
+ }
+ if (*cp == '=') {
+ cp++;
+ tp = buf;
+ while (cp < reqend && isspace(*cp))
+ cp++;
+ while (cp < reqend && *cp != ',')
+ *tp++ = *cp++;
+ if (cp < reqend)
+ cp++;
+ *tp = '\0';
+ while (isspace(*(tp-1)))
+ *(--tp) = '\0';
+ reqpt = cp;
+ *data = buf;
+ return v;
+ }
+ }
+ cp = reqpt;
+ }
+ v++;
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd != 0) {
+ if ((peer = findpeerbyassoc((int)res_associd)) == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else {
+ rpkt.status = htons(ctlsysstatus());
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+static void
+read_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register int i;
+ register struct peer *peer;
+ u_short ass_stat[CTL_MAX_DATA_LEN/sizeof(u_short)];
+
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("read_status: ID %d\n", res_associd);
+#endif
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd == 0) {
+ register int n;
+
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (i = 0; i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ ass_stat[n++] = htons(peer->associd);
+ ass_stat[n++] = htons(ctlpeerstatus(peer));
+ if (n == CTL_MAX_DATA_LEN/sizeof(u_short)) {
+ ctl_putdata((char *)ass_stat,
+ n * sizeof(u_short), 1);
+ n = 0;
+ }
+ }
+ }
+
+ if (n != 0)
+ ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1);
+ ctl_flushpkt(0);
+ } else {
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ } else {
+ register u_char *cp;
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ /*
+ * For now, output everything we know about the peer.
+ * May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ }
+ }
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+static void
+read_variables(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ctl_var *v;
+ register int i;
+ char *valuep;
+ u_char *wants;
+ int gotvar = (CS_MAXCODE>CP_MAXCODE) ? (CS_MAXCODE+1) : (CP_MAXCODE+1);
+
+ if (res_associd == 0) {
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ rpkt.status = htons(ctlsysstatus());
+ if (res_authokay)
+ ctl_sys_num_events = 0;
+ gotvar += count_var(ext_sys_var);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char *)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[CS_MAXCODE+1+v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CS_MAXCODE; i++)
+ if (wants[i])
+ ctl_putsys(i);
+ for (i = 0; ext_sys_var && !(ext_sys_var[i].flags & EOV); i++)
+ if (wants[i+CS_MAXCODE+1])
+ ctl_putdata(ext_sys_var[i].text,
+ strlen(ext_sys_var[i].text), 0);
+ } else {
+ register u_char *cs;
+ register struct ctl_var *kv;
+
+ for (cs = def_sys_var; *cs != 0; cs++)
+ ctl_putsys((int)*cs);
+ for (kv = ext_sys_var; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+ }
+ free((char *)wants);
+ } else {
+ register struct peer *peer;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(peer_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CP_MAXCODE; i++)
+ if (wants[i])
+ ctl_putpeer(i, peer);
+ } else {
+ register u_char *cp;
+
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ }
+ free((char *)wants);
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit writing
+ * this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ctl_var *v;
+ register int ext_var;
+ char *valuep;
+ long val;
+ int leapind, leapwarn;
+
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Set flags to not-in-sync so we can tell when we get something.
+ */
+ leapind = ~0;
+ leapwarn = ~0;
+
+ /*
+ * Look through the variables. Dump out at the first sign of trouble.
+ */
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ ext_var = 0;
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ ext_var = 1;
+ } else {
+ break;
+ }
+ }
+ if (!(v->flags & CAN_WRITE)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ if (!ext_var && (*valuep == '\0' || !atoint(valuep, &val))) {
+ ctl_error(CERR_BADFMT);
+ return;
+ }
+ if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (ext_var) {
+ char *s = emalloc(strlen(v->text)+strlen(valuep)+2);
+ char *t, *tt = s;
+
+ t = v->text;
+ while (*t && *t != '=')
+ *tt++ = *t++;
+
+ *tt++ = '=';
+ strcat(tt, valuep);
+
+ set_sys_var(s, strlen(s)+1, v->flags);
+ free(s);
+ } else {
+ /*
+ * This one seems sane. Save it.
+ */
+ switch(v->code) {
+ case CS_LEAP:
+ case CS_LEAPIND:
+ leapind = val;
+ break;
+ case CS_LEAPWARNING:
+ leapwarn = val;
+ break;
+ default:
+ ctl_error(CERR_UNSPEC); /* our fault, really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it.
+ */
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clock_status - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clock_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ ctl_error(CERR_BADASSOC);
+#else
+ register struct ctl_var *v;
+ register int i;
+ register struct peer *peer;
+ char *valuep;
+ u_char *wants;
+ int gotvar;
+ struct refclockstat clock;
+
+ if (res_associd == 0) {
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search the hash tables
+ * for one.
+ */
+ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) {
+ peer = sys_peer;
+ } else {
+ peer = 0;
+ for (i = 0; peer == 0 && i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ if (peer->flags & FLAG_REFCLOCK)
+ break;
+ }
+ }
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+ } else {
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+
+ /*
+ * If we got here we have a peer which is a clock. Get his status.
+ */
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock);
+
+ /*
+ * Look for variables in the packet.
+ */
+ rpkt.status = htons(ctlclkstatus(&clock));
+ gotvar = CC_MAXCODE+1+count_var(clock.kv_list);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(clock_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(clock.kv_list, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char*)wants);
+ free_varlist(clock.kv_list);
+ return;
+ }
+ wants[CC_MAXCODE+1+v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+
+ if (gotvar) {
+ for (i = 1; i <= CC_MAXCODE; i++)
+ if (wants[i])
+ ctl_putclock(i, &clock, 1);
+ for (i = 0; clock.kv_list && !(clock.kv_list[i].flags & EOV); i++)
+ if (wants[i+CC_MAXCODE+1])
+ ctl_putdata(clock.kv_list[i].text,
+ strlen(clock.kv_list[i].text), 0);
+ } else {
+ register u_char *cc;
+ register struct ctl_var *kv;
+
+ for (cc = def_clock_var; *cc != 0; cc++)
+ ctl_putclock((int)*cc, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+ }
+
+ free((char*)wants);
+ free_varlist(clock.kv_list);
+
+ ctl_flushpkt(0);
+#endif
+}
+
+
+/*
+ * write_clock_status - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clock_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own
+ * trap unless the trap is configured. Note we also
+ * must be aware of the possibility that restriction
+ * flags were changed since this guy last set his trap.
+ * Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(raddr, linter, traptype, version)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+ int traptype;
+ int version;
+{
+ register struct ctl_trap *tp;
+ register struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
+ switch (traptype) {
+ case TRAP_TYPE_CONFIG:
+ tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
+ break;
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return 1; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return 1; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return 1;
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ tptouse = NULL;
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE) &&
+ !(tp->tr_flags & TRAP_CONFIGURED) &&
+ ((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ }
+
+ if (!(tp->tr_flags & TRAP_INUSE)) {
+ tptouse = tp;
+ } else if (!(tp->tr_flags & TRAP_CONFIGURED)) {
+ switch (traptype) {
+ case TRAP_TYPE_CONFIG:
+ if (tptouse == NULL) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_flags & TRAP_NONPRIO
+ && !(tp->tr_flags & TRAP_NONPRIO))
+ break;
+ if (!(tptouse->tr_flags & TRAP_NONPRIO)
+ && tp->tr_flags & TRAP_NONPRIO) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_origtime < tp->tr_origtime)
+ tptouse = tp;
+ break;
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_NONPRIO) {
+ if (tptouse == NULL ||
+ (tptouse->tr_flags & TRAP_INUSE
+ && tptouse->tr_origtime
+ < tp->tr_origtime))
+ tptouse = tp;
+ }
+ break;
+ case TRAP_TYPE_NONPRIO:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return 0;
+
+ /*
+ * Set up this structure for him.
+ */
+ tptouse->tr_settime = tptouse->tr_origtime = current_time;
+ tptouse->tr_count = tptouse->tr_resets = 0;
+ tptouse->tr_sequence = 1;
+ tptouse->tr_addr = *raddr;
+ tptouse->tr_localaddr = linter;
+ tptouse->tr_version = version;
+
+ tptouse->tr_flags = TRAP_INUSE;
+ if (traptype == TRAP_TYPE_CONFIG)
+ tptouse->tr_flags |= TRAP_CONFIGURED;
+ else if (traptype == TRAP_TYPE_NONPRIO)
+ tptouse->tr_flags |= TRAP_NONPRIO;
+ num_ctl_traps++;
+ return 1;
+}
+
+
+/*
+ * ctlclrtrap - called to clr a trap
+ */
+int
+ctlclrtrap(raddr, linter, traptype)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+ int traptype;
+{
+ register struct ctl_trap *tp;
+
+ if ((tp = ctlfindtrap(raddr, linter)) == NULL)
+ return 0;
+
+ if (tp->tr_flags & TRAP_CONFIGURED
+ && traptype != TRAP_TYPE_CONFIG)
+ return 0;
+
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ return 1;
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(raddr, linter)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+{
+ register struct ctl_trap *tp;
+
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if (tp->tr_flags & TRAP_INUSE
+ && NSRCADR(raddr) == NSRCADR(&tp->tr_addr)
+ && NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr)
+ && linter == tp->tr_localaddr)
+ return tp;
+ }
+ return (struct ctl_trap *)NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(err, peer)
+ int err;
+ struct peer *peer;
+{
+ register int i;
+
+ /*
+ * Record error code in proper spots, but have mercy on the
+ * log file.
+ */
+ if (!(err & PEER_EVENT)) {
+ if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
+ ctl_sys_num_events++;
+ if (ctl_sys_last_event != (u_char)err) {
+ syslog(LOG_INFO, "system event %x status %x",
+ err, ctlsysstatus());
+#ifdef DEBUG
+ if (debug)
+ printf("report_event: system event %x status %x\n",
+ err, ctlsysstatus());
+#endif
+ ctl_sys_last_event = (u_char)err;
+ }
+ } else if (peer != 0) {
+ peer->last_event = (u_char)(err & ~PEER_EVENT);
+ if (peer->num_events < CTL_PEER_MAXEVENTS)
+ peer->num_events++;
+ syslog(LOG_INFO, "peer %s event %x status %x",
+ ntoa(&peer->srcadr), err, ctlpeerstatus(peer));
+#ifdef DEBUG
+ if (debug)
+ printf("peer %s event %x status %x\n",
+ ntoa(&peer->srcadr), err, ctlpeerstatus(peer));
+#endif
+ } else {
+ syslog(LOG_ERR, "report_event: err %x, no peer", err);
+#ifdef DEBUG
+ printf("report_event: err %x, no peer\n", err);
+#endif
+ return;
+ }
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ res_opcode = CTL_OP_ASYNCMSG;
+ res_offset = 0;
+ res_async = 1;
+ res_authenticate = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ if (!(err & PEER_EVENT)) {
+ rpkt.associd = 0;
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * For now, put everything we know about system
+ * variables. Maybe more selective later
+ */
+ for (i = 1; i <= CS_MAXCODE; i++)
+ ctl_putsys(i);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_CLOCKEXCPT) {
+ struct refclockstat clock;
+ struct ctl_var *kv;
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock);
+ ctl_puthex("refclockstatus", ctlclkstatus(&clock));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+
+ free_varlist(clock.kv_list);
+ }
+#endif /*REFCLOCK*/
+ } else {
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /*
+ * Dump it all. Later, maybe less.
+ */
+ for (i = 1; i <= CP_MAXCODE; i++)
+ ctl_putpeer(i, peer);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_PEERCLOCK) {
+ struct refclockstat clock;
+ struct ctl_var *kv;
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0,
+ &clock);
+
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+
+ free_varlist(clock.kv_list);
+ }
+#endif /*REFCLOCK*/
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+void
+ctl_clr_stats()
+{
+ ctltimereset = current_time;
+ numctlreq = 0;
+ numctlbadpkts = 0;
+ numctlresponses = 0;
+ numctlfrags = 0;
+ numctlerrors = 0;
+ numctlfrags = 0;
+ numctltooshort = 0;
+ numctlinputresp = 0;
+ numctlinputfrag = 0;
+ numctlinputerr = 0;
+ numctlbadoffset = 0;
+ numctlbadversion = 0;
+ numctldatatooshort = 0;
+ numctlbadop = 0;
+ numasyncmsgs = 0;
+}
+
+static u_long
+count_var(k)
+ struct ctl_var *k;
+{
+ register u_long c;
+
+ c = 0;
+ while (k && !(k++->flags & EOV))
+ c++;
+
+ return c;
+}
+
+char *
+add_var(kv, size, def)
+ struct ctl_var **kv;
+ u_long size;
+ int def;
+{
+ register u_long c;
+ register struct ctl_var *k;
+
+ c = count_var(*kv);
+
+ k = *kv;
+ *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var));
+ if (k)
+ {
+ memmove((char *)*kv, (char *)k, sizeof(struct ctl_var)*c);
+ free((char *)k);
+ }
+
+ (*kv)[c].code = c;
+ (*kv)[c].text = (char *)emalloc(size);
+ (*kv)[c].flags = def;
+ (*kv)[c+1].code = 0;
+ (*kv)[c+1].text = (char *)0;
+ (*kv)[c+1].flags = EOV;
+ return (*kv)[c].text;
+}
+
+void
+set_var(kv, data, size, def)
+ struct ctl_var **kv;
+ char *data;
+ u_long size;
+ int def;
+{
+ register struct ctl_var *k;
+ register char *s, *t;
+
+ if (!data || !size)
+ return;
+
+ if ((k = *kv))
+ {
+ while (!(k->flags & EOV))
+ {
+ s = data;
+ t = k->text;
+ if (t)
+ {
+ while (*t != '=' && *s - *t == 0)
+ {
+ s++;
+ t++;
+ }
+ if (*s == *t && ((*t == '=') || !*t))
+ {
+ free(k->text);
+ k->text = (char *)emalloc(size);
+ memmove(k->text, data, size);
+ k->flags = def;
+ return;
+ }
+ }
+ else
+ {
+ k->text = (char *)emalloc(size);
+ memmove(k->text, data, size);
+ k->flags = def;
+ return;
+ }
+ k++;
+ }
+ }
+ t = add_var(kv, size, def);
+ memmove(t, data, size);
+}
+
+void
+set_sys_var(data, size, def)
+ char *data;
+ u_long size;
+ int def;
+{
+ set_var(&ext_sys_var, data, size, def);
+}
+
+void
+free_varlist(kv)
+ struct ctl_var *kv;
+{
+ struct ctl_var *k;
+ if (kv)
+ {
+ for (k = kv; !(k->flags & EOV); k++)
+ free(k->text);
+ free((char *)kv);
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_filegen.c b/usr.sbin/xntpd/xntpd/ntp_filegen.c
new file mode 100644
index 0000000..cc4882b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_filegen.c
@@ -0,0 +1,538 @@
+/*
+ * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
+ *
+ * implements file generations support for NTP
+ * logfiles and statistic files
+ *
+ *
+ * Copyright (c) 1992
+ * Rainer Pruy Friedrich-Alexander Unuiversitaet Erlangen-Nuernberg
+ *
+ * This code may be modified and used freely
+ * provided credits remain intact.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_string.h"
+#include "ntp_calendar.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * NTP is intended to run long periods of time without restart.
+ * Thus log and statistic files generated by NTP will grow large.
+ *
+ * this set of routines provides a central interface
+ * to generating files using file generations
+ *
+ * the generation of a file is changed according to file generation type
+ */
+
+
+/*
+ * to check reason on open failure
+ */
+extern int errno;
+
+/*
+ * imported from timer
+ */
+extern u_long current_time;
+
+/*
+ * redefine this if your system dislikes filename suffixes like
+ * X.19910101 or X.1992W50 or ....
+ */
+#define SUFFIX_SEP '.'
+
+/*
+ * other constants
+ */
+#define FGEN_AGE_SECS (24*60*60) /* life time of FILEGEN_AGE in seconds */
+
+#ifdef DEBUG
+extern int debug;
+#endif
+
+static void filegen_open P((FILEGEN *, u_long));
+static int valid_fileref P((char *, char *));
+#ifdef UNUSED
+static FILEGEN *filegen_unregister P((char *));
+#endif /* UNUSED */
+
+/*
+ * open a file generation according to the current settings of gen
+ * will also provide a link to basename if requested to do so
+ */
+
+static void
+filegen_open(gen, newid)
+ FILEGEN *gen;
+ u_long newid;
+{
+ char *filename;
+ char *basename;
+ u_int len;
+ FILE *fp;
+ struct calendar cal;
+
+ len = strlen(gen->prefix) + strlen(gen->basename) + 1;
+ basename = emalloc(len);
+ sprintf(basename, "%s%s", gen->prefix, gen->basename);
+
+ switch(gen->type) {
+ default:
+ syslog(LOG_ERR, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
+ gen->type, basename);
+ gen->type = FILEGEN_NONE;
+
+ /*FALLTHROUGH*/
+ case FILEGEN_NONE:
+ filename = emalloc(len);
+ sprintf(filename,"%s", basename);
+ break;
+
+ case FILEGEN_PID:
+ filename = emalloc(len + 1 + 1 + 10);
+ sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid);
+ break;
+
+ case FILEGEN_DAY:
+ /* You can argue here in favor of using MJD, but
+ * I would assume it to be easier for humans to interpret dates
+ * in a format they are used to in everyday life.
+ */
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 2 + 2);
+ sprintf(filename, "%s%c%04d%02d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month, cal.monthday);
+ break;
+
+ case FILEGEN_WEEK:
+ /*
+ * This is still a hack
+ * - the term week is not correlated to week as it is used
+ * normally - it just refers to a period of 7 days
+ * starting at Jan 1 - 'weeks' are counted starting from zero
+ */
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 1 + 2);
+ sprintf(filename, "%s%c%04dw%02d",
+ basename, SUFFIX_SEP, cal.year, cal.yearday / 7);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 2);
+ sprintf(filename, "%s%c%04d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month);
+ break;
+
+ case FILEGEN_YEAR:
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4);
+ sprintf(filename, "%s%c%04d", basename, SUFFIX_SEP, cal.year);
+ break;
+
+ case FILEGEN_AGE:
+ filename = emalloc(len + 1 + 2 + 10);
+ sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid);
+ break;
+ }
+
+ if (gen->type != FILEGEN_NONE) {
+ /*
+ * check for existence of a file with name 'basename'
+ * as we disallow such a file
+ * if FGEN_FLAG_LINK is set create a link
+ */
+ struct stat stats;
+ /*
+ * try to resolve name collisions
+ */
+ static u_long conflicts = 0;
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
+#endif
+ if (stat(basename, &stats) == 0) {
+ /* Hm, file exists... */
+ if (S_ISREG(stats.st_mode)) {
+ if (stats.st_nlink <= 1) {
+ /*
+ * Oh, it is not linked - try to save it
+ */
+ char *savename = emalloc(len + 1 + 1 + 10 + 10);
+ sprintf(savename, "%s%c%dC%lu",
+ basename, SUFFIX_SEP, getpid(),
+ (u_long)conflicts++);
+ if (rename(basename, savename) != 0)
+ syslog(LOG_ERR," couldn't save %s: %m", basename);
+ free(savename);
+ } else {
+ /*
+ * there is at least a second link tpo this file
+ * just remove the conflicting one
+ */
+ if (unlink(basename) != 0)
+ syslog(LOG_ERR, "couldn't unlink %s: %m", basename);
+ }
+ } else {
+ /*
+ * Ehh? Not a regular file ?? strange !!!!
+ */
+ syslog(LOG_ERR, "expected regular file for %s (found mode 0%o)",
+ basename, stats.st_mode);
+ }
+ } else {
+ /*
+ * stat(..) failed, but it is absolutely correct for
+ * 'basename' not to exist
+ */
+ if (errno != ENOENT)
+ syslog(LOG_ERR,"stat(%s) failed: %m", basename);
+ }
+ }
+
+ /*
+ * now, try to open new file generation...
+ */
+ fp = fopen(filename, "a");
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
+ gen->type, (u_long)newid, filename);
+#endif
+
+ if (fp == NULL) {
+ /* open failed -- keep previous state
+ *
+ * If the file was open before keep the previous generation.
+ * This will cause output to end up in the 'wrong' file,
+ * but I think this is still better than loosing output
+ *
+ * ignore errors due to missing directories
+ */
+
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "can't open %s: %m", filename);
+ } else {
+ if (gen->fp != NULL) {
+ fclose(gen->fp);
+ }
+ gen->fp = fp;
+ gen->id = newid;
+
+ if (gen->flag & FGEN_FLAG_LINK) {
+ /*
+ * need to link file to basename
+ * have to use hardlink for now as I want to allow
+ * gen->basename spanning directory levels
+ * this would make it more complex to get the correct filename
+ * for symlink
+ *
+ * Ok, it would just mean taking the part following the last '/'
+ * in the name.... Should add it later....
+ */
+
+ if (link(filename, basename) != 0) {
+ if (errno != EEXIST)
+ syslog(LOG_ERR, "can't link(%s, %s): %m", filename, basename);
+ }
+
+ } /*flags & FGEN_FLAG_LINK*/
+ } /*else fp == NULL*/
+
+ free(basename);
+ free(filename);
+ return;
+}
+
+/*
+ * this function sets up gen->fp to point to the correct
+ * generation of the file for the time specified by 'now'
+ *
+ * 'now' usually is interpreted as second part of a l_fp as is in the cal...
+ * library routines
+ */
+
+void
+filegen_setup(gen,now)
+ FILEGEN *gen;
+ u_long now;
+{
+ u_long new_gen = ~0;
+ struct calendar cal;
+
+ if (!(gen->flag & FGEN_FLAG_ENABLED)) {
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+ return;
+ }
+
+ switch (gen->type) {
+ case FILEGEN_NONE:
+ if (gen->fp != NULL) return; /* file already open */
+ break;
+
+ case FILEGEN_PID:
+ new_gen = getpid();
+ break;
+
+ case FILEGEN_DAY:
+ caljulian(now, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_WEEK:
+ /* Would be nice to have a calweekstart() routine */
+ /* so just use a hack ... */
+ /* just round time to integral 7 days period for actual year */
+ new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
+ + 60;
+ /*
+ * just to be sure -
+ * the computation above would fail in the presence of leap seconds
+ * so at least carry the date to the next day (+60 (seconds))
+ * and go back to the start of the day via calendar computations
+ */
+ caljulian(new_gen, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(now, &cal);
+ cal.yearday -= cal.monthday - 1;
+ cal.monthday = 1;
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_YEAR:
+ new_gen = calyearstart(now);
+ break;
+
+ case FILEGEN_AGE:
+ new_gen = current_time - (current_time % FGEN_AGE_SECS);
+ break;
+ }
+ /*
+ * try to open file if not yet open
+ * reopen new file generation file on change of generation id
+ */
+ if (gen->fp == NULL || gen->id != new_gen) {
+ filegen_open(gen, new_gen);
+ }
+}
+
+
+/*
+ * change settings for filegen files
+ */
+void
+filegen_config(gen,basename,type,flag)
+ FILEGEN *gen;
+ char *basename;
+ u_int type;
+ u_int flag;
+{
+ /*
+ * if nothing would be changed...
+ */
+ if ((basename == gen->basename || strcmp(basename,gen->basename) == 0) &&
+ type == gen->type &&
+ flag == gen->flag)
+ return;
+
+ /*
+ * validate parameters
+ */
+ if (!valid_fileref(gen->prefix,basename))
+ return;
+
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
+ gen->prefix, gen->basename, basename, gen->type, type, gen->flag, flag);
+#endif
+ if (gen->basename != basename || strcmp(gen->basename, basename) != 0) {
+ free(gen->basename);
+ gen->basename = emalloc(strlen(basename) + 1);
+ strcpy(gen->basename, basename);
+ }
+ gen->type = type;
+ gen->flag = flag;
+
+ /*
+ * make filegen use the new settings
+ * special action is only required when a generation file
+ * is currently open
+ * otherwise the new settings will be used anyway at the next open
+ */
+ if (gen->fp != NULL) {
+ l_fp now;
+
+ gettstamp(&now);
+ filegen_setup(gen, now.l_ui);
+ }
+}
+
+
+/*
+ * check whether concatenating prefix and basename
+ * yields a legal filename
+ */
+static int
+valid_fileref(prefix,basename)
+ char *prefix, *basename;
+{
+ /*
+ * prefix cannot be changed dynamically
+ * (within the context of filegen)
+ * so just reject basenames containing '..'
+ *
+ * ASSUMPTION:
+ * file system parts 'below' prefix may be
+ * specified without infringement of security
+ *
+ * restricing prefix to legal values
+ * has to be ensured by other means
+ * (however, it would be possible to perform some checks here...)
+ */
+ register char *p = basename;
+
+ /*
+ * Just to catch, dumb errors opening up the world...
+ */
+ if (prefix == NULL || *prefix == '\0')
+ return 0;
+
+ if (basename == NULL)
+ return 0;
+
+ for (p = basename; p; p = strchr(p, '/')) {
+ if (*p == '.' && *(p+1) == '.' && (*(p+2) == '\0' || *(p+2) == '/'))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * filegen registry
+ */
+
+
+static struct filegen_entry {
+ char *name;
+ FILEGEN *filegen;
+ struct filegen_entry *next;
+} *filegen_registry = NULL;
+
+
+FILEGEN *
+filegen_get(name)
+ char *name;
+{
+ struct filegen_entry *f = filegen_registry;
+
+ while(f) {
+ if (f->name == name || strcmp(name, f->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = %x\n", name,
+ (u_int)f->filegen);
+#endif
+ return f->filegen;
+ }
+ f = f->next;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = NULL\n", name);
+#endif
+ return NULL;
+}
+
+void
+filegen_register(name, filegen)
+ char *name;
+ FILEGEN *filegen;
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_register(\"%s\",%x)\n", name, (u_int)filegen);
+#endif
+ while (*f) {
+ if ((*f)->name == name || strcmp(name, (*f)->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 4) {
+ printf("replacing filegen %x\n", (u_int)(*f)->filegen);
+ }
+#endif
+ (*f)->filegen = filegen;
+ return;
+ }
+ f = &((*f)->next);
+ }
+
+ *f = (struct filegen_entry *) emalloc(sizeof(struct filegen_entry));
+ if (*f) {
+ (*f)->next = NULL;
+ (*f)->name = emalloc(strlen(name) + 1);
+ strcpy((*f)->name, name);
+ (*f)->filegen = filegen;
+#ifdef DEBUG
+ if (debug > 5) {
+ printf("adding new filegen\n");
+ }
+#endif
+ }
+
+ return;
+}
+
+#ifdef UNUSED
+static FILEGEN *
+filegen_unregister(name)
+ char *name;
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_unregister(\"%s\")\n", name);
+#endif
+
+ while (*f) {
+ if (strcmp((*f)->name,name) == 0) {
+ struct filegen_entry *ff = *f;
+ FILEGEN *fg;
+
+ *f = (*f)->next;
+ fg = ff->filegen;
+ free(ff->name);
+ free(ff);
+ return fg;
+ }
+ f = &((*f)->next);
+ }
+ return NULL;
+}
+#endif /* UNUSED */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_intres.c b/usr.sbin/xntpd/xntpd/ntp_intres.c
new file mode 100644
index 0000000..62d3792
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_intres.c
@@ -0,0 +1,799 @@
+/*
+ * ripped off from ../xnptres/xntpres.c by Greg Troxel 4/2/92
+ * routine callable from xntpd, rather than separate program
+ * also, key info passed in via a global, so no key file needed.
+ */
+
+/*
+ * xntpres - process configuration entries which require use of the resolver
+ *
+ * This is meant to be run by xntpd on the fly. It is not guaranteed
+ * to work properly if run by hand. This is actually a quick hack to
+ * stave off violence from people who hate using numbers in the
+ * configuration file (at least I hope the rest of the daemon is
+ * better than this). Also might provide some ideas about how one
+ * might go about autoconfiguring an NTP distribution network.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Each item we are to resolve and configure gets one of these
+ * structures defined for it.
+ */
+struct conf_entry {
+ struct conf_entry *ce_next;
+ char *ce_name; /* name we are trying to resolve */
+ struct conf_peer ce_config; /* configuration info for peer */
+};
+#define ce_peeraddr ce_config.peeraddr
+#define ce_hmode ce_config.hmode
+#define ce_version ce_config.version
+#define ce_minpoll ce_config.minpoll
+#define ce_maxpoll ce_config.maxpoll
+#define ce_flags ce_config.flags
+#define ce_ttl ce_config.ttl
+#define ce_keyid ce_config.keyid
+
+/*
+ * confentries is a pointer to the list of configuration entries
+ * we have left to do.
+ */
+struct conf_entry *confentries = NULL;
+
+/*
+ * We take an interrupt every thirty seconds, at which time we decrement
+ * config_timer and resolve_timer. The former is set to 2, so we retry
+ * unsucessful reconfigurations every minute. The latter is set to
+ * an exponentially increasing value which starts at 2 and increases to
+ * 32. When this expires we retry failed name resolutions.
+ *
+ * We sleep SLEEPTIME seconds before doing anything, to give the server
+ * time to arrange itself.
+ */
+#define MINRESOLVE 2
+#define MAXRESOLVE 32
+#define CONFIG_TIME 2
+#define ALARM_TIME 30
+
+#define SLEEPTIME 2
+
+static int config_timer = 0;
+static int resolve_timer = 0;
+
+static int resolve_value; /* next value of resolve timer */
+
+/*
+ * Big hack attack
+ */
+#define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
+#define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
+
+/*
+ * Select time out. Set to 2 seconds. The server is on the local machine,
+ * after all.
+ */
+#define TIMEOUT_SEC 2
+#define TIMEOUT_USEC 0
+
+
+/*
+ * Input processing. The data on each line in the configuration file
+ * is supposed to consist of entries in the following order
+ */
+#define TOK_HOSTNAME 0
+#define TOK_HMODE 1
+#define TOK_VERSION 2
+#define TOK_MINPOLL 3
+#define TOK_MAXPOLL 4
+#define TOK_FLAGS 5
+#define TOK_TTL 6
+#define TOK_KEYID 7
+#define NUMTOK 8
+
+#define MAXLINESIZE 512
+
+
+/*
+ * File descriptor for ntp request code.
+ */
+static int sockfd = -1;
+
+
+/* stuff to be filled in by caller */
+
+u_long req_keyid; /* request keyid */
+char *req_file; /* name of the file with configuration info */
+
+/* end stuff to be filled in */
+
+
+extern int debug; /* use global debug flag */
+extern int errno;
+
+static RETSIGTYPE bong P((int));
+static void checkparent P((void));
+static void removeentry P((struct conf_entry *));
+static void addentry P((char *, int, int, int, int, int, int, u_long));
+static int findhostaddr P((struct conf_entry *));
+static void openntp P((void));
+static int request P((struct conf_peer *));
+static char * nexttoken P((char **));
+static void readconf P((FILE *, char *));
+static void doconfigure P((int));
+
+/*
+ * assumes: req_key, req_keyid, conffile valid
+ * syslog still open
+ */
+void
+ntp_intres()
+{
+ FILE *in;
+
+#ifdef DEBUG
+ if ( debug )
+ syslog(LOG_INFO, "ntp_intres running");
+#endif
+
+ /* check out auth stuff */
+ if (!authhavekey(req_keyid)) {
+ syslog(LOG_ERR, "request keyid %lu not found",
+ req_keyid );
+ exit(1);
+ }
+
+ /*
+ * Read the configuration info
+ * {this is bogus, since we are forked, but it is easier
+ * to keep this code - gdt}
+ */
+ if ((in = fopen(req_file, "r")) == NULL) {
+ syslog(LOG_ERR, "can't open configuration file %s: %m",
+ req_file);
+ exit(1);
+ }
+ readconf(in, req_file);
+ (void) fclose(in);
+
+ if (!debug )
+ (void) unlink(req_file);
+
+ /*
+ * Sleep a little to make sure the server is completely up
+ */
+ sleep(SLEEPTIME);
+
+ /*
+ * Make a first cut at resolving the bunch
+ */
+ doconfigure(1);
+ if (confentries == NULL)
+ exit(0); /* done that quick */
+
+ /*
+ * Here we've got some problem children. Set up the timer
+ * and wait for it.
+ */
+ resolve_value = resolve_timer = MINRESOLVE;
+ config_timer = CONFIG_TIME;
+ (void) signal_no_reset(SIGALRM, bong);
+ alarm(ALARM_TIME);
+
+ for (;;) {
+ if (confentries == NULL)
+ exit(0);
+ checkparent();
+ if (resolve_timer == 0) {
+ if (resolve_value < MAXRESOLVE)
+ resolve_value <<= 1;
+ resolve_timer = resolve_value;
+ config_timer = CONFIG_TIME;
+ doconfigure(1);
+ continue;
+ } else if (config_timer == 0) {
+ config_timer = CONFIG_TIME;
+ doconfigure(0);
+ continue;
+ }
+ /*
+ * There is a race in here. Is okay, though, since
+ * all it does is delay things by 30 seconds.
+ */
+ (void) pause();
+ }
+}
+
+
+/*
+ * bong - service and reschedule an alarm() interrupt
+ */
+static RETSIGTYPE
+bong(sig)
+int sig;
+{
+ if (config_timer > 0)
+ config_timer--;
+ if (resolve_timer > 0)
+ resolve_timer--;
+ alarm(ALARM_TIME);
+}
+
+
+/*
+ * checkparent - see if our parent process is still running
+ */
+static void
+checkparent()
+{
+ /*
+ * If our parent (the server) has died we will have been
+ * inherited by init. If so, exit.
+ */
+ if (getppid() == 1) {
+ syslog(LOG_INFO, "parent died before we finished, exiting");
+ exit(0);
+ }
+}
+
+
+/*
+ * removeentry - we are done with an entry, remove it from the list
+ */
+static void
+removeentry(entry)
+ struct conf_entry *entry;
+{
+ register struct conf_entry *ce;
+
+ ce = confentries;
+ if (ce == entry) {
+ confentries = ce->ce_next;
+ return;
+ }
+
+ while (ce != NULL) {
+ if (ce->ce_next == entry) {
+ ce->ce_next = entry->ce_next;
+ return;
+ }
+ ce = ce->ce_next;
+ }
+}
+
+
+/*
+ * addentry - add an entry to the configuration list
+ */
+static void
+addentry(name, mode, version, minpoll, maxpoll, flags, ttl, keyid)
+ char *name;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long keyid;
+{
+ register char *cp;
+ register struct conf_entry *ce;
+ int len;
+
+ len = strlen(name) + 1;
+ cp = emalloc((unsigned)len);
+ memmove(cp, name, len);
+
+ ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
+ ce->ce_name = cp;
+ ce->ce_peeraddr = 0;
+ ce->ce_hmode = (u_char)mode;
+ ce->ce_version = (u_char)version;
+ ce->ce_minpoll = (u_char)minpoll;
+ ce->ce_maxpoll = (u_char)maxpoll;
+ ce->ce_flags = (u_char)flags;
+ ce->ce_ttl = (u_char)ttl;
+ ce->ce_keyid = keyid;
+ ce->ce_next = NULL;
+
+ if (confentries == NULL) {
+ confentries = ce;
+ } else {
+ register struct conf_entry *cep;
+
+ for (cep = confentries; cep->ce_next != NULL;
+ cep = cep->ce_next)
+ /* nothing */;
+ cep->ce_next = ce;
+ }
+}
+
+
+/*
+ * findhostaddr - resolve a host name into an address
+ *
+ * The routine sticks the address into the entry's ce_peeraddr if it
+ * gets one. It returns 1 for "success" and 0 for an uncorrectable
+ * failure. Note that "success" includes try again errors. You can
+ * tell that you got a try again since ce_peeraddr will still be zero.
+ */
+static int
+findhostaddr(entry)
+ struct conf_entry *entry;
+{
+ struct hostent *hp;
+
+ checkparent(); /* make sure our guy is still running */
+
+ hp = gethostbyname(entry->ce_name);
+
+ if (hp == NULL) {
+#ifndef NODNS
+ /*
+ * If the resolver is in use, see if the failure is
+ * temporary. If so, return success.
+ */
+ extern int h_errno;
+
+ if (h_errno == TRY_AGAIN)
+ return (1);
+#endif
+ return (0);
+ }
+
+ /*
+ * Use the first address. We don't have any way to
+ * tell preferences and older gethostbyname() implementations
+ * only return one.
+ */
+ memmove((char *)&(entry->ce_peeraddr),
+ (char *)hp->h_addr,
+ sizeof(struct in_addr));
+ return (1);
+}
+
+
+/*
+ * openntp - open a socket to the ntp server
+ */
+static void
+openntp()
+{
+ struct sockaddr_in saddr;
+
+ if (sockfd >= 0)
+ return;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ }
+
+ memset((char *)&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(NTP_PORT); /* trash */
+ saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */
+
+ /*
+ * Make the socket non-blocking. We'll wait with select()
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
+ syslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
+ exit(1);
+ }
+#else
+#if defined(FNDELAY)
+ if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
+ syslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
+ exit(1);
+ }
+#else
+NEED NON BLOCKING IO
+#endif
+#endif
+
+
+ if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
+ syslog(LOG_ERR, "connect() failed: %m");
+ exit(1);
+ }
+}
+
+
+/*
+ * request - send a configuration request to the server, wait for a response
+ */
+static int
+request(conf)
+ struct conf_peer *conf;
+{
+ fd_set fdset;
+ struct timeval tvout;
+ struct req_pkt reqpkt;
+ l_fp ts;
+ int n;
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (sockfd < 0)
+ openntp();
+
+ /*
+ * Try to clear out any previously received traffic so it
+ * doesn't fool us. Note the socket is nonblocking.
+ */
+ tvout.tv_sec = 0;
+ tvout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
+ 0) {
+ read(sockfd, (char *)&reqpkt, REQ_LEN_MAC);
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ }
+
+ /*
+ * Make up a request packet with the configuration info
+ */
+ memset((char *)&reqpkt, 0, sizeof(reqpkt));
+
+ reqpkt.rm_vn_mode = RM_VN_MODE(0, 0);
+ reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
+ reqpkt.implementation = IMPL_XNTPD; /* local implementation */
+ reqpkt.request = REQ_CONFIG; /* configure a new peer */
+ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
+ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
+ memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
+ reqpkt.keyid = htonl(req_keyid);
+
+ auth1crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC);
+ gettstamp(&ts);
+ L_ADDUF(&ts, SKEWTIME);
+ HTONL_FP(&ts, &reqpkt.tstamp);
+ n = auth2crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC);
+
+ /*
+ * Done. Send it.
+ */
+ n = write(sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n);
+ if (n < 0) {
+ syslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0; /* maybe should exit */
+ }
+
+ /*
+ * Wait for a response. A weakness of the mode 7 protocol used
+ * is that there is no way to associate a response with a
+ * particular request, i.e. the response to this configuration
+ * request is indistinguishable from that to any other. I should
+ * fix this some day. In any event, the time out is fairly
+ * pessimistic to make sure that if an answer is coming back
+ * at all, we get it.
+ */
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ tvout.tv_sec = TIMEOUT_SEC;
+ tvout.tv_usec = TIMEOUT_USEC;
+
+ n = select(sockfd + 1, &fdset, (fd_set *)0,
+ (fd_set *)0, &tvout);
+
+ if (n <= 0) {
+ if (n < 0)
+ syslog(LOG_ERR, "select() fails: %m");
+ return 0;
+ }
+
+ n = read(sockfd, (char *)&reqpkt, REQ_LEN_MAC);
+ if (n <= 0) {
+ if (n < 0) {
+ syslog(LOG_ERR, "read() fails: %m");
+ return 0;
+ }
+ continue;
+ }
+
+ /*
+ * Got one. Check through to make sure it is what
+ * we expect.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ syslog(LOG_ERR, "received runt response (%d octets)",
+ n);
+ continue;
+ }
+
+ if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("received non-response packet\n");
+#endif
+ continue;
+ }
+
+ if (ISMORE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("received fragmented packet\n");
+#endif
+ continue;
+ }
+
+ if (INFO_VERSION(reqpkt.rm_vn_mode) != NTP_VERSION
+ || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("version (%d) or mode (%d) incorrect\n",
+ INFO_VERSION(reqpkt.rm_vn_mode),
+ INFO_MODE(reqpkt.rm_vn_mode));
+#endif
+ continue;
+ }
+
+ if (INFO_SEQ(reqpkt.auth_seq) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("nonzero sequence number (%d)\n",
+ INFO_SEQ(reqpkt.auth_seq));
+#endif
+ continue;
+ }
+
+ if (reqpkt.implementation != IMPL_XNTPD ||
+ reqpkt.request != REQ_CONFIG) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "implementation (%d) or request (%d) incorrect\n",
+ reqpkt.implementation, reqpkt.request);
+#endif
+ continue;
+ }
+
+ if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
+ INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize != 0)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "nitems (%d) mbz (%d) or itemsize (%d) nonzero\n",
+ INFO_NITEMS(reqpkt.err_nitems),
+ INFO_MBZ(reqpkt.mbz_itemsize),
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize));
+#endif
+ continue;
+ }
+
+ n = INFO_ERR(reqpkt.err_nitems);
+ switch (n) {
+ case INFO_OKAY:
+ /* success */
+ return 1;
+
+ case INFO_ERR_IMPL:
+ syslog(LOG_ERR,
+ "server reports implementation mismatch!!");
+ return 0;
+
+ case INFO_ERR_REQ:
+ syslog(LOG_ERR,
+ "server claims configuration request is unknown");
+ return 0;
+
+ case INFO_ERR_FMT:
+ syslog(LOG_ERR,
+ "server indicates a format error occured(!!)");
+ return 0;
+
+ case INFO_ERR_NODATA:
+ syslog(LOG_ERR,
+ "server indicates no data available (shouldn't happen)");
+ return 0;
+
+ case INFO_ERR_AUTH:
+ syslog(LOG_ERR,
+ "server returns a permission denied error");
+ return 0;
+
+ default:
+ syslog(LOG_ERR,
+ "server returns unknown error code %d", n);
+ return 0;
+ }
+ }
+}
+
+
+/*
+ * nexttoken - return the next token from a line
+ */
+static char *
+nexttoken(lptr)
+ char **lptr;
+{
+ register char *cp;
+ register char *tstart;
+
+ cp = *lptr;
+
+ /*
+ * Skip leading white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * If this is the end of the line, return nothing.
+ */
+ if (*cp == '\n' || *cp == '\0') {
+ *lptr = cp;
+ return NULL;
+ }
+
+ /*
+ * Must be the start of a token. Record the pointer and look
+ * for the end.
+ */
+ tstart = cp++;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
+ cp++;
+
+ /*
+ * Terminate the token with a \0. If this isn't the end of the
+ * line, space to the next character.
+ */
+ if (*cp == '\n' || *cp == '\0')
+ *cp = '\0';
+ else
+ *cp++ = '\0';
+
+ *lptr = cp;
+ return tstart;
+}
+
+
+/*
+ * readconf - read the configuration information out of the file we
+ * were passed. Note that since the file is supposed to be
+ * machine generated, we bail out at the first sign of trouble.
+ */
+static void
+readconf(fp, name)
+ FILE *fp;
+ char *name;
+{
+ register int i;
+ char *token[NUMTOK];
+ u_long intval[NUMTOK];
+ int flags;
+ char buf[MAXLINESIZE];
+ char *bp;
+
+ while (fgets(buf, MAXLINESIZE, fp) != NULL) {
+
+ bp = buf;
+ for (i = 0; i < NUMTOK; i++) {
+ if ((token[i] = nexttoken(&bp)) == NULL) {
+ syslog(LOG_ERR,
+ "tokenizing error in file `%s', quitting",
+ name);
+ exit(1);
+ }
+ }
+
+ for (i = 1; i < NUMTOK; i++) {
+ if (!atouint(token[i], &intval[i])) {
+ syslog(LOG_ERR,
+ "format error for integer token `%s', file `%s', quitting",
+ token[i], name);
+ exit(1);
+ }
+ }
+
+ if (intval[TOK_HMODE] != MODE_ACTIVE &&
+ intval[TOK_HMODE] != MODE_CLIENT &&
+ intval[TOK_HMODE] != MODE_BROADCAST) {
+ syslog(LOG_ERR, "invalid mode (%d) in file %s",
+ intval[TOK_HMODE], name);
+ exit(1);
+ }
+
+ if (intval[TOK_VERSION] > NTP_VERSION ||
+ intval[TOK_VERSION] < NTP_OLDVERSION) {
+ syslog(LOG_ERR, "invalid version (%d) in file %s",
+ intval[TOK_VERSION], name);
+ exit(1);
+ }
+ if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
+ intval[TOK_MINPOLL] > NTP_MAXPOLL) {
+ syslog(LOG_ERR, "invalid MINPOLL value (%d) in file %s",
+ intval[TOK_MINPOLL], name);
+ exit(1);
+ }
+
+ if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
+ intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
+ syslog(LOG_ERR, "invalid MAXPOLL value (%d) in file %s",
+ intval[TOK_MAXPOLL], name);
+ exit(1);
+ }
+
+ if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE|FLAG_PREFER))
+ != 0) {
+ syslog(LOG_ERR, "invalid flags (%d) in file %s",
+ intval[TOK_FLAGS], name);
+ exit(1);
+ }
+
+ flags = 0;
+ if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
+ flags |= CONF_FLAG_AUTHENABLE;
+ if (intval[TOK_FLAGS] & FLAG_PREFER)
+ flags |= CONF_FLAG_PREFER;
+
+ /*
+ * This is as good as we can check it. Add it in.
+ */
+ addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
+ (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
+ (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
+ intval[TOK_KEYID]);
+ }
+}
+
+
+/*
+ * doconfigure - attempt to resolve names and configure the server
+ */
+static void
+doconfigure(dores)
+ int dores;
+{
+ register struct conf_entry *ce;
+ register struct conf_entry *ceremove;
+
+ ce = confentries;
+ while (ce != NULL) {
+ if (dores && ce->ce_peeraddr == 0) {
+ if (!findhostaddr(ce)) {
+ syslog(LOG_ERR,
+ "couldn't resolve `%s', giving up on it",
+ ce->ce_name);
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+
+ if (ce->ce_peeraddr != 0) {
+ if (request(&ce->ce_config)) {
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+ ce = ce->ce_next;
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_io.c b/usr.sbin/xntpd/xntpd/ntp_io.c
new file mode 100644
index 0000000..ba35a2f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_io.c
@@ -0,0 +1,1807 @@
+/*
+ * xntp_io.c - input/output routines for xntpd. The socket-opening code
+ * was shamelessly stolen from ntpd.
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#ifndef __bsdi__
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#endif
+#if defined(__bsdi__) || defined(SYS_NETBSD) || defined(SYS_FREEBSD) || defined(SYS_AIX)
+#include <sys/ioctl.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#if defined(MCAST) && !defined(IP_ADD_MEMBERSHIP)
+#undef MCAST
+#endif
+
+#if defined(BSD)&&!defined(sun)&&!defined(SYS_SINIXM)
+#if BSD >= 199006
+#define HAVE_VARIABLE_IFR_LENGTH
+#endif
+#endif
+
+#if !defined(HAVE_VARIABLE_IFR_LENGTH) && defined(AF_LINK) && (defined(_SOCKADR_LEN) || !defined(SYS_DECOSF1))
+#define HAVE_VARIABLE_IFR_LENGTH
+#endif
+
+#if defined(USE_TTY_SIGPOLL)||defined(USE_UDP_SIGPOLL)
+#if defined(SYS_AIX)&&defined(_IO)
+#undef _IO
+#endif
+#include <stropts.h>
+#endif
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see which sockets are ready and read the
+ * packets from them into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We watch the number of recvbufs available to the signal handler
+ * and allocate more when this number drops below the low water
+ * mark. If the signal handler should run out of buffers in the
+ * interim it will drop incoming frames, the idea being that it is
+ * better to drop a packet than to be inaccurate.
+ */
+
+/*
+ * Block the interrupt, for critical sections.
+ */
+#if defined(HAVE_SIGNALED_IO)
+#define BLOCKIO() ((void) block_sigio())
+#define UNBLOCKIO() ((void) unblock_sigio())
+#else
+#define BLOCKIO()
+#define UNBLOCKIO()
+#endif
+
+/*
+ * recvbuf memory management
+ */
+#define RECV_INIT 10 /* 10 buffers initially */
+#define RECV_LOWAT 3 /* when we're down to three buffers get more */
+#define RECV_INC 5 /* get 5 more at a time */
+#define RECV_TOOMANY 30 /* this is way too many buffers */
+
+/*
+ * Memory allocation
+ */
+u_long full_recvbufs; /* number of recvbufs on fulllist */
+u_long free_recvbufs; /* number of recvbufs on freelist */
+
+static struct recvbuf *freelist; /* free buffers */
+static struct recvbuf *fulllist; /* lifo buffers with data */
+static struct recvbuf *beginlist; /* fifo buffers with data */
+
+u_long total_recvbufs; /* total recvbufs currently in use */
+u_long lowater_additions; /* number of times we have added memory */
+
+static struct recvbuf initial_bufs[RECV_INIT]; /* initial allocation */
+
+
+/*
+ * Other statistics of possible interest
+ */
+u_long packets_dropped; /* total number of packets dropped on reception */
+u_long packets_ignored; /* packets received on wild card interface */
+u_long packets_received; /* total number of packets received */
+u_long packets_sent; /* total number of packets sent */
+u_long packets_notsent; /* total number of packets which couldn't be sent */
+
+u_long handler_calls; /* number of calls to interrupt handler */
+u_long handler_pkts; /* number of pkts received by handler */
+u_long io_timereset; /* time counters were reset */
+
+/*
+ * Interface stuff
+ */
+#define MAXINTERFACES 192 /* much better for big gateways with IP/X.25 and more ... */
+struct interface *any_interface; /* pointer to default interface */
+struct interface *loopback_interface; /* point to loopback interface */
+static struct interface inter_list[MAXINTERFACES];
+static int ninterfaces;
+
+#ifdef REFCLOCK
+/*
+ * Refclock stuff. We keep a chain of structures with data concerning
+ * the guys we are doing I/O for.
+ */
+static struct refclockio *refio;
+#endif
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+fd_set activefds;
+int maxactivefd;
+
+/*
+ * Imported from ntp_timer.c
+ */
+extern u_long current_time;
+
+extern int errno;
+extern int debug;
+
+static int create_sockets P((u_int));
+static int open_socket P((struct sockaddr_in *, int));
+static void close_socket P((int));
+#ifdef HAVE_SIGNALED_IO
+static int init_clock_sig P(());
+static void init_socket_sig P((int));
+static void set_signal P(());
+static RETSIGTYPE sigio_handler P((int));
+static void block_sigio P((void));
+static void unblock_sigio P(());
+#endif
+
+/*
+ * init_io - initialize I/O data structures and call socket creation routine
+ */
+void
+init_io()
+{
+ register int i;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ freelist = 0;
+ for (i = 0; i < RECV_INIT; i++) {
+ initial_bufs[i].next = freelist;
+ freelist = &initial_bufs[i];
+ }
+
+ fulllist = 0;
+ free_recvbufs = total_recvbufs = RECV_INIT;
+ full_recvbufs = lowater_additions = 0;
+ packets_dropped = packets_received = 0;
+ packets_ignored = 0;
+ packets_sent = packets_notsent = 0;
+ handler_calls = handler_pkts = 0;
+ io_timereset = 0;
+ loopback_interface = 0;
+
+#ifdef REFCLOCK
+ refio = 0;
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal();
+#endif
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ (void) create_sockets(htons(NTP_PORT));
+ UNBLOCKIO();
+
+#ifdef DEBUG
+ if (debug)
+ printf("init_io: maxactivefd %d\n", maxactivefd);
+#endif
+}
+
+/*
+ * create_sockets - create a socket for each interface plus a default
+ * socket for when we don't know where to send
+ */
+static int
+create_sockets(port)
+ u_int port;
+{
+#ifdef STREAMS_TLI
+ struct strioctl ioc;
+#endif /* STREAMS_TLI */
+ char buf[MAXINTERFACES*sizeof(struct ifreq)];
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ int n, i, j, vs, size;
+ struct sockaddr_in resmask;
+
+#ifdef DEBUG
+ if (debug)
+ printf("create_sockets(%d)\n", ntohs(port));
+#endif
+
+ /*
+ * create pseudo-interface with wildcard address
+ */
+ inter_list[0].sin.sin_family = AF_INET;
+ inter_list[0].sin.sin_port = port;
+ inter_list[0].sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ (void) strncpy(inter_list[0].name, "wildcard",
+ sizeof(inter_list[0].name));
+ inter_list[0].mask.sin_addr.s_addr = htonl(~0);
+ inter_list[0].received = 0;
+ inter_list[0].sent = 0;
+ inter_list[0].notsent = 0;
+ inter_list[0].flags = INT_BROADCAST;
+
+#ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG
+ if ((vs = open("/dev/ip", O_RDONLY)) < 0) {
+#else /* ! USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+ if ((vs = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+#endif /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+ syslog(LOG_ERR, "vs=socket(AF_INET, SOCK_DGRAM) %m");
+ exit(1);
+ }
+
+ i = 1;
+
+ ifc.ifc_len = sizeof(buf);
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFCONF;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)buf;
+ ioc.ic_len = sizeof(buf);
+ if(ioctl(vs, I_STR, &ioc) < 0 ||
+ ioc.ic_len < sizeof(struct ifreq)) {
+ syslog(LOG_ERR, "get interface configuration: %m");
+ exit(1);
+ }
+#ifdef SIZE_RETURNED_IN_BUFFER
+ ifc.ifc_len = ioc.ic_len - sizeof(int);
+ ifc.ifc_buf = buf + sizeof(int);
+#else /* ! SIZE_RETURNED_IN_BUFFER */
+ ifc.ifc_len = ioc.ic_len;
+ ifc.ifc_buf = buf;
+#endif /* SIZE_RETURNED_IN_BUFFER */
+
+#else /* ! STREAMS_TLI */
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) {
+ syslog(LOG_ERR, "get interface configuration: %m");
+ exit(1);
+ }
+#endif /* STREAMS_TLI */
+
+ for(n = ifc.ifc_len, ifr = ifc.ifc_req; n > 0;
+ ifr = (struct ifreq *)((char *)ifr + size)) {
+ size = sizeof(*ifr);
+
+#ifdef HAVE_VARIABLE_IFR_LENGTH
+ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
+ size += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
+#endif
+ n -= size;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ifreq = *ifr;
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFFLAGS;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "get interface flags: %m");
+ continue;
+ }
+ if ((ifreq.ifr_flags & IFF_UP) == 0)
+ continue;
+ inter_list[i].flags = 0;
+ if (ifreq.ifr_flags & IFF_BROADCAST)
+ inter_list[i].flags |= INT_BROADCAST;
+#if !defined(SUN_3_3_STINKS)
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+ if (ifreq.ifr_flags & IFF_LOCAL_LOOPBACK)
+#else
+ if (ifreq.ifr_flags & IFF_LOOPBACK)
+#endif
+ {
+ inter_list[i].flags |= INT_LOOPBACK;
+ if (loopback_interface == 0)
+ loopback_interface = &inter_list[i];
+ }
+#endif
+
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "get interface addr: %m");
+ continue;
+ }
+
+ (void)strncpy(inter_list[i].name, ifreq.ifr_name,
+ sizeof(inter_list[i].name));
+ inter_list[i].sin = *(struct sockaddr_in *)&ifreq.ifr_addr;
+ inter_list[i].sin.sin_family = AF_INET;
+ inter_list[i].sin.sin_port = port;
+
+#if defined(SUN_3_3_STINKS)
+ /*
+ * Oh, barf! I'm too disgusted to even explain this
+ */
+ if (SRCADR(&inter_list[i].sin) == 0x7f000001) {
+ inter_list[i].flags |= INT_LOOPBACK;
+ if (loopback_interface == 0)
+ loopback_interface = &inter_list[i];
+ }
+#endif
+ if (inter_list[i].flags & INT_BROADCAST) {
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFBRDADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "SIOCGIFBRDADDR fails");
+ exit(1);
+ }
+#ifndef ifr_broadaddr
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_addr;
+#else
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+#endif
+ inter_list[i].bcast.sin_family = AF_INET;
+ inter_list[i].bcast.sin_port = port;
+ }
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFNETMASK;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "SIOCGIFNETMASK fails");
+ exit(1);
+ }
+ inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr;
+
+ /*
+ * look for an already existing source interface address. If
+ * the machine has multiple point to point interfaces, then
+ * the local address may appear more than once.
+ */
+ for (j=0; j < i; j++)
+ if (inter_list[j].sin.sin_addr.s_addr ==
+ inter_list[i].sin.sin_addr.s_addr) {
+ break;
+ }
+ if (j == i)
+ i++;
+ }
+ close(vs);
+ ninterfaces = i;
+
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+
+ for (i = 0; i < ninterfaces; i++) {
+ inter_list[i].fd = open_socket(&inter_list[i].sin,
+ inter_list[i].flags & INT_BROADCAST);
+ }
+
+#if defined(MCAST) && !defined(sun) && !defined(SYS_BSDI) && !defined(SYS_DECOSF1) && !defined(SYS_44BSD)
+ /*
+ * enable possible multicast reception on the broadcast socket
+ */
+ inter_list[0].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+ inter_list[0].bcast.sin_family = AF_INET;
+ inter_list[0].bcast.sin_port = port;
+#endif /* MCAST */
+
+ /*
+ * Blacklist all bound interface addresses
+ */
+ resmask.sin_addr.s_addr = ~0L;
+ for (i = 1; i < ninterfaces; i++)
+ restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+
+ any_interface = &inter_list[0];
+#ifdef DEBUG
+ if (debug > 2) {
+ printf("create_sockets: ninterfaces=%d\n", ninterfaces);
+ for (i = 0; i < ninterfaces; i++) {
+ printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n",
+ i,
+ inter_list[i].fd,
+ inter_list[i].bfd,
+ inter_list[i].name,
+ inter_list[i].flags);
+ /* Leave these as three printf calls. */
+ printf(" sin=%s",
+ inet_ntoa((inter_list[i].sin.sin_addr)));
+ if(inter_list[i].flags & INT_BROADCAST)
+ printf(" bcast=%s,",
+ inet_ntoa((inter_list[i].bcast.sin_addr)));
+ printf(" mask=%s\n",
+ inet_ntoa((inter_list[i].mask.sin_addr)));
+ }
+ }
+#endif
+ return ninterfaces;
+}
+
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient()
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if (inter_list[i].flags & INT_BCASTOPEN)
+ continue;
+#ifdef SOLARIS
+ inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+#ifndef SYS_DOMAINOS
+ inter_list[i].bfd = open_socket(&inter_list[i].bcast, 0);
+ inter_list[i].flags |= INT_BCASTOPEN;
+#endif
+ }
+}
+
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(addr)
+ u_long addr;
+{
+#ifdef MCAST
+ struct ip_mreq mreq;
+ int i = ninterfaces; /* Use the next interface */
+ u_long haddr = ntohl(addr);
+ struct in_addr iaddr;
+ int s;
+ struct sockaddr_in *sinp;
+
+ iaddr.s_addr = addr;
+
+ if (!IN_CLASSD(haddr))
+ { syslog(LOG_ERR,
+ "cannot add multicast address %s as it is not class D",
+ inet_ntoa(iaddr));
+ return;
+ }
+
+ for (i=0; i<ninterfaces; i++) {
+ /* Already have this address */
+ if (inter_list[i].sin.sin_addr.s_addr == addr) return;
+ /* found a free slot */
+ if (inter_list[i].sin.sin_addr.s_addr == 0 &&
+ inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 &&
+ inter_list[i].flags == 0) break;
+ }
+ sinp = &(inter_list[i].sin);
+
+ memset((char *)&mreq, 0, sizeof(mreq));
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ sinp->sin_family = AF_INET;
+ sinp->sin_addr = iaddr;
+ sinp->sin_port = htons(123);
+
+ s = open_socket(sinp, 0);
+ /* Try opening a socket for the specified class D address */
+ /* This works under SunOS 4.x, but not OSF1 .. :-( */
+ if (s < 0) {
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ i = 0;
+ /* HACK ! -- stuff in an address */
+ inter_list[i].bcast.sin_addr.s_addr = addr;
+ syslog(LOG_ERR, "...multicast address %s using wildcard socket",
+ inet_ntoa(iaddr));
+ }
+ else {
+ inter_list[i].fd = s;
+ inter_list[i].bfd = -1;
+ (void) strncpy(inter_list[i].name, "multicast",
+ sizeof(inter_list[i].name));
+ inter_list[i].mask.sin_addr.s_addr = htonl(~0);
+ }
+
+ /*
+ * enable reception of multicast packets
+ */
+ mreq.imr_multiaddr = iaddr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)",
+ mreq.imr_multiaddr, mreq.imr_interface.s_addr,
+ inet_ntoa(iaddr));
+ inter_list[i].flags |= INT_MULTICAST;
+ if (i >= ninterfaces) ninterfaces = i+1;
+#else /* MCAST */
+ struct in_addr iaddr;
+ iaddr.s_addr = addr;
+ syslog(LOG_ERR, "cannot add multicast address %s as no MCAST support",
+ inet_ntoa(iaddr));
+#endif /* MCAST */
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient()
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ continue;
+ close_socket(inter_list[i].bfd);
+ inter_list[i].flags &= ~INT_BCASTOPEN;
+ }
+}
+
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(addr)
+ u_long addr;
+{
+#ifdef MCAST
+ int i;
+ struct ip_mreq mreq;
+ struct sockaddr_in sinaddr;
+
+ if (!IN_CLASSD(addr)) {
+ sinaddr.sin_addr.s_addr = addr;
+ syslog(LOG_ERR,
+ "invalid multicast address %s", ntoa(&sinaddr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ mreq.imr_multiaddr.s_addr = addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ for (i = 0; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_MULTICAST))
+ continue;
+ if (!(inter_list[i].fd < 0))
+ continue;
+ if (addr != inter_list[i].sin.sin_addr.s_addr)
+ continue;
+ if (i != 0) {
+ /* we have an explicit fd, so we can slose it */
+ close_socket(inter_list[i].fd);
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ inter_list[i].fd = -1;
+ inter_list[i].bfd = -1;
+ } else {
+ /* We are sharing "any address" port :-( Don't close it! */
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ syslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m");
+ /* This is **WRONG** -- there may be others ! */
+ /* There should be a count of users ... */
+ inter_list[i].flags &= ~INT_MULTICAST;
+ }
+ }
+#else /* MCAST */
+ syslog(LOG_ERR, "this function requires multicast kernel");
+#endif /* MCAST */
+}
+
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+static int
+open_socket(addr, flags)
+ struct sockaddr_in *addr;
+ int flags;
+{
+ int fd;
+ int on = 1, off = 0;
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /* set SO_REUSEADDR since we will be binding the same port
+ number on each interface */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m");
+ }
+
+ /*
+ * bind the local address.
+ */
+ if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
+ char buff[160];
+ sprintf(buff,
+ "bind() fd %d, family %d, port %d, addr %08lx, in_classd=%d flags=%d fails: %%m",
+ fd, addr->sin_family, (int)ntohs(addr->sin_port),
+ (u_long)ntohl(addr->sin_addr.s_addr),
+ IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags);
+ syslog(LOG_ERR, buff);
+ close(fd);
+
+ /*
+ * soft fail if opening a class D address
+ */
+ if (IN_CLASSD(ntohl(addr->sin_addr.s_addr)))
+ return -1;
+ exit(1);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("bind() fd %d, family %d, port %d, addr %08lx, flags=%d\n",
+ fd,
+ addr->sin_family,
+ (int)ntohs(addr->sin_port),
+ (u_long)ntohl(addr->sin_addr.s_addr),
+ flags);
+#endif
+ if (fd > maxactivefd)
+ maxactivefd = fd;
+ FD_SET(fd, &activefds);
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#else /* HAVE_SIGNALED_IO */
+
+ /*
+ * set non-blocking,
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* O_NONBLOCK */
+#if defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* FNDELAY */
+Need non blocking I/O
+#endif /* FNDELAY */
+#endif /* O_NONBLOCK */
+#endif /* HAVE_SIGNALED_IO */
+
+ /*
+ * Turn off the SO_REUSEADDR socket option. It apparently
+ * causes heartburn on systems with multicast IP installed.
+ * On normal systems it only gets looked at when the address
+ * is being bound anyway..
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&off, sizeof(off))) {
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m");
+ }
+
+#ifdef SO_BROADCAST
+ /* if this interface can support broadcast, set SO_BROADCAST */
+ if (flags & INT_BROADCAST) {
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m");
+ }
+ }
+#endif /* SO_BROADCAST */
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("flags for fd %d: 0%o\n", fd,
+ fcntl(fd, F_GETFL, 0));
+#endif
+
+ return fd;
+}
+
+
+/*
+ * closesocket - close a socket and remove from the activefd list
+ */
+static void
+close_socket(fd)
+ int fd;
+{
+ int i, newmax;
+
+ (void) close(fd);
+ FD_CLR(fd, &activefds);
+
+ if (fd >= maxactivefd) {
+ newmax = 0;
+ for (i = 0; i < maxactivefd; i++)
+ if (FD_ISSET(i, &activefds))
+ newmax = i;
+ maxactivefd = newmax;
+ }
+}
+
+
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+struct interface *
+findbcastinter(addr)
+ struct sockaddr_in *addr;
+{
+#ifdef SIOCGIFCONF
+ register int i;
+ register u_long netnum;
+
+ netnum = NSRCADR(addr);
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if (NSRCADR(&inter_list[i].bcast) == netnum)
+ return &inter_list[i];
+ if ((NSRCADR(&inter_list[i].sin) & NSRCADR(&inter_list[i].mask))
+ == (netnum & NSRCADR(&inter_list[i].mask)))
+ return &inter_list[i];
+ }
+#endif /* SIOCGIFCONF */
+ return any_interface;
+}
+
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * getrecvbufs - get receive buffers which have data in them
+ *
+ * ***N.B. must be called with SIGIO blocked***
+ */
+struct recvbuf *
+getrecvbufs()
+{
+ struct recvbuf *rb;
+
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs: %ld handler interrupts, %ld frames\n",
+ handler_calls, handler_pkts);
+#endif
+
+ if (full_recvbufs == 0) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs called, no action here\n");
+#endif
+ return (struct recvbuf *)0; /* nothing has arrived */
+ }
+
+ /*
+ * Get the fulllist chain and mark it empty
+ */
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs returning %ld buffers\n", full_recvbufs);
+#endif
+ rb = beginlist;
+ fulllist = 0;
+ full_recvbufs = 0;
+
+ /*
+ * Check to see if we're below the low water mark.
+ */
+ if (free_recvbufs <= RECV_LOWAT) {
+ register struct recvbuf *buf;
+ register int i;
+
+ if (total_recvbufs >= RECV_TOOMANY)
+ syslog(LOG_ERR, "too many recvbufs allocated (%d)",
+ total_recvbufs);
+ else {
+ buf = (struct recvbuf *)
+ emalloc(RECV_INC*sizeof(struct recvbuf));
+ for (i = 0; i < RECV_INC; i++) {
+ buf->next = freelist;
+ freelist = buf;
+ buf++;
+ }
+
+ free_recvbufs += RECV_INC;
+ total_recvbufs += RECV_INC;
+ lowater_additions++;
+ }
+ }
+
+ /*
+ * Return the chain
+ */
+ return rb;
+}
+
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+ BLOCKIO();
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ UNBLOCKIO();
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination. Maintain a
+ * send error cache so that only the first consecutive error for a
+ * destination is logged.
+ */
+void
+sendpkt(dest, inter, ttl, pkt, len)
+ struct sockaddr_in *dest;
+ struct interface *inter;
+ int ttl;
+ struct pkt *pkt;
+ int len;
+{
+ int cc, slot;
+ /*
+ * Send error cache. Empty slots have port == 0
+ * Set ERRORCACHESIZE to 0 to disable
+ */
+ struct cache {
+ u_short port;
+ struct in_addr addr;
+ };
+
+#ifndef ERRORCACHESIZE
+#define ERRORCACHESIZE 8
+#endif
+#if ERRORCACHESIZE > 0
+ static struct cache badaddrs[ERRORCACHESIZE];
+#else
+#define badaddrs ((struct cache *)0) /* Only used in empty loops! */
+#endif
+
+#ifdef DEBUG
+ if (debug)
+ printf("%ssendpkt(fd=%d %s, %s, ttl=%d, %d)\n",
+ (ttl >= 0) ? "\tMCAST\t*****" : "",
+ inter->fd, ntoa(dest),
+ ntoa(&inter->sin), ttl, len);
+#endif
+
+#ifdef MCAST
+ /* for the moment we use the bcast option to set multicast ttl */
+ if (ttl >= 0 && ttl != inter->last_ttl) {
+ u_char mttl = ttl;
+
+ /* set the multicast ttl for outgoing packets */
+ if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &mttl, sizeof(mttl)) == -1) {
+ syslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m");
+ }
+ else inter->last_ttl = ttl;
+ }
+#endif /* MCAST */
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == dest->sin_port &&
+ badaddrs[slot].addr.s_addr == dest->sin_addr.s_addr)
+ break;
+
+ cc = sendto(inter->fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ inter->notsent++;
+ packets_notsent++;
+ if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0) {
+ /*
+ * Remember this, if there's an empty slot
+ */
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == 0) {
+ badaddrs[slot].port = dest->sin_port;
+ badaddrs[slot].addr = dest->sin_addr;
+ break;
+ }
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+ } else {
+ inter->sent++;
+ packets_sent++;
+ /*
+ * He's not bad any more
+ */
+ if (slot >= 0)
+ badaddrs[slot].port = 0;
+ }
+}
+
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+void
+input_handler(cts)
+ l_fp *cts;
+{
+ register int i, n;
+ register struct recvbuf *rb;
+ register int doing;
+ register int fd;
+ struct timeval tvzero;
+ int fromlen;
+ l_fp ts;
+ fd_set fds;
+ int first = 1;
+
+ handler_calls++;
+ ts = *cts;
+
+ /*
+ * Do a poll to see who has data
+ */
+again:
+ fds = activefds;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ /*
+ * If nothing to do, just return. If an error occurred, complain
+ * and return. If we've got some, freeze a timestamp.
+ */
+ if (n == 0)
+ return;
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return;
+ }
+ if (!first)get_systime(&ts);
+ first = 0;
+ handler_pkts += n;
+
+#ifdef REFCLOCK
+ /*
+ * Check out the reference clocks first, if any
+ */
+ if (refio != 0) {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0 && n > 0; rp = rp->next) {
+ fd = rp->fd;
+ if (FD_ISSET(fd, &fds)) {
+ n--;
+ if (free_recvbufs == 0) {
+ char buf[RX_BUFF_SIZE];
+
+ (void) read(fd, buf, sizeof buf);
+ packets_dropped++;
+ continue;
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ i = (rp->datalen == 0
+ || rp->datalen > sizeof(rb->recv_space))
+ ? sizeof(rb->recv_space) : rp->datalen;
+
+ rb->recv_length =
+ read(fd, (char *)&rb->recv_space, i);
+
+ if (rb->recv_length == -1) {
+ syslog(LOG_ERR, "clock read fd %d: %m", fd);
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->recv_srcclock = rp->srcclock;
+ rb->dstadr = 0;
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = rp->clock_recv;
+
+ if (fulllist == 0) {
+ beginlist = rb;
+ rb->next = 0;
+ } else {
+ rb->next = fulllist->next;
+ fulllist->next = rb;
+ }
+ fulllist = rb;
+ full_recvbufs++;
+
+ rp->recvcount++;
+ packets_received++;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Loop through the interfaces looking for data to read.
+ */
+ for (i = ninterfaces-1; i >= 0 && n > 0; i--) {
+ for (doing = 0; doing < 2 && n > 0; doing++) {
+ if (doing == 0) {
+ fd = inter_list[i].fd;
+ } else {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ break;
+ fd = inter_list[i].bfd;
+ }
+ if (fd < 0) continue;
+ if (FD_ISSET(fd, &fds)) {
+ n--;
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on the wild card socket, just dump the
+ * packet.
+ */
+ if (!(free_recvbufs && i == 0 &&
+ inter_list[i].flags & INT_MULTICAST)) {
+#ifdef UDP_WILDCARD_DELIVERY
+ /*
+ * these guys manage to put properly addressed
+ * packets into the wildcard queue
+ */
+ if (free_recvbufs == 0) {
+#else
+ if (i == 0 || free_recvbufs == 0) {
+#endif
+ char buf[RX_BUFF_SIZE];
+ struct sockaddr from;
+ fromlen = sizeof from;
+ (void) recvfrom(fd, buf,
+ sizeof(buf), 0,
+ &from, &fromlen);
+#ifdef DEBUG
+ if (debug)
+ printf("ignore/drop on %d(%lu) fd=%d from %s\n",
+ i, free_recvbufs, fd,
+ inet_ntoa(((struct sockaddr_in *) &from)->sin_addr));
+#endif
+ if (i == 0)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ continue;
+ }
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd,
+ (char *)&rb->recv_space,
+ sizeof(rb->recv_space), 0,
+ (struct sockaddr *)&rb->recv_srcadr,
+ &fromlen);
+ if (rb->recv_length == -1) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd);
+#endif
+ continue;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d length %d from %08lx %s\n",
+ fd, rb->recv_length,
+ (u_long)ntohl(rb->recv_srcadr.sin_addr.s_addr) &
+ 0x00000000ffffffff, inet_ntoa(rb->recv_srcadr.sin_addr));
+#endif
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->dstadr = &inter_list[i];
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = receive;
+
+
+ if (fulllist == 0) {
+ beginlist = rb;
+ rb->next = 0;
+ } else {
+ rb->next = fulllist->next;
+ fulllist->next = rb;
+ }
+ fulllist = rb;
+ full_recvbufs++;
+
+ inter_list[i].received++;
+ packets_received++;
+ }
+ }
+ }
+ /*
+ * Done everything from that select. Poll again.
+ */
+ goto again;
+}
+
+
+/*
+ * findinterface - utility used by other modules to find an interface
+ * given an address.
+ */
+struct interface *
+findinterface(addr)
+ struct sockaddr_in *addr;
+{
+ register int i;
+ register u_long saddr;
+
+ /*
+ * Just match the address portion.
+ */
+ saddr = addr->sin_addr.s_addr;
+ for (i = 0; i < ninterfaces; i++) {
+ if (inter_list[i].sin.sin_addr.s_addr == saddr)
+ return &inter_list[i];
+ }
+ return (struct interface *)0;
+}
+
+
+/*
+ * io_clr_stats - clear I/O module statistics
+ */
+void
+io_clr_stats()
+{
+ packets_dropped = 0;
+ packets_ignored = 0;
+ packets_received = 0;
+ packets_sent = 0;
+ packets_notsent = 0;
+
+ handler_calls = 0;
+ handler_pkts = 0;
+ io_timereset = current_time;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * This is a hack so that I don't have to fool with these ioctls in the
+ * pps driver ... we are already non-blocking and turn on SIGIO thru
+ * another mechanisim
+ */
+int
+io_addclock_simple(rio)
+ struct refclockio *rio;
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_addclock - add a reference clock to the list and arrange that we
+ * get SIGIO interrupts from it.
+ */
+int
+io_addclock(rio)
+ struct refclockio *rio;
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+#ifdef HAVE_SIGNALED_IO
+ if (init_clock_sig(rio)) {
+ UNBLOCKIO();
+ return 0;
+ }
+#endif
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_closeclock - close the clock in the I/O structure given
+ */
+void
+io_closeclock(rio)
+ struct refclockio *rio;
+{
+ /*
+ * Remove structure from the list
+ */
+ if (refio == rio) {
+ refio = rio->next;
+ } else {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0; rp = rp->next)
+ if (rp->next == rio) {
+ rp->next = rio->next;
+ break;
+ }
+
+ if (rp == 0) {
+ /*
+ * Internal error. Report it.
+ */
+ syslog(LOG_ERR,
+ "internal error: refclockio structure not found");
+ return;
+ }
+ }
+
+ /*
+ * Close the descriptor. close_socket does the right thing despite
+ * the misnomer.
+ */
+ close_socket(rio->fd);
+}
+#endif /* REFCLOCK */
+
+/*
+ * SIGPOLL and SIGIO ROUTINES.
+ */
+#ifdef HAVE_SIGNALED_IO
+/*
+ * Some systems (MOST) define SIGPOLL==SIGIO others SIGIO==SIGPOLL a few
+ * have seperate SIGIO and SIGPOLL signals. This code checks for the
+ * SIGIO==SIGPOLL case at compile time.
+ * Do not defined USE_SIGPOLL or USE_SIGIO.
+ * these are interal only to ntp_io.c!
+ */
+#if defined(USE_SIGPOLL)
+#undef USE_SIGPOLL
+#endif
+#if defined(USE_SIGIO)
+#undef USE_SIGIO
+#endif
+
+#if defined(USE_TTY_SIGPOLL)||defined(USE_UDP_SIGPOLL)
+#define USE_SIGPOLL
+#endif
+
+#if !defined(USE_TTY_SIGPOLL)||!defined(USE_UDP_SIGPOLL)
+#define USE_SIGIO
+#endif
+
+#if defined(USE_SIGIO)&&defined(USE_SIGPOLL)
+#if SIGIO==SIGPOLL
+#define USE_SIGIO
+#undef USE_SIGPOLL
+#endif /* SIGIO==SIGPOLL */
+#endif /* USE_SIGIO && USE_SIGIO */
+
+
+/*
+ * TTY instialzation routeins.
+ */
+#ifndef USE_TTY_SIGPOLL
+/*
+ * Spical cases first!
+ */
+#if defined(SYS_HPUX)
+#define CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ int pgrp, on = 1;
+
+ pgrp = getpid();
+ if (ioctl(rio->fd, FIOSSAIOOWN, (char *)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSSAIOOWN) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (ioctl(rio->fd, FIOSNBIO, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSNBIO) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (ioctl(rio->fd, FIOSSAIOSTAT, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSSAIOSTAT) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+ return 0;
+}
+#endif /* SYS_HPUX */
+#if defined(SYS_AIX)&&!defined(_BSD)
+/*
+ * SYSV compatibility mode under AIX.
+ */
+#define CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ int pgrp, on = 1;
+
+ if (ioctl(rio->fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ pgrp = -getpid();
+ if (ioctl(rio->fd, FIOSETOWN, (char*)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSETOWN) fails for clock I/O: %m");
+ return 1;
+ }
+
+ if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* AIX && !BSD */
+#ifndef CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ if (fcntl(rio->fd, F_SETOWN, getpid()) == -1) {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails for clock I/O: %m");
+ return 1;
+ }
+
+ if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR,
+ "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* CLOCK_DONE */
+#else /* !USE_TTY_SIGPOLL */
+int
+static init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ if (ioctl(rio->fd, I_SETSIG, S_INPUT) < 0) {
+ syslog(LOG_ERR,
+ "ioctl(I_SETSIG, S_INPUT) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* !USE_TTY_SIGPOLL */
+
+
+
+#ifndef USE_UDP_SIGPOLL
+/*
+ * Socket SIGPOLL initialization routines.
+ * Special cases first!
+ */
+#if defined(SYS_HPUX) || defined(SYS_LINUX)
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ int pgrp, on = 1;
+
+ /*
+ * Big difference here for HP-UX ... why can't life be easy ?
+ */
+ if (ioctl(fd, FIOSNBIO, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (ioctl(fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+#if (SYS_HPUX > 7)
+ pgrp = getpid();
+#else
+ pgrp = -getpid();
+#endif
+ if (ioctl(fd, SIOCSPGRP, (char *)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(SIOCSPGRP) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* SYS_HPUX */
+#if defined(SYS_AIX)&&!defined(_BSD)
+/*
+ * SYSV compatibility mod under AIX
+ */
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ int pgrp, on = 1;
+
+ if (ioctl(fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+ pgrp = -getpid();
+ if (ioctl(fd, FIOSETOWN, (char*)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSETOWN) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* AIX && !BSD */
+#if defined(UDP_BACKWARDS_SETOWN)
+/*
+ * SunOS 3.5 and Ultirx 2.0
+ */
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ /*
+ * The way Sun did it as recently as SunOS 3.5. Only
+ * in the case of sockets, of course, just to confuse
+ * the issue. Don't they even bother to test the stuff
+ * they send out? Ibid for Ultrix 2.0
+ */
+ if (fcntl(fd, F_SETOWN, -getpid()) == -1)
+ {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m");
+ exit(1);
+ }
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* UDP_BACKWARDS_SETOWN */
+#ifndef SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ if (fcntl(fd, F_SETOWN, getpid()) == -1)
+ {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m");
+ exit(1);
+ }
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* SOCKET_DONE */
+#else /* !USE_UDP_SIGPOLL */
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ if (ioctl(fd, I_SETSIG, S_INPUT) < 0) {
+ syslog(LOG_ERR,
+ "ioctl(I_SETSIG, S_INPUT) fails for socket I/O: %m");
+ exit(1);
+ }
+}
+#endif /* USE_UDP_SIGPOLL */
+
+static RETSIGTYPE
+sigio_handler(sig)
+int sig;
+{
+ l_fp ts;
+
+#ifdef SYS_SVR4
+ /* This should not be necessary for a signal previously set with
+ * sigset().
+ */
+# if defined(USE_SIGIO)
+ (void) sigset(SIGIO, sigio_handler);
+# endif
+# if defined(USE_SIGPOLL)
+ (void) sigset(SIGPOLL, sigio_handler);
+# endif
+#endif /* SYS_SVR4 */
+
+ get_systime(&ts);
+ (void)input_handler(&ts);
+}
+
+/*
+ * Signal support routines.
+ */
+#ifdef NTP_POSIX_SOURCE
+static void
+set_signal()
+{
+ int n;
+ struct sigaction vec;
+
+ sigemptyset(&vec.sa_mask);
+
+#ifdef USE_SIGIO
+ sigaddset(&vec.sa_mask, SIGIO);
+#endif
+#ifdef USE_SIGPOLL
+ sigaddset(&vec.sa_mask, SIGPOLL);
+#endif
+ vec.sa_flags = 0;
+
+#if defined(USE_SIGIO)
+ vec.sa_handler = sigio_handler;
+
+ while (1) {
+ n = sigaction(SIGIO, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+#endif
+#if defined(USE_SIGPOLL)
+ vec.sa_handler = sigio_handler;
+
+ while (1) {
+ n = sigaction(SIGPOLL, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+#endif
+}
+
+void
+block_io_and_alarm()
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+#if defined(USE_SIGIO)
+ sigaddset(&set, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&set, SIGPOLL);
+#endif
+ sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+static void
+block_sigio()
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+#if defined(USE_SIGIO)
+ sigaddset(&set, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&set, SIGPOLL);
+#endif
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+void
+unblock_io_and_alarm()
+{
+ sigset_t unset;
+
+ sigemptyset(&unset);
+
+#if defined(USE_SIGIO)
+ sigaddset(&unset, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&unset, SIGPOLL);
+#endif
+ sigaddset(&unset, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &unset, NULL);
+}
+
+static
+void
+unblock_sigio()
+{
+ sigset_t unset;
+
+ sigemptyset(&unset);
+
+#if defined(USE_SIGIO)
+ sigaddset(&unset, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&unset, SIGPOLL);
+#endif
+ sigprocmask(SIG_UNBLOCK, &unset, NULL);
+}
+
+void
+wait_for_signal()
+{
+ sigset_t old;
+
+ sigprocmask(SIG_UNBLOCK, NULL, &old);
+
+#if defined(USE_SIGIO)
+ sigdelset(&old, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigdelset(&old, SIGPOLL);
+#endif
+ sigdelset(&old, SIGALRM);
+
+ sigsuspend(&old);
+}
+
+#else
+/*
+ * Must be an old bsd system.
+ * We assume there is no SIGPOLL.
+ */
+
+void
+block_io_and_alarm()
+{
+ int mask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ (void)sigblock(mask);
+}
+
+void
+block_sigio()
+{
+ int mask;
+
+ mask = sigmask(SIGIO);
+ (void)sigblock(mask);
+}
+
+static void
+set_signal()
+{
+ (void) signal_no_reset(SIGIO, sigio_handler);
+}
+
+void
+unblock_io_and_alarm()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ omask = sigblock(0);
+ omask &= ~mask;
+ (void)sigsetmask(omask);
+}
+
+void
+unblock_sigio()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO);
+ omask = sigblock(0);
+ omask &= ~mask;
+ (void)sigsetmask(omask);
+}
+
+void
+wait_for_signal()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ omask = sigblock(0);
+ omask &= ~mask;
+ sigpause(omask);
+}
+#endif /* NTP_POSIX_SOURCE */
+#endif /* HAVE_SIGNALED_IO */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_leap.c b/usr.sbin/xntpd/xntpd/ntp_leap.c
new file mode 100644
index 0000000..5e7eca7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_leap.c
@@ -0,0 +1,315 @@
+/*
+ * ntp_leap - maintain leap bits and take action when a leap occurs
+ */
+#include <stdio.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This module is devoted to maintaining the leap bits and taking
+ * action when a leap second occurs. It probably has bugs since
+ * a leap second has never occurred to excercise the code.
+ *
+ * The code does two things when a leap second occurs. It first
+ * steps the clock one second in the appropriate direction. It
+ * then informs the reference clock code, if compiled in, that the
+ * leap second has occured so that any clocks which need to disable
+ * themselves can do so. This is done within the first few seconds
+ * after midnight, UTC.
+ *
+ * The code maintains two variables which may be written externally,
+ * leap_warning and leap_indicator. Leap_warning can be written
+ * any time in the month preceeding a leap second. 24 hours before
+ * the leap is to occur, leap_warning's contents are copied to
+ * leap_indicator. The latter is used by reference clocks to set
+ * their leap bits.
+ *
+ * The module normally maintains a timer which is arranged to expire
+ * just after 0000Z one day before the leap. On the day a leap might
+ * occur the interrupt is aimed at 2200Z and every 5 minutes thereafter
+ * until 1200Z to see if the leap bits appear.
+ */
+
+/*
+ * The leap indicator and leap warning flags. Set by control messages
+ */
+u_char leap_indicator;
+u_char leap_warning;
+u_char leap_mask; /* set on day before a potential leap */
+/*
+ * Timer. The timer code imports this so it can call us prior to
+ * calling out any pending transmits.
+ */
+u_long leap_timer;
+
+/*
+ * We don't want to do anything drastic if the leap function is handled
+ * by the kernel.
+ */
+extern int pll_control; /* set nonzero if kernel pll in uss */
+
+/*
+ * Internal leap bits. If we see leap bits set during the last
+ * hour we set these.
+ */
+u_char leapbits;
+
+/*
+ * Constants.
+ */
+#define OKAYTOSETWARNING (31*24*60*60)
+#define DAYBEFORE (24*60*60)
+#define TWOHOURSBEFORE (2*60*60)
+#define FIVEMINUTES (5*60)
+#define ONEMINUTE (60)
+
+/*
+ * Imported from the timer module.
+ */
+extern u_long current_time;
+
+
+/*
+ * Some statistics counters
+ */
+u_long leap_processcalls; /* calls to leap_process */
+u_long leap_notclose; /* leap found to be a long time from now */
+u_long leap_monthofleap; /* in the month of a leap */
+u_long leap_dayofleap; /* This is the day of the leap */
+u_long leap_hoursfromleap; /* only 2 hours from leap */
+u_long leap_happened; /* leap process saw the leap */
+
+/*
+ * Imported from the main module
+ */
+extern int debug;
+
+
+static void setnexttimeout P((u_long));
+
+/*
+ * init_leap - initialize the leap module's data.
+ */
+void
+init_leap()
+{
+ /*
+ * Zero the indicators. Schedule an event for just after
+ * initialization so we can sort things out.
+ */
+ leap_indicator = leap_warning = leap_mask = 0;
+ leap_timer = 1<<EVENT_TIMEOUT;
+ leapbits = 0;
+
+ leap_processcalls = leap_notclose = 0;
+ leap_monthofleap = leap_dayofleap = 0;
+ leap_hoursfromleap = leap_happened = 0;
+}
+
+
+/*
+ * leap_process - process a leap event expiry and/or a system time step
+ */
+void
+leap_process()
+{
+ u_long leapnext;
+ u_long leaplast;
+ l_fp ts;
+ u_char bits;
+ extern u_char sys_leap;
+
+ leap_processcalls++;
+ get_systime(&ts);
+ calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
+
+ /*
+ * Figure out what to do based on how long to the next leap.
+ */
+ if (leapnext > OKAYTOSETWARNING) {
+ if (leaplast < ONEMINUTE) {
+ /*
+ * The golden moment! See if there's anything
+ * to do.
+ */
+ leap_happened++;
+ bits = 0;
+ leap_mask = 0;
+ if (leap_indicator != 0)
+ bits = leap_indicator;
+ else if (leapbits != 0)
+ bits = leapbits;
+
+ if (bits != 0 && !pll_control) {
+ l_fp tmp;
+
+ /*
+ * Step the clock 1 second in the proper
+ * direction.
+ */
+ if (bits == LEAP_DELSECOND)
+ tmp.l_i = 1;
+ else
+ tmp.l_i = -1;
+ tmp.l_uf = 0;
+
+ step_systime(&tmp);
+#ifdef SLEWALWAYS
+ syslog(LOG_NOTICE,
+ "leap second occured, slewed time %s 1 second",
+ tmp.l_i > 0 ? "forward" : "back");
+#else
+ syslog(LOG_NOTICE,
+ "leap second occured, stepped time %s 1 second",
+ tmp.l_i > 0 ? "forward" : "back");
+#endif
+ }
+ } else {
+ leap_notclose++;
+ }
+ leap_warning = 0;
+ } else {
+ if (leapnext > DAYBEFORE)
+ leap_monthofleap++;
+ else if (leapnext > TWOHOURSBEFORE)
+ leap_dayofleap++;
+ else
+ leap_hoursfromleap++;
+ }
+
+ if (leapnext > DAYBEFORE) {
+ leap_indicator = 0;
+ leapbits = 0;
+ /*
+ * Berkeley's setitimer call does result in alarm
+ * signal drift despite rumours to the contrary.
+ * Schedule an event no more than 24 hours into
+ * the future to allow the event time to be
+ * recomputed.
+ */
+ if ((leapnext - DAYBEFORE) >= DAYBEFORE)
+ setnexttimeout((u_long)DAYBEFORE);
+ else
+ setnexttimeout(leapnext - DAYBEFORE);
+ return;
+ }
+
+ /*
+ * Here we're in the day of the leap. Set the leap indicator
+ * bits from the warning, if necessary.
+ */
+ if (leap_indicator == 0 && leap_warning != 0)
+ leap_indicator = leap_warning;
+ leap_mask = LEAP_NOTINSYNC;
+ if (leapnext > TWOHOURSBEFORE) {
+ leapbits = 0;
+ setnexttimeout(leapnext - TWOHOURSBEFORE);
+ return;
+ }
+
+ /*
+ * Here we're in the final 2 hours. If sys_leap is set, set
+ * leapbits to it.
+ */
+ if (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND)
+ leapbits = sys_leap;
+ setnexttimeout((leapnext > FIVEMINUTES) ? FIVEMINUTES : leapnext);
+}
+
+
+/*
+ * setnexttimeout - set the next leap alarm
+ */
+static void
+setnexttimeout(secs)
+ u_long secs;
+{
+ /*
+ * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT)
+ * seconds after the desired time.
+ */
+ leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time)
+ & ~((1<<EVENT_TIMEOUT)-1);
+}
+
+
+/*
+ * leap_setleap - set leap_indicator and/or leap_warning. Return failure
+ * if we don't want to do it.
+ */
+int
+leap_setleap(indicator, warning)
+ int indicator;
+ int warning;
+{
+ u_long leapnext;
+ u_long leaplast;
+ l_fp ts;
+ int i;
+
+ get_systime(&ts);
+ calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
+
+ i = 0;
+ if (warning != ~0)
+ if (leapnext > OKAYTOSETWARNING)
+ i = 1;
+
+ if (indicator != ~0)
+ if (leapnext > DAYBEFORE)
+ i = 1;
+
+ if (i) {
+ syslog(LOG_ERR,
+ "attempt to set leap bits at unlikely time of month");
+ return 0;
+ }
+
+ if (warning != ~0)
+ leap_warning = warning;
+
+ if (indicator != ~0) {
+ if (indicator == LEAP_NOWARNING) {
+ leap_warning = LEAP_NOWARNING;
+ }
+ leap_indicator = indicator;
+ }
+ return 1;
+}
+
+/*
+ * leap_actual
+ *
+ * calculate leap value - pass arg through of no local
+ * configuration. Otherwise ise local configuration
+ * (only used to cope with broken time servers and
+ * broken refclocks)
+ *
+ * Mapping of leap_indicator:
+ * LEAP_NOWARNING
+ * pass peer value to sys_leap - usual operation
+ * LEAP_ADD/DEL_SECOND
+ * pass LEAP_ADD/DEL_SECOND to sys_leap
+ * LEAP_NOTINSYNC
+ * pass LEAP_NOWARNING to sys_leap - effectively ignores leap
+ */
+/* there seems to be a bug in the IRIX 4 compiler which prevents
+ u_char from beeing used in prototyped functions
+ AIX also suffers from this.
+ So give up and define it terms of int.
+*/
+int
+leap_actual(l)
+ int l ;
+{
+ if (leap_indicator != LEAP_NOWARNING) {
+ if (leap_indicator == LEAP_NOTINSYNC)
+ return LEAP_NOWARNING;
+ else
+ return leap_indicator;
+ } else {
+ return l;
+ }
+}
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..f2866d5
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c
@@ -0,0 +1,721 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef KERNEL_PLL
+#ifdef HAVE_SYS_TIMEX_H
+#include <sys/timex.h>
+#else
+#include "sys/timex.h"
+#endif /* not HAVE_SYS_TIMEX_H */
+
+#ifndef NTP_SYSCALLS_LIBC
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#endif /* not NTP_SYSCALLS_LIBC */
+#endif /* KERNEL_PLL */
+
+/*
+ * The loop filter is implemented in slavish adherence to the
+ * specification (Section 5), except that for consistency we
+ * mostly carry the quantities in the same units as appendix G.
+ *
+ * Note that the long values below are the fractional portion of
+ * a long fixed-point value. This limits these values to +-0.5
+ * seconds. When adjustments are capped inside this range (see
+ * CLOCK_MAX_{I,F}) both the clock_adjust and the compliance
+ * registers should be fine. (When the compliance is above 16, it
+ * will at most accumulate 2 ** CLOCK_MULT times the maximum offset,
+ * which means it fits in a s_fp.)
+ *
+ * The skew compensation is a special case. In version 2, it was
+ * kept in ms / 4s (i.e., CLOCK_FREQ was 10). In version 3 (Section 5)
+ * it seems to be 2 ** -16ms / 4s in a s_fp for a maximum of +-125ppm
+ * (stated maximum 100ppm). Since this seems about to change to a
+ * larger range, it will be kept in units of 2 ** -20 (CLOCK_DSCALE)
+ * in an s_fp (mainly because that's nearly the same as parts per
+ * million). Note that this is ``seconds per second'', whereas a
+ * clock adjustment is a 32-bit fraction of a second to be applied
+ * every 2 ** CLOCK_ADJ seconds; to find it, shift the drift right by
+ * (CLOCK_DSCALE - 16 - CLOCK_ADJ). When updating the drift, on the
+ * other hand, the CLOCK_FREQ factor from the spec assumes the value to
+ * be in ``seconds per 4 seconds''; to get our units, CLOCK_ADJ must be
+ * added to the shift.
+ *
+ * Kernel PLL/PPS state machine
+ *
+ * The following state machine is used when the kernel PLL modifications
+ * described in the README.kernel file are present. The initial
+ * configuration routine loop_config() sets up the initial frequency
+ * estimate and tests if the kernel modifications are present. If so and
+ * the PLL mode bit 1 (STA_PLL) of the mode word in the drift file
+ * (ntp.drift) is set, pll_control is set true and the kernel pll is
+ * enabled. If the kernel modifications are present and the PLL mode bit
+ * 2 (STA_PPSFREQ) is set, the kernel PPS frequency discipline is
+ * enabled.
+ *
+ * Each update to a prefer peer sets pps_update true if it survives the
+ * intersection algorithm and its time is within range. The PPS time
+ * discipline is enabled (STA_PPSTIME bit set in the status word) when
+ * pps_update is true and the PPS frequency discipline is enabled. If
+ * the PPS time discipline is enabled and the kernel reports a PPS
+ * signal is present, the pps_control variable is set to the current
+ * time. If the current time is later than pps_control by PPS_MAXAGE
+ * (120 s), this variable is set to zero.
+ *
+ * The pll_enable switch can be set both at configuration time and at
+ * run time using xntpdc. If true, the kernel modifications are active
+ * as described above; if false, the kernel is bypassed entirely (except
+ * for the PPS frequency update, if enabled) and the daemon PLL used
+ * instead.
+ */
+#define RSH_DRIFT_TO_FRAC (CLOCK_DSCALE - 16)
+#define RSH_DRIFT_TO_ADJ (RSH_DRIFT_TO_FRAC - CLOCK_ADJ)
+#define RSH_FRAC_TO_FREQ (CLOCK_FREQ + CLOCK_ADJ - RSH_DRIFT_TO_FRAC)
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+
+/*
+ * Program variables
+ */
+ l_fp last_offset; /* last clock offset */
+ u_long last_time; /* time of last clock update (s) */
+ u_fp clock_stability; /* clock stability (ppm) */
+ s_fp clock_frequency; /* clock frequency error (ppm) */
+ s_fp drift_comp; /* pll frequency (ppm) */
+static long clock_adjust; /* clock adjust (fraction only) */
+static s_fp max_comp; /* max frequency offset (ppm) */
+ int tc_counter; /* poll-adjust counter */
+ int pll_status; /* status bits for kernel pll */
+ int pll_control; /* true if working kernel pll */
+ int pll_enable; /* true if pll enabled */
+ u_long pps_control; /* last pps sample time */
+ int pps_update; /* pps update valid */
+ int fdpps = -1; /* pps file descriptor */
+
+/*
+ * Imported from the ntp_proto module
+ */
+extern s_fp sys_rootdelay; /* root delay */
+extern u_fp sys_rootdispersion; /* root dispersion */
+extern struct peer *sys_peer; /* system peer pointer */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern u_char sys_leap; /* system leap bits */
+extern l_fp sys_refskew; /* accumulated skew since last update */
+extern u_fp sys_maxd; /* max dispersion of survivor list */
+
+/*
+ * Imported from ntp_io.c
+ */
+extern struct interface *loopback_interface;
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from timer module
+ */
+extern u_long current_time; /* like it says, in seconds */
+
+/*
+ * Imported from leap module
+ */
+extern u_char leapbits; /* sanitized leap bits */
+
+#if defined(KERNEL_PLL)
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
+ MOD_STATUS | MOD_TIMECONST)
+extern int sigvec P((int, struct sigvec *, struct sigvec *));
+#ifndef NTP_SYSCALLS_LIBC
+extern int syscall P((int, void *, ...));
+#endif /* no NTP syscalls in libc */
+void pll_trap P((int));
+
+static struct sigvec sigsys; /* current sigvec status */
+static struct sigvec newsigsys; /* new sigvec status */
+#endif /* KERNEL_PLL */
+
+/*
+ * init_loopfilter - initialize loop filter data
+ */
+void
+init_loopfilter()
+{
+ extern u_long tsf_maxslew;
+ u_long tsf_limit;
+
+ /*
+ * Limit for drift_comp, minimum of two values. The first is to
+ * avoid signed overflow, the second to keep within 75% of the
+ * maximum adjustment possible in adj_systime().
+ */
+ max_comp = 0x7fff0000;
+ tsf_limit = ((tsf_maxslew >> 1) + (tsf_maxslew >> 2));
+ if ((max_comp >> RSH_DRIFT_TO_ADJ) > tsf_limit)
+ max_comp = tsf_limit << RSH_DRIFT_TO_ADJ;
+
+ /*
+ * Reset clockworks
+ */
+ drift_comp = 0;
+ clock_adjust = 0;
+ tc_counter = 0;
+ sys_poll = NTP_MINPOLL;
+
+ L_CLR(&last_offset);
+ last_time = 0;
+ clock_frequency = 0;
+ clock_stability = 0;
+ pps_update = pps_control = 0;
+}
+
+/*
+ * local_clock - the NTP logical clock loop filter. Returns 1 if the
+ * clock was stepped, 0 if it was slewed and -1 if it is hopeless.
+ */
+int
+local_clock(fp_offset, peer)
+ l_fp *fp_offset; /* best offset estimate */
+ struct peer *peer; /* from peer - for messages */
+{
+ long offset;
+ long tmp;
+ int return_code;
+ l_fp ftmp;
+ s_fp stmp;
+ u_fp smax;
+ long allan;
+ long interval;
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+ /*
+ * Initialize estimated measurement error and Allan variance
+ * intercept point. The measurement error is assumed the sum of
+ * the peer dispersion plus select dispersion, which seems
+ * reasonable. The Allan variance intercept point is assumed
+ * at MAXSEC for reference clocks and twice that for peer
+ * clocks, which seems cowardly.
+ */
+ if (peer->refclktype)
+ allan = CLOCK_MAXSEC;
+ else
+ allan = CLOCK_MAXSEC << 1;
+
+ if (!last_time)
+ last_time = current_time;
+ interval = (long)(current_time - last_time);
+ clock_adjust = 0;
+ offset = fp_offset->l_f;
+ smax = peer->dispersion + peer->selectdisp;
+ return_code = 0;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "local_clock: offset %s peer %s watch %ld)\n",
+ lfptoa(fp_offset, 6), ntoa(&peer->srcadr),
+ interval);
+#endif
+
+ /*
+ * If the clock is way off, don't tempt fate by correcting it.
+ */
+ ftmp = *fp_offset;
+ if (L_ISNEG(&ftmp))
+ L_NEG(&ftmp);
+ if (ftmp.l_ui >= CLOCK_WAYTOOBIG) {
+ syslog(LOG_ERR,
+ "time error %s is way too large (set clock manually)",
+ lfptoa(fp_offset, 6));
+#ifndef BIGTIMESTEP
+ return (-1);
+#endif /* BIGTIMESTEP */
+
+ /*
+ * If the magnitude of the offset is greater than CLOCK.MAX
+ * (128 ms), reset the time and frequency. We are quite
+ * aggresive here, since the intrinsic clock oscillator
+ * frequency error can be quite large, sometimes over +-300 ppm.
+ * With something this large and a noisy peer, the casual time
+ * updates wander right through the acceptable range, causing
+ * this section to trigger.
+ */
+ } else if (ftmp.l_ui > CLOCK_MAX_I || (ftmp.l_ui == CLOCK_MAX_I
+ && ftmp.l_uf >= CLOCK_MAX_F)) {
+ tc_counter = 0;
+ sys_poll = peer->minpoll;
+
+ /*
+ * Either we are not in synchronization, or we have gone
+ * CLOCK_MINSTEP (900 s) since the last acceptable
+ * update. We step the clock and leave the frequency
+ * alone. Since the clock filter has been reset, the
+ * dispersions will be high upon recovery and the quick-
+ * march code below will trigger to keep the clock in
+ * bounds.
+ */
+ if (sys_leap == LEAP_NOTINSYNC || interval >
+ CLOCK_MINSTEP) {
+ step_systime(fp_offset);
+ syslog(LOG_NOTICE,
+
+ "time reset (%s) %s s",
+#ifdef SLEWALWAYS
+ "slew",
+#else
+ "step",
+#endif
+ lfptoa(fp_offset, 6));
+ return_code = 1;
+
+ /*
+ * The local clock is out of range, but we haven't
+ * allowed enough time for the peer (usually a radio
+ * clock) to recover after a leap second. Pretend we wuz
+ * never here.
+ */
+ } else
+ return (return_code);
+
+ /*
+ * This code segment works when the clock-adjustment code is
+ * implemented in the kernel, which at present is only in the
+ * (modified) SunOS 4.1, Ultrix 4.3 and OSF/1 kernels. In the
+ * case of the DECstation 5000/240 and Alpha AXP, additional
+ * kernel modifications provide a true microsecond clock. We
+ * know the scaling of the frequency variable (s_fp) is the same
+ * as the kernel variable (1 << SHIFT_USEC = 16).
+ */
+#if defined(KERNEL_PLL)
+ } else if (pll_control && pll_enable) {
+ l_fp pps_offset;
+ u_fp pps_dispersion;
+
+ /*
+ * We initialize the structure for the ntp_adjtime()
+ * system call. We have to convert everything to
+ * microseconds first. Afterwards, remember the
+ * frequency offset for the drift file.
+ */
+ memset((char *)&ntv, 0, sizeof ntv);
+ ntv.modes = MOD_BITS;
+ if (offset >= 0) {
+ TSFTOTVU(offset, ntv.offset);
+ } else {
+ TSFTOTVU(-offset, ntv.offset);
+ ntv.offset = -ntv.offset;
+ }
+ TSFTOTVU(sys_rootdispersion + sys_rootdelay / 2,
+ ntv.maxerror);
+ TSFTOTVU(sys_rootdispersion, ntv.esterror);
+ ntv.status = pll_status & (STA_PLL | STA_PPSFREQ);
+ if (pps_update && pll_status & STA_PPSFREQ)
+ ntv.status |= STA_PPSTIME;
+ if (sys_leap & LEAP_ADDSECOND &&
+ sys_leap & LEAP_DELSECOND)
+ ntv.status |= STA_UNSYNC;
+ else if (sys_leap & LEAP_ADDSECOND)
+ ntv.status |= STA_INS;
+ else if (sys_leap & LEAP_DELSECOND)
+ ntv.status |= STA_DEL;
+ ntv.constant = min(peer->ppoll, sys_poll) - NTP_MINPOLL;
+ (void)ntp_adjtime(&ntv);
+ drift_comp = ntv.freq;
+ pll_status = ntv.status;
+
+ /*
+ * If the kernel pps discipline is working, monitor its
+ * performance.
+ */
+ if (pll_status & STA_PPSTIME && pll_status &
+ STA_PPSSIGNAL && ntv.shift) {
+ if (ntv.offset >= 0)
+ TVUTOTSF(ntv.offset, offset);
+ else {
+ TVUTOTSF(-ntv.offset, offset);
+ offset = -offset;
+ }
+ L_CLR(&pps_offset);
+ L_ADDF(&pps_offset, offset);
+ TVUTOTSF(ntv.jitter, tmp);
+ pps_dispersion = (tmp >> 16) & 0xffff;
+ if (!pps_control)
+ syslog(LOG_INFO,
+ "kernel pps sync enabled");
+ pps_control = current_time;
+ record_peer_stats(&loopback_interface->sin,
+ ctlsysstatus(), fp_offset, 0,
+ pps_dispersion);
+ }
+#endif /* KERNEL_PLL */
+
+ /*
+ * If the dispersion exceeds 128 ms, we need to quick-march it
+ * to nominal zero offset and wait for the next update. This is
+ * necessary when the intrinsic frequency error is large and the
+ * clock has drifted during the interval the clock filter was
+ * stabilizing. Note that, if unsynchronized, the dispersion is
+ * always greater than 128 ms, so we don't need a check for
+ * that.
+ */
+ } else if (smax > CLOCK_MAX_FP) {
+ clock_adjust = offset;
+
+ /*
+ * If the dispersion has increased substantially over the
+ * previous value, we have a spike which probably should be
+ * suppressed. A factor of eight has been found reasonable by
+ * simulation.
+ */
+ } else if (smax > sys_maxd << 3) {
+ return (0);
+
+ /*
+ * If the interval between corrections is less than the Allan
+ * variance intercept point, we use a phase-lock loop to compute
+ * new values of time and frequency. The bandwidth is controlled
+ * by the time constant, which is adjusted in response to the
+ * phase error and dispersion. Note the frequency is not changed
+ * if the local clock driver is in control.
+ */
+ } else if (interval < allan) {
+ int time_constant = min(peer->ppoll, sys_poll) -
+ NTP_MINPOLL;
+ int ltmp = interval;
+
+ if (offset < 0)
+ clock_adjust = -(-offset >> time_constant);
+ else
+ clock_adjust = offset >> time_constant;
+ if (interval && !(peer->refclktype ==
+ REFCLK_LOCALCLOCK)) {
+ tmp = peer->maxpoll;
+ while (ltmp < (1 << peer->maxpoll)) {
+ tmp--;
+ ltmp <<= 1;
+ }
+ tmp = (RSH_FRAC_TO_FREQ - tmp) + time_constant +
+ time_constant;
+ if (offset < 0)
+ tmp = -(-offset >> tmp);
+ else
+ tmp = offset >> tmp;
+ drift_comp += tmp;
+ }
+
+ /*
+ * If the interval between corrections is greater than the Allan
+ * variance intercept point, we use a hybrid frequency-lock loop
+ * to compute new values of phase and frequency. The following
+ * code is based on ideas suggested by Judah Levine of NIST and
+ * used in his "lockclock" implementation of ACTS. The magic
+ * factor of 4 in the left shift is to convert from s_fp to ppm.
+ */
+ } else {
+ clock_adjust = offset;
+ stmp = (offset / interval) << 4;
+ if (stmp < 0)
+ drift_comp -= -stmp >> CLOCK_G;
+ else
+ drift_comp += stmp >> CLOCK_G;
+ }
+
+ /*
+ * As a sanity check, we clamp the frequency not to exceed the
+ * slew rate of the stock Unix adjtime() system call. Finally,
+ * do a little housekeeping.
+ */
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ else if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+ if (interval > (1 << (peer->minpoll - 1))) {
+
+ /*
+ * Determine when to adjust the poll interval. We do
+ * this regardless of what source controls the loop,
+ * since we might flap back and forth between sources.
+ */
+ stmp = LFPTOFP(fp_offset);
+ if (stmp < 0)
+ stmp = -stmp;
+ if (stmp > smax) {
+ tc_counter -= (int)sys_poll << 1;
+ if (tc_counter < -CLOCK_LIMIT) {
+ tc_counter = -CLOCK_LIMIT;
+ if (sys_poll > peer->minpoll) {
+ sys_poll--;
+ tc_counter = 0;
+ }
+ }
+ } else {
+ tc_counter += (int)sys_poll;
+ if (tc_counter > CLOCK_LIMIT) {
+ tc_counter = CLOCK_LIMIT;
+ if (sys_poll < peer->maxpoll) {
+ sys_poll++;
+ tc_counter = 0;
+ }
+ }
+ }
+
+ /*
+ * Calculate the frequency offset and frequency
+ * stability. These are useful for performance
+ * monitoring, but do not affect the loop variables. The
+ * results are scaled as a s_fp in ppm, because we know
+ * more than we should.
+ */
+ ftmp = *fp_offset;
+ L_SUB(&ftmp, &last_offset);
+ clock_frequency = (LFPTOFP(&ftmp) / interval) << 20;
+ if (clock_frequency < -max_comp)
+ clock_frequency = -max_comp;
+ else if (clock_frequency > max_comp)
+ clock_frequency = max_comp;
+ stmp = clock_frequency;
+ if (stmp < 0)
+ stmp = -stmp;
+ stmp -= clock_stability;
+ if (stmp < 0)
+ clock_stability -= -stmp >> NTP_MAXD;
+ else
+ clock_stability += stmp >> NTP_MAXD;
+ }
+ last_offset = *fp_offset;
+ last_time = current_time;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "local_clock: phase %s freq %s err %s allan %ld poll %d\n",
+ mfptoa((clock_adjust < 0 ? -1 : 0), clock_adjust,
+ 6), fptoa(drift_comp, 3), fptoa(smax, 6), allan,
+ sys_poll);
+#endif /* DEBUG */
+
+ (void) record_loop_stats(fp_offset, drift_comp, sys_poll);
+
+ /*
+ * Whew. I've had enough.
+ */
+ return (return_code);
+}
+
+
+/*
+ * adj_host_clock - Called every 1 << CLOCK_ADJ seconds to update host
+ * clock
+ */
+void
+adj_host_clock()
+{
+ register long adjustment;
+ l_fp offset;
+
+ /*
+ * Update the dispersion since the last update. Don't allow
+ * frequency measurements over periods longer than NTP_MAXAGE
+ * (86400 s = one day).
+ */
+ if (current_time - last_time > NTP_MAXAGE)
+ last_time = 0;
+ L_ADDUF(&sys_refskew, NTP_SKEWINC);
+
+ /*
+ * Declare PPS kernel unsync if the pps signal has been heard
+ * during the last few minutes.
+ */
+ if (pps_control && current_time - pps_control > PPS_MAXAGE) {
+ if (pps_control)
+ syslog(LOG_INFO, "kernel pps sync disabled");
+ pps_control = 0;
+ }
+
+ /*
+ * If the phase-lock loop is not implemented in the kernel, we
+ * do it the hard way using incremental adjustments and the
+ * adjtime() system call.
+ */
+ if (pll_control && pll_enable)
+ return;
+ adjustment = clock_adjust;
+ if (adjustment < 0)
+ adjustment = -(-adjustment >> CLOCK_PHASE);
+ else
+ adjustment >>= CLOCK_PHASE;
+
+ clock_adjust -= adjustment;
+ if (drift_comp < 0)
+ adjustment -= -drift_comp >> RSH_DRIFT_TO_ADJ;
+ else
+ adjustment += drift_comp >> RSH_DRIFT_TO_ADJ;
+
+ /*
+ * Intricate wrinkle. If the local clock driver is in use and
+ * selected for synchronization, somebody else may be tinker the
+ * adjtime() syscall. In this case we have to avoid calling
+ * adjtime(), since that may truncate the other guy's requests.
+ * That means the local clock fudge time and frequency
+ * adjustments don't work in that case. Caveat empty.
+ */
+ if (sys_peer) {
+ if (sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ sys_peer->flags & FLAG_PREFER)
+ return;
+ }
+ L_CLR(&offset);
+ L_ADDF(&offset, adjustment);
+ adj_systime(&offset);
+}
+
+
+/*
+ * adj_frequency - adjust local clock frequency
+ */
+void
+adj_frequency(freq)
+ s_fp freq; /* frequency (ppm) */
+{
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+ /*
+ * This routine adjusts the frequency offset. It is used by the
+ * local clock driver to adjust frequency when no external
+ * discipline source is available and by the acts driver when
+ * the interval between updates is greater than 1 <<
+ * NTP_MAXPOLL. Note that the maximum offset is limited by
+ * max_comp when the daemon pll is used, but the maximum may be
+ * different when the kernel pll is used.
+ */
+ drift_comp += freq;
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ else if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+#if defined(KERNEL_PLL)
+ /*
+ * If the phase-lock code is implemented in the kernel, set the
+ * kernel frequency as well, but be sure to set drift_comp to
+ * the actual frequency.
+ */
+ if (!(pll_control && pll_enable))
+ return;
+ memset((char *)&ntv, 0, sizeof ntv);
+ ntv.modes = MOD_FREQUENCY;
+ ntv.freq = freq + drift_comp;
+ (void)ntp_adjtime(&ntv);
+ drift_comp = ntv.freq;
+#endif /* KERNEL_PLL */
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ */
+void
+loop_config(item, lfp_value, int_value)
+ int item;
+ l_fp *lfp_value;
+ int int_value;
+{
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+#ifdef DEBUG
+ if (debug) {
+ printf("loop_config %d %s %x\n", item,
+ lfptoa(lfp_value, 3), int_value);
+ }
+#endif
+ switch (item) {
+
+ case LOOP_DRIFTCOMP:
+ drift_comp = LFPTOFP(lfp_value);
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+
+#if defined(KERNEL_PLL)
+ /*
+ * If the phase-lock code is implemented in the kernel,
+ * give the time_constant and saved frequency offset to
+ * the kernel. If not, no harm is done. We do this
+ * whether or not the use of the kernel mods is
+ * requested, in order to clear out the trash from
+ * possible prior customers.
+ */
+ memset((char *)&ntv, 0, sizeof ntv);
+ pll_status = int_value & (STA_PLL | STA_PPSFREQ);
+ if (pll_status & STA_PLL)
+ pll_control = 1;
+ else
+ pll_control = 0;
+ ntv.modes = MOD_BITS | MOD_FREQUENCY;
+ if (pll_status) {
+ ntv.freq = drift_comp;
+ ntv.maxerror = NTP_MAXDISPERSE;
+ ntv.esterror = NTP_MAXDISPERSE;
+ ntv.status = pll_status | STA_UNSYNC;
+ ntv.constant = sys_poll - NTP_MINPOLL;
+ }
+ newsigsys.sv_handler = pll_trap;
+ newsigsys.sv_mask = 0;
+ newsigsys.sv_flags = 0;
+ if ((sigvec(SIGSYS, &newsigsys, &sigsys)))
+ syslog(LOG_ERR,
+ "sigvec() fails to save SIGSYS trap: %m");
+ (void)ntp_adjtime(&ntv);
+ if ((sigvec(SIGSYS, &sigsys,
+ (struct sigvec *)NULL)))
+ syslog(LOG_ERR,
+ "sigvec() fails to restore SIGSYS trap: %m");
+ if (pll_control)
+ syslog(LOG_NOTICE,
+ "using kernel phase-lock loop %04x",
+ ntv.status);
+ else
+ syslog(LOG_NOTICE,
+ "using xntpd phase-lock loop");
+#endif /* KERNEL_PLL */
+ break;
+
+ default:
+ /* sigh */
+ break;
+ }
+}
+
+
+#if defined(KERNEL_PLL)
+/*
+ * _trap - trap processor for undefined syscalls
+ *
+ * This nugget is called by the kernel when the SYS_ntp_adjtime()
+ * syscall bombs because the silly thing has not been implemented in
+ * the kernel. In this case the phase-lock loop is emulated by
+ * the stock adjtime() syscall and a lot of indelicate abuse.
+ */
+RETSIGTYPE
+pll_trap(int sig)
+{
+ pll_control = 0;
+}
+#endif /* KERNEL_PLL */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_monitor.c b/usr.sbin/xntpd/xntpd/ntp_monitor.c
new file mode 100644
index 0000000..e6a1c4f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_monitor.c
@@ -0,0 +1,349 @@
+/*
+ * ntp_monitor.c - monitor who is using the xntpd server
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * I'm still not sure I like what I've done here. It certainly consumes
+ * memory like it is going out of style, and also may not be as low
+ * overhead as I'd imagined.
+ *
+ * Anyway, we record statistics based on source address, mode and version
+ * (for now, anyway. Check the code). The receive procedure calls us with
+ * the incoming rbufp before it does anything else.
+ *
+ * Each entry is doubly linked into two lists, a hash table and a
+ * most-recently-used list. When a packet arrives it is looked up
+ * in the hash table. If found, the statistics are updated and the
+ * entry relinked at the head of the MRU list. If not found, a new
+ * entry is allocated, initialized and linked into both the hash
+ * table and at the head of the MRU list.
+ *
+ * Memory is usually allocated by grabbing a big chunk of new memory
+ * and cutting it up into littler pieces. The exception to this when we
+ * hit the memory limit. Then we free memory by grabbing entries off
+ * the tail for the MRU list, unlinking from the hash table, and
+ * reinitializing.
+ *
+ * trimmed back memory consumption ... jdg 8/94
+ */
+
+/*
+ * Limits on the number of structures allocated. This limit is picked
+ * with the illicit knowlege that we can only return somewhat less
+ * than 8K bytes in a mode 7 response packet, and that each structure
+ * will require about 20 bytes of space in the response.
+ *
+ * ... I don't believe the above is true anymore ... jdg
+ */
+#ifndef MAXMONMEM
+#define MAXMONMEM 600 /* we allocate up to 400 structures */
+#endif
+#ifndef MONMEMINC
+#define MONMEMINC 40 /* allocate them 40 at a time */
+#endif
+
+/*
+ * Hashing stuff
+ */
+#define MON_HASH_SIZE 128
+#define MON_HASH_MASK (MON_HASH_SIZE-1)
+#define MON_HASH(addr) ((int)(ntohl((addr)) & MON_HASH_MASK))
+
+/*
+ * Pointers to the hash table, the MRU list and the count table. Memory
+ * for the hash and count tables is only allocated if monitoring is turned on.
+ */
+static struct mon_data *mon_hash[MON_HASH_SIZE]; /* array of list ptrs */
+ struct mon_data mon_mru_list;
+ struct mon_data mon_fifo_list;
+/*
+ * List of free structures structures, and counters of free and total
+ * structures. The free structures are linked with the hash_next field.
+ */
+static struct mon_data *mon_free; /* the free list or null if none */
+
+static int mon_total_mem; /* total number of structures allocated */
+static int mon_mem_increments; /* number of times we've called malloc() */
+
+/*
+ * Initialization state. We may be monitoring, we may not. If
+ * we aren't, we may not even have allocated any memory yet.
+ */
+ int mon_enabled;
+static int mon_have_memory;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+static void mon_getmoremem P((void));
+static void remove_from_hash P((struct mon_data *));
+
+/*
+ * init_mon - initialize monitoring global data
+ */
+void
+init_mon()
+{
+ /*
+ * Don't do much of anything here. We don't allocate memory
+ * until someone explicitly starts us.
+ */
+ mon_enabled = MON_OFF;
+ mon_have_memory = 0;
+
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ memset((char *)&mon_hash[0], 0, sizeof mon_hash);
+ memset((char *)&mon_mru_list, 0, sizeof mon_mru_list);
+ memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_list);
+}
+
+
+/*
+ * mon_start - start up the monitoring software
+ */
+void
+mon_start(mode)
+ int mode;
+{
+
+ if (mon_enabled != MON_OFF) {
+ mon_enabled |= mode;
+ return;
+ }
+ if (mode == MON_OFF)
+ return; /* Ooops.. */
+
+ if (!mon_have_memory) {
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ mon_getmoremem();
+ mon_have_memory = 1;
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+
+ mon_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_list;
+
+ mon_enabled = mode;
+}
+
+
+/*
+ * mon_stop - stop the monitoring software
+ */
+void
+mon_stop(mode)
+ int mode;
+{
+ register struct mon_data *md, *md_next;
+ register int i;
+
+ if (mon_enabled == MON_OFF)
+ return;
+ if ((mon_enabled & mode) == 0 || mode == MON_OFF)
+ return;
+
+ mon_enabled &= ~mode;
+ if (mon_enabled != MON_OFF)
+ return;
+
+ /*
+ * Put everything back on the free list
+ */
+ for (i = 0; i < MON_HASH_SIZE; i++) {
+ md = mon_hash[i]; /* get next list */
+ mon_hash[i] = NULL; /* zero the list head */
+ while (md != NULL) {
+ md_next = md->hash_next;
+ md->hash_next = mon_free;
+ mon_free = md;
+ md = md_next;
+ }
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+
+ mon_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_list;
+}
+
+
+/*
+ * monitor - record stats about this packet
+ */
+void
+monitor(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pkt *pkt;
+ register struct mon_data *md;
+ register u_long netnum;
+ register int hash;
+ register int mode;
+
+ if (mon_enabled == MON_OFF)
+ return;
+
+ pkt = &rbufp->recv_pkt;
+ netnum = NSRCADR(&rbufp->recv_srcadr);
+ hash = MON_HASH(netnum);
+ mode = PKT_MODE(pkt->li_vn_mode);
+
+ md = mon_hash[hash];
+ while (md != NULL) {
+ if (md->rmtadr == netnum &&
+ /* ?? md->interface == rbufp->dstadr && ?? */
+ md->mode == (u_char)mode) {
+ md->lasttime = current_time;
+ md->count++;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+
+ /*
+ * Shuffle him to the head of the
+ * mru list. What a crock.
+ */
+ md->mru_next->mru_prev = md->mru_prev;
+ md->mru_prev->mru_next = md->mru_next;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+
+ return;
+ }
+ md = md->hash_next;
+ }
+
+ /*
+ * If we got here, this is the first we've heard of this
+ * guy. Get him some memory, either from the free list
+ * or from the tail of the MRU list.
+ */
+ if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
+ /*
+ * Get it from MRU list
+ */
+ md = mon_mru_list.mru_prev;
+ md->mru_prev->mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = md->mru_prev;
+
+ remove_from_hash(md);
+
+ /*
+ * Get it from FIFO list
+ */
+ md->fifo_prev->fifo_next = md->fifo_next;
+ md->fifo_next->fifo_prev = md->fifo_prev;
+
+ } else {
+ if (mon_free == NULL) /* if free list empty */
+ mon_getmoremem(); /* then get more */
+ md = mon_free;
+ mon_free = md->hash_next;
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ md->lasttime = md->firsttime = current_time;
+ md->lastdrop = 0;
+ md->count = 1;
+ md->rmtadr = netnum;
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->interface = rbufp->dstadr;
+ md->cast_flags = ((rbufp->dstadr->flags & INT_MULTICAST) &&
+ rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
+ md->interface->bfd ? MDF_BCAST : MDF_UCAST;
+
+ /*
+ * Drop him into front of the hash table.
+ * Also put him on top of the MRU list
+ * and at bottom of FIFO list
+ */
+
+ md->hash_next = mon_hash[hash];
+ mon_hash[hash] = md;
+
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+
+ md->fifo_prev = mon_fifo_list.fifo_prev;
+ md->fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev->fifo_next = md;
+ mon_fifo_list.fifo_prev = md;
+}
+
+
+/*
+ * mon_getmoremem - get more memory and put it on the free list
+ */
+static void
+mon_getmoremem()
+{
+ register struct mon_data *md;
+ register int i;
+ struct mon_data *freedata; /* 'old' free list (null) */
+
+ md = (struct mon_data *)emalloc(MONMEMINC * sizeof(struct mon_data));
+ freedata = mon_free;
+ mon_free = md;
+
+ for (i = 0; i < (MONMEMINC-1); i++) {
+ md->hash_next = (md + 1);
+ md++;
+ }
+
+ /*
+ * md now points at the last. Link in the rest of the chain.
+ */
+ md->hash_next = freedata;
+
+ mon_total_mem += MONMEMINC;
+ mon_mem_increments++;
+}
+
+static void
+remove_from_hash(md)
+struct mon_data *md;
+{ register int hash;
+ register struct mon_data *md_prev;
+
+ hash = MON_HASH(md->rmtadr);
+ if (mon_hash[hash] == md) {
+ mon_hash[hash] = md->hash_next;
+ } else {
+ md_prev = mon_hash[hash];
+ while (md_prev->hash_next != md) {
+ md_prev = md_prev->hash_next;
+ if (md_prev == NULL) {
+ /* logic error */
+ return;
+ }
+ }
+ md_prev->hash_next = md->hash_next;
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_peer.c b/usr.sbin/xntpd/xntpd/ntp_peer.c
new file mode 100644
index 0000000..f777734
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_peer.c
@@ -0,0 +1,667 @@
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of the peer hash table. The two main entry
+ * points are findpeer(), which looks for corresponding peer data
+ * in the peer list, newpeer(), which allocates a new peer structure
+ * and adds it to the list, and unpeer(), which demobilizes the association
+ * and deallocates the structure.
+ */
+
+/*
+ * The peer hash table (imported by the protocol module).
+ */
+struct peer *peer_hash[HASH_SIZE];
+int peer_hash_count[HASH_SIZE]; /* count of peers in each bucket */
+
+/*
+ * The association ID hash table. Used for lookups by association ID
+ */
+struct peer *assoc_hash[HASH_SIZE];
+int assoc_hash_count[HASH_SIZE];
+
+/*
+ * The free list. Clean structures only, please.
+ */
+struct peer *peer_free;
+int peer_free_count;
+
+/*
+ * Association ID. We initialize this value randomly, the assign a new
+ * value every time the peer structure is incremented.
+ */
+u_short current_association_ID;
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 15 /* initialize space for 15 peers */
+#define INC_PEER_ALLOC 5 /* when we run out, add 5 more */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters were zeroed */
+u_long findpeer_calls; /* number of calls to findpeer */
+u_long assocpeer_calls; /* number of calls to findpeerbyassoc */
+u_long peer_allocations; /* number of allocations from the free list */
+u_long peer_demobilizations; /* number of structs freed to free list */
+int total_peer_structs; /* number of peer structs in circulation */
+
+/*
+ * default interface. Imported from the io module.
+ */
+extern struct interface *any_interface;
+
+/*
+ * Timer queue and current time. Imported from the timer module.
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Our initial allocation of peer space
+ */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC];
+
+/*
+ * Initialization data. When configuring peers at initialization time,
+ * we try to get their poll update timers initialized to different values
+ * to prevent us from sending big clumps of data all at once.
+ */
+u_long init_peer_starttime;
+extern int initializing;
+extern int debug;
+
+static void getmorepeermem P((void));
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer()
+{
+ register int i;
+
+ /*
+ * Clear hash table and counters.
+ */
+ for (i = 0; i < HASH_SIZE; i++) {
+ peer_hash[i] = 0;
+ peer_hash_count[i] = 0;
+ assoc_hash[i] = 0;
+ assoc_hash_count[i] = 0;
+ }
+
+ /*
+ * Clear stat counters
+ */
+ findpeer_calls = peer_allocations = 0;
+ assocpeer_calls = peer_demobilizations = 0;
+
+ /*
+ * Initialization counter.
+ */
+ init_peer_starttime = 0;
+
+ /*
+ * Initialize peer memory.
+ */
+ peer_free = 0;
+ for (i = 0; i < INIT_PEER_ALLOC; i++) {
+ init_peer_alloc[i].next = peer_free;
+ peer_free = &init_peer_alloc[i];
+ }
+ total_peer_structs = INIT_PEER_ALLOC;
+ peer_free_count = INIT_PEER_ALLOC;
+
+ /*
+ * Initialize our first association ID
+ */
+ current_association_ID = (u_short)ranp2(16);
+ if (current_association_ID == 0)
+ current_association_ID = 1;
+}
+
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem()
+{
+ register int i;
+ register struct peer *peer;
+
+ peer = (struct peer *)emalloc(INC_PEER_ALLOC*sizeof(struct peer));
+ for (i = 0; i < INC_PEER_ALLOC; i++) {
+ peer->next = peer_free;
+ peer_free = peer;
+ peer++;
+ }
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+
+/*
+ * findexistingpeer - return a pointer to a peer in the hash table
+ */
+struct peer *
+findexistingpeer(addr, start_peer)
+ struct sockaddr_in *addr;
+ struct peer *start_peer;
+{
+ register struct peer *peer;
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ */
+ if (start_peer == 0)
+ peer = peer_hash[HASH_ADDR(addr)];
+ else
+ peer = start_peer->next;
+
+ while (peer != 0) {
+ if (NSRCADR(addr) == NSRCADR(&peer->srcadr)
+ && NSRCPORT(addr) == NSRCPORT(&peer->srcadr))
+ return peer;
+ peer = peer->next;
+ }
+
+ return (struct peer *)0;
+}
+
+
+/*
+ * findpeer - find and return a peer in the hash table.
+ */
+struct peer *
+findpeer(srcadr, dstadr, fd)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int fd;
+{
+ register struct peer *any_inter_peer;
+ register struct peer *peer;
+ register struct peer *best = (struct peer *) 0;
+ int hash;
+
+ findpeer_calls++;
+
+ any_inter_peer = 0;
+ hash = HASH_ADDR(srcadr);
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next) {
+ if (NSRCADR(srcadr) == NSRCADR(&peer->srcadr)
+ && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
+ if (peer->dstadr == dstadr) {
+ int rfd = (peer->cast_flags & MDF_BCAST) ?
+ dstadr->bfd : dstadr->fd;
+
+ if (rfd == fd)
+ return peer; /* got it! */
+ best = peer;
+ }
+ if (peer->dstadr == any_interface) {
+
+ /*
+ * We shouldn't have more than one
+ * instance of the peer in the table,
+ * but I don't trust this. Save this
+ * one for later and continue search.
+ */
+ if (any_inter_peer == 0)
+ any_inter_peer = peer;
+ else
+ syslog(LOG_ERR,
+ "two instances of default interface for %s in hash table",
+ ntoa(srcadr));
+ }
+
+ /*
+ * Multicast hacks to determine peer when a
+ * packet arrives and there exists an assoc.
+ * with src in client/server mode
+ */
+ if (((dstadr == any_interface) || (peer->cast_flags &
+ MDF_MCAST)) && peer->flags & FLAG_MCAST2)
+ return peer;
+ }
+ }
+
+ if(best) {
+ return best;
+ }
+
+ /*
+ * If we didn't find the specific peer but found a wild card,
+ * modify the interface and return him.
+ */
+ if (any_inter_peer != 0) {
+ any_inter_peer->dstadr = dstadr;
+ return any_inter_peer;
+ }
+
+ /*
+ * Out of luck. Return 0.
+ */
+ return (struct peer *)0;
+}
+
+/*
+ * findpeerbyassocid - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(assoc)
+ int assoc;
+{
+ register struct peer *peer;
+ int hash;
+
+ assocpeer_calls++;
+
+ hash = assoc & HASH_MASK;
+ for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) {
+ if ((u_short)assoc == peer->associd)
+ return peer; /* got it! */
+ }
+
+ /*
+ * Out of luck. Return 0.
+ */
+ return (struct peer *)0;
+}
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(peer_to_remove)
+ struct peer *peer_to_remove;
+{
+ int hash;
+
+ hash = HASH_ADDR(&peer_to_remove->srcadr);
+ peer_hash_count[hash]--;
+ peer_demobilizations++;
+
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (peer_to_remove->flags & FLAG_REFCLOCK)
+ refclock_unpeer(peer_to_remove);
+#endif
+
+ if (peer_hash[hash] == peer_to_remove)
+ peer_hash[hash] = peer_to_remove->next;
+ else {
+ register struct peer *peer;
+
+ peer = peer_hash[hash];
+ while (peer != 0 && peer->next != peer_to_remove)
+ peer = peer->next;
+
+ if (peer == 0) {
+ peer_hash_count[hash]++;
+ syslog(LOG_ERR, "peer struct for %s not in table!",
+ ntoa(&peer->srcadr));
+ } else {
+ peer->next = peer_to_remove->next;
+ }
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = peer_to_remove->associd & HASH_MASK;
+ assoc_hash_count[hash]--;
+ if (assoc_hash[hash] == peer_to_remove)
+ assoc_hash[hash] = peer_to_remove->ass_next;
+ else {
+ register struct peer *peer;
+
+ peer = assoc_hash[hash];
+ while (peer != 0 && peer->ass_next != peer_to_remove)
+ peer = peer->ass_next;
+
+ if (peer == 0) {
+ assoc_hash_count[hash]++;
+ syslog(LOG_ERR,
+ "peer struct for %s not in association table!",
+ ntoa(&peer->srcadr));
+ } else {
+ peer->ass_next = peer_to_remove->ass_next;
+ }
+ }
+
+ TIMER_DEQUEUE(&peer_to_remove->event_timer);
+
+ peer_to_remove->next = peer_free;
+ peer_free = peer_to_remove;
+ peer_free_count++;
+}
+
+
+/*
+ * peer_config - configure a new peer
+ */
+struct peer *
+peer_config(srcadr, dstadr, hmode, version, minpoll, maxpoll, flags, ttl, key)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int hmode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long key;
+{
+ register struct peer *peer;
+
+#ifdef DEBUG
+ if (debug)
+ printf("peer_config: addr %s mode %d version %d minpoll %d maxpoll %d flags %d ttl %d key %lu\n",
+ ntoa(srcadr), hmode, version, minpoll, maxpoll, flags,
+ ttl, key);
+#endif
+ /*
+ * See if we have this guy in the tables already. If
+ * so just mark him configured.
+ */
+ peer = findexistingpeer(srcadr, (struct peer *)0);
+ if (dstadr != 0) {
+ while (peer != 0) {
+ if (peer->dstadr == dstadr)
+ break;
+ peer = findexistingpeer(srcadr, peer);
+ }
+ }
+
+ /*
+ * Torque the flags to make sure they're valid
+ */
+ flags &= (FLAG_AUTHENABLE|FLAG_PREFER);
+
+ /*
+ * If we found one, just change his mode and mark him configured.
+ */
+ if (peer != 0) {
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->flags = ((u_char)(flags | FLAG_CONFIG)) |
+ (peer->flags & FLAG_REFCLOCK);
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) ? MDF_MCAST : MDF_BCAST : MDF_UCAST;
+ peer->ttl = (u_char)ttl;
+ peer->keyid = key;
+ return peer;
+ }
+
+ /*
+ * If we're here this guy is unknown to us. Make a new peer
+ * structure for him.
+ */
+ peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
+ ttl, key);
+ if (peer != 0)
+ peer->flags |= (u_char)(flags|FLAG_CONFIG);
+ return peer;
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int hmode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ u_long key;
+{
+ register struct peer *peer;
+ register int i;
+
+ /*
+ * Some dirt here. Some of the initialization requires
+ * knowlege of our system state.
+ */
+ extern s_fp sys_bdelay;
+ extern long sys_clock;
+
+ if (peer_free_count == 0)
+ getmorepeermem();
+
+ peer = peer_free;
+ peer_free = peer->next;
+ peer_free_count--;
+
+ /*
+ * Initialize the structure. This stuff is sort of part of
+ * the receive procedure and part of the clear procedure rolled
+ * into one.
+ *
+ * Zero the whole thing for now. We might be pickier later.
+ */
+ memset((char *)peer, 0, sizeof(struct peer));
+
+ peer->srcadr = *srcadr;
+ if (dstadr != 0)
+ peer->dstadr = dstadr;
+ else if (hmode == MODE_BROADCAST)
+ peer->dstadr = findbcastinter(srcadr);
+ else
+ peer->dstadr = any_interface;
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) ? MDF_MCAST : MDF_BCAST :
+ (hmode == MODE_BCLIENT || hmode == MODE_MCLIENT) ?
+ (peer->dstadr->flags & INT_MULTICAST) ? MDF_MCAST : MDF_BCAST :
+ MDF_UCAST;
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->ttl = ttl;
+ peer->keyid = key;
+ peer->estbdelay = sys_bdelay;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->precision = DEFPRECISION;
+ peer->dispersion = NTP_MAXDISPERSE;
+ peer->stratum = STRATUM_UNSPEC;
+ peer->update = sys_clock;
+
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = i;
+ peer->filter_error[i] = NTP_MAXDISPERSE;
+ }
+
+ /*
+ * Assign him an association ID and increment the system variable
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ peer->next = peer_free;
+ peer_free = peer;
+ peer_free_count++;
+ return 0;
+ }
+ } else {
+#endif
+ /*
+ * Set up timer. If initializing, just make sure we start polling
+ * in different 4 second intervals.
+ */
+ peer->event_timer.peer = peer;
+ peer->event_timer.event_handler = transmit;
+
+ if (initializing) {
+ init_peer_starttime += (1 << EVENT_TIMEOUT);
+ if (init_peer_starttime >= (1 << peer->minpoll))
+ init_peer_starttime = (1 << EVENT_TIMEOUT);
+ peer->event_timer.event_time = init_peer_starttime;
+ } else {
+ /*
+ * First expiry is set to eight seconds from now.
+ */
+ peer->event_timer.event_time
+ = (1 << (peer->minpoll - 1)) + current_time;
+ }
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+#ifdef REFCLOCK
+ }
+#endif
+
+ /*
+ * Put him in the hash tables.
+ */
+ i = HASH_ADDR(&peer->srcadr);
+ peer->next = peer_hash[i];
+ peer_hash[i] = peer;
+ peer_hash_count[i]++;
+
+ i = peer->associd & HASH_MASK;
+ peer->ass_next = assoc_hash[i];
+ assoc_hash[i] = peer;
+ assoc_hash_count[i]++;
+
+ return peer;
+}
+
+
+/*
+ * peer_unconfig - remove the configuration bit from a peer
+ */
+int
+peer_unconfig(srcadr, dstadr)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+{
+ register struct peer *peer;
+ int num_found;
+
+ num_found = 0;
+ peer = findexistingpeer(srcadr, (struct peer *)0);
+ while (peer != 0) {
+ if (peer->flags & FLAG_CONFIG
+ && (dstadr == 0 || peer->dstadr == dstadr)) {
+ num_found++;
+ /*
+ * Tricky stuff here. If the peer is polling us
+ * in active mode, turn off the configuration bit
+ * and make the mode passive. This allows us to
+ * avoid dumping a lot of history for peers we
+ * might choose to keep track of in passive mode.
+ * The protocol will eventually terminate undesirables
+ * on its own.
+ */
+ if (peer->hmode == MODE_ACTIVE
+ && peer->pmode == MODE_ACTIVE) {
+ peer->hmode = MODE_PASSIVE;
+ peer->flags &= ~FLAG_CONFIG;
+ } else {
+ unpeer(peer);
+ peer = 0;
+ }
+ }
+ peer = findexistingpeer(srcadr, peer);
+ }
+ return num_found;
+}
+
+
+/*
+ * peer_clr_stats - clear peer module stat counters
+ */
+void
+peer_clr_stats()
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+/*
+ * peer_reset - reset stat counters in a peer structure
+ */
+void
+peer_reset(peer)
+ struct peer *peer;
+{
+ if (peer == 0)
+ return;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->selbroken = 0;
+ peer->seltooold = 0;
+ peer->timereset = current_time;
+}
+
+
+/*
+ * peer_all_reset - reset all peer stat counters
+ */
+void
+peer_all_reset()
+{
+ struct peer *peer;
+ int hash;
+
+ for (hash = 0; hash < HASH_SIZE; hash++)
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next)
+ peer_reset(peer);
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_proto.c b/usr.sbin/xntpd/xntpd/ntp_proto.c
new file mode 100644
index 0000000..fb81206
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_proto.c
@@ -0,0 +1,2268 @@
+/*
+ * ntp_proto.c - NTP version 3 protocol machinery
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+/*
+ * System variables are declared here. See Section 3.2 of the
+ * specification.
+ */
+u_char sys_leap; /* system leap indicator */
+u_char sys_stratum; /* stratum of system */
+s_char sys_precision; /* local clock precision */
+s_fp sys_rootdelay; /* distance to current sync source */
+u_fp sys_rootdispersion; /* dispersion of system clock */
+u_long sys_refid; /* reference source for local clock */
+l_fp sys_offset; /* combined offset from clock_select */
+u_fp sys_maxd; /* dispersion of selected peer */
+l_fp sys_reftime; /* time we were last updated */
+l_fp sys_refskew; /* accumulated skew since last update */
+struct peer *sys_peer; /* our current peer */
+u_char sys_poll; /* log2 of system poll interval */
+extern long sys_clock; /* second part of current time */
+long sys_lastselect; /* sys_clock at last synch update */
+
+/*
+ * Nonspecified system state variables.
+ */
+int sys_bclient; /* we set our time to broadcasts */
+s_fp sys_bdelay; /* broadcast client default delay */
+int sys_authenticate; /* authenticate time used for syncing */
+u_char consensus_leap; /* mitigated leap bits */
+u_long sys_authdelay; /* encryption time (l_fp fraction) */
+u_char leap_consensus; /* consensus of survivor leap bits */
+
+/*
+ * Statistics counters
+ */
+u_long sys_stattime; /* time when we started recording */
+u_long sys_badstratum; /* packets with invalid stratum */
+u_long sys_oldversionpkt; /* old version packets received */
+u_long sys_newversionpkt; /* new version packets received */
+u_long sys_unknownversion; /* don't know version packets */
+u_long sys_badlength; /* packets with bad length */
+u_long sys_processed; /* packets processed */
+u_long sys_badauth; /* packets dropped because of auth */
+u_long sys_limitrejected; /* pkts rejected due toclient count per net */
+
+/*
+ * Imported from ntp_timer.c
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_io.c
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from ntp_loopfilter.c
+ */
+extern int pll_enable;
+extern int pps_update;
+
+/*
+ * Imported from ntp_util.c
+ */
+extern int stats_control;
+
+/*
+ * The peer hash table. Imported from ntp_peer.c
+ */
+extern struct peer *peer_hash[];
+extern int peer_hash_count[];
+
+/*
+ * debug flag
+ */
+extern int debug;
+
+static void clear_all P((void));
+
+/*
+ * transmit - Transmit Procedure. See Section 3.4.1 of the
+ * specification.
+ */
+void
+transmit(peer)
+ register struct peer *peer;
+{
+ struct pkt xpkt; /* packet to send */
+ u_long peer_timer;
+ u_fp precision;
+ int bool;
+
+ /*
+ * We need to be very careful about honking uncivilized time. If
+ * not operating in broadcast mode, honk in all except broadcast
+ * client mode. If operating in broadcast mode and synchronized
+ * to a real source, honk except when the peer is the local-
+ * clock driver and the prefer flag is not set. In other words,
+ * in broadcast mode we never honk unless known to be
+ * synchronized to real time.
+ */
+ bool = 0;
+ if (peer->hmode != MODE_BROADCAST) {
+ if (peer->hmode != MODE_BCLIENT)
+ bool = 1;
+ } else if (sys_peer != 0 && sys_leap != LEAP_NOTINSYNC) {
+ if (!(sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ !(sys_peer->flags & FLAG_PREFER)))
+ bool = 1;
+ }
+ if (bool) {
+ u_long xkeyid;
+ int find_rtt = (peer->cast_flags & MDF_MCAST) &&
+ peer->hmode != MODE_BROADCAST;
+
+ /*
+ * Figure out which keyid to include in the packet
+ */
+ if ((peer->flags & FLAG_AUTHENABLE)
+ && (peer->flags & (FLAG_CONFIG|FLAG_AUTHENTIC))
+ && authhavekey(peer->keyid)) {
+ xkeyid = peer->keyid;
+ } else {
+ xkeyid = 0;
+ }
+
+ /*
+ * Make up a packet to send.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ peer->version, peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = peer->hpoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(sys_rootdelay);
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ xpkt.rootdispersion = HTONS_FP(sys_rootdispersion +
+ precision + LFPTOFP(&sys_refskew));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->org, &xpkt.org);
+ HTONL_FP(&peer->rec, &xpkt.rec);
+
+ /*
+ * Decide whether to authenticate or not. If so, call
+ * encrypt() to fill in the rest of the frame. If not,
+ * just add in the xmt timestamp and send it quick.
+ */
+ if (peer->flags & FLAG_AUTHENABLE) {
+ int sendlen;
+
+ xpkt.keyid = htonl(xkeyid);
+ auth1crypt(xkeyid, (U_LONG *)&xpkt,
+ LEN_PKT_NOMAC);
+ get_systime(&peer->xmt);
+ L_ADDUF(&peer->xmt, sys_authdelay);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendlen = auth2crypt(xkeyid, (U_LONG *)&xpkt,
+ LEN_PKT_NOMAC);
+ sendpkt(&peer->srcadr, find_rtt ?
+ any_interface : peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ peer->ttl : -7, &xpkt, sendlen +
+ LEN_PKT_NOMAC);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("transmit auth to %s\n",
+ ntoa(&(peer->srcadr)));
+#endif
+ peer->sent++;
+ } else {
+ /*
+ * Get xmt timestamp, then send it without mac
+ * field
+ */
+ int find_rtt = (peer->cast_flags & MDF_MCAST) &&
+ peer->dstadr != any_interface;
+ get_systime(&(peer->xmt));
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendpkt(&(peer->srcadr), find_rtt ?
+ any_interface : peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ peer->ttl : -8, &xpkt, LEN_PKT_NOMAC);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("transmit to %s\n",
+ ntoa(&(peer->srcadr)));
+#endif
+ peer->sent++;
+ }
+ }
+
+ if (peer->hmode != MODE_BROADCAST) {
+ u_char opeer_reach;
+ /*
+ * Determine reachability and diddle things if we
+ * haven't heard from the host for a while. If we are
+ * about to become unreachable and are a
+ * broadcast/multicast client, the server has refused to
+ * boogie in client/server mode, so we switch to
+ * MODE_BCLIENT anyway and wait for subsequent
+ * broadcasts.
+ */
+ opeer_reach = peer->reach;
+ if (opeer_reach & 0x80 && peer->flags & FLAG_MCAST2) {
+ peer->hmode = MODE_BCLIENT;
+ }
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+ if (opeer_reach != 0)
+ report_event(EVNT_UNREACH, peer);
+ /*
+ * Clear this guy out. No need to redo clock
+ * selection since by now this guy won't be a
+ * player
+ */
+ if (peer->flags & FLAG_CONFIG) {
+ if (opeer_reach != 0) {
+ peer_clear(peer);
+ peer->timereachable =
+ current_time;
+ }
+ }
+
+ /*
+ * While we have a chance, if our system peer is
+ * zero or his stratum is greater than the last
+ * known stratum of this guy, make sure hpoll is
+ * clamped to the minimum before resetting the
+ * timer. If the peer has been unreachable for a
+ * while and we have a system peer who is at
+ * least his equal, we may want to ramp his
+ * polling interval up to avoid the useless
+ * traffic.
+ */
+ if (sys_peer == 0) {
+ peer->hpoll = peer->minpoll;
+ peer->unreach = 0;
+ } else if (sys_peer->stratum > peer->stratum) {
+ peer->hpoll = peer->minpoll;
+ peer->unreach = 0;
+ } else {
+ if (peer->unreach < 16) {
+ peer->unreach++;
+ peer->hpoll = peer->minpoll;
+ } else if (peer->hpoll < peer->maxpoll) {
+ peer->hpoll++;
+ peer->ppoll = peer->hpoll;
+ }
+ }
+
+ /*
+ * Update reachability and poll variables
+ */
+ } else if ((opeer_reach & 3) == 0) {
+
+ l_fp off;
+
+ if (peer->valid > 0)
+ peer->valid--;
+ if (peer->hpoll > peer->minpoll)
+ peer->hpoll--;
+ L_CLR(&off);
+ clock_filter(peer, &off, (s_fp)0,
+ (u_fp)NTP_MAXDISPERSE);
+ if (peer->flags & FLAG_SYSPEER)
+ clock_select();
+ } else {
+ if (peer->valid < NTP_SHIFT) {
+ peer->valid++;
+ } else {
+ if (peer->hpoll < peer->maxpoll)
+ peer->hpoll++;
+ }
+ }
+ }
+
+ /*
+ * Finally, adjust the hpoll variable for special conditions. If
+ * we are a broadcast/multicast client, we use the server poll
+ * interval if listening for broadcasts and one-eighth this
+ * interval if in client/server mode. The following clamp
+ * prevents madness. If this is the system poll, sys_poll
+ * controls hpoll.
+ */
+ if (peer->flags & FLAG_MCAST2) {
+ if (peer->hmode == MODE_BCLIENT)
+ peer->hpoll = peer->ppoll;
+ else
+ peer->hpoll = peer->ppoll - 3;
+ } else if (peer->flags & FLAG_SYSPEER)
+ peer->hpoll = sys_poll;
+ if (peer->hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+
+ /*
+ * Arrange for our next timeout. hpoll will be less than maxpoll
+ * for sure.
+ */
+ if (peer->event_timer.next != 0)
+ /*
+ * Oops, someone did already.
+ */
+ TIMER_DEQUEUE(&peer->event_timer);
+ peer_timer = 1 << (int)max((u_char)min(peer->ppoll,
+ peer->hpoll), peer->minpoll);
+ peer->event_timer.event_time = current_time + peer_timer;
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+}
+
+/*
+ * receive - Receive Procedure. See section 3.4.2 in the specification.
+ */
+void
+receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct peer *peer;
+ register struct pkt *pkt;
+ register u_char hismode;
+ int restrict;
+ int has_mac;
+ int trustable;
+ int is_authentic;
+ u_long hiskeyid;
+ struct peer *peer2;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("receive from %s\n", ntoa(&rbufp->recv_srcadr));
+#endif
+
+ /*
+ * Let the monitoring software take a look at this first.
+ */
+ monitor(rbufp);
+
+ /*
+ * Get the restrictions on this guy. If we're to ignore him,
+ * go no further.
+ */
+ restrict = restrictions(&rbufp->recv_srcadr);
+ if (restrict & RES_IGNORE)
+ return;
+
+ /*
+ * Get a pointer to the packet.
+ */
+ pkt = &rbufp->recv_pkt;
+
+ /*
+ * Catch packets whose version number we can't deal with
+ */
+ if (PKT_VERSION(pkt->li_vn_mode) >= NTP_VERSION) {
+ sys_newversionpkt++;
+ } else if (PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION) {
+ sys_oldversionpkt++;
+ } else {
+ sys_unknownversion++;
+ return;
+ }
+
+ /*
+ * Catch private mode packets. Dump it if queries not allowed.
+ */
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_PRIVATE) {
+ if (restrict & RES_NOQUERY)
+ return;
+ process_private(rbufp, ((restrict&RES_NOMODIFY) == 0));
+ return;
+ }
+
+ /*
+ * Same with control mode packets.
+ */
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_CONTROL) {
+ if (restrict & RES_NOQUERY)
+ return;
+ process_control(rbufp, restrict);
+ return;
+ }
+
+ /*
+ * See if we're allowed to serve this guy time. If not, ignore
+ * him.
+ */
+ if (restrict & RES_DONTSERVE)
+ return;
+
+ /*
+ * See if we only accept limited number of clients from the net
+ * this guy is from. Note: the flag is determined dynamically
+ * within restrictions()
+ */
+ if (restrict & RES_LIMITED) {
+ extern u_long client_limit;
+
+ sys_limitrejected++;
+ syslog(LOG_NOTICE,
+ "rejected mode %d request from %s - per net client limit (%d) exceeded",
+ PKT_MODE(pkt->li_vn_mode),
+ ntoa(&rbufp->recv_srcadr), client_limit);
+ return;
+ }
+ /*
+ * Dump anything with a putrid stratum. These will most likely
+ * come from someone trying to poll us with ntpdc.
+ */
+ if (pkt->stratum > NTP_MAXSTRATUM) {
+ sys_badstratum++;
+ return;
+ }
+
+ /*
+ * Find the peer. This will return a null if this guy isn't in
+ * the database.
+ */
+ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd);
+
+ /*
+ * Check the length for validity, drop the packet if it is
+ * not as expected. If this is a client mode poll, go no
+ * further. Send back his time and drop it.
+ *
+ * The scheme we use for authentication is this. If we are
+ * running in non-authenticated mode, we accept both frames
+ * which are authenticated and frames which aren't, but don't
+ * authenticate. We do record whether the frame had a mac field
+ * or not so we know what to do on output.
+ *
+ * If we are running in authenticated mode, we only trust frames
+ * which have authentication attached, which are validated and
+ * which are using one of our trusted keys. We respond to all
+ * other pollers without saving any state. If a host we are
+ * passively peering with changes his key from a trusted one to
+ * an untrusted one, we immediately unpeer with him, reselect
+ * the clock and treat him as an unmemorable client (this is
+ * a small denial-of-service hole I'll have to think about).
+ * If a similar event occurs with a configured peer we drop the
+ * frame and hope he'll revert to our key again. If we get a
+ * frame which can't be authenticated with the given key, we
+ * drop it. Either we disagree on the keys or someone is trying
+ * some funny stuff.
+ */
+
+ /*
+ * here we assume that any packet with an authenticator is at
+ * least LEN_PKT_MAC bytes long, which means at least 96 bits
+ */
+ if (rbufp->recv_length >= LEN_PKT_MAC) {
+ has_mac = rbufp->recv_length - LEN_PKT_NOMAC;
+ hiskeyid = ntohl(pkt->keyid);
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "receive: pkt is %d octets, mac %d octets long, keyid %ld\n",
+ rbufp->recv_length, has_mac, hiskeyid);
+#endif
+ } else if (rbufp->recv_length == LEN_PKT_NOMAC) {
+ hiskeyid = 0;
+ has_mac = 0;
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("receive: bad length %d %ld\n",
+ rbufp->recv_length, sizeof(struct pkt));
+#endif
+ sys_badlength++;
+ return;
+ }
+
+
+ /*
+ * Figure out his mode and validate it.
+ */
+ hismode = PKT_MODE(pkt->li_vn_mode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("receive: his mode %d\n", hismode);
+#endif
+ if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION && hismode ==
+ 0) {
+ /*
+ * Easy. If it is from the NTP port it is
+ * a sym act, else client.
+ */
+ if (SRCPORT(&rbufp->recv_srcadr) == NTP_PORT)
+ hismode = MODE_ACTIVE;
+ else
+ hismode = MODE_CLIENT;
+ } else {
+ if (hismode != MODE_ACTIVE && hismode != MODE_PASSIVE &&
+ hismode != MODE_SERVER && hismode != MODE_CLIENT &&
+ hismode != MODE_BROADCAST) {
+ syslog(LOG_ERR, "bad mode %d received from %s",
+ PKT_MODE(pkt->li_vn_mode),
+ ntoa(&rbufp->recv_srcadr));
+ return;
+ }
+ }
+
+ /*
+ * If he included a mac field, decrypt it to see if it is
+ * authentic.
+ */
+ is_authentic = 0;
+ if (has_mac) {
+ if (authhavekey(hiskeyid)) {
+ if (!authistrusted(hiskeyid)) {
+ sys_badauth++;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: untrusted keyid\n");
+#endif
+ return;
+ }
+ if (authdecrypt(hiskeyid, (U_LONG *)pkt,
+ LEN_PKT_NOMAC)) {
+ is_authentic = 1;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: authdecrypt succeeds\n");
+#endif
+ } else {
+ sys_badauth++;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: authdecrypt fails\n");
+#endif
+ }
+ }
+ }
+
+ /*
+ * If this is someone we don't remember from a previous
+ * association, dispatch him now. Either we send something back
+ * quick, we ignore him, or we allocate some memory for him and
+ * let him continue.
+ */
+ if (peer == 0) {
+ int mymode;
+
+ mymode = MODE_PASSIVE;
+ switch(hismode) {
+ case MODE_ACTIVE:
+ /*
+ * See if this guy qualifies as being the least
+ * bit memorable. If so we keep him around for
+ * later. If not, send his time quick.
+ */
+ if (restrict & RES_NOPEER) {
+ fast_xmit(rbufp, (int)hismode,
+ is_authentic);
+ return;
+ }
+ break;
+
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ /*
+ * These are obvious errors. Ignore.
+ */
+ return;
+
+ case MODE_CLIENT:
+ /*
+ * Send it back quick and go home.
+ */
+ fast_xmit(rbufp, (int)hismode, is_authentic);
+ return;
+
+ case MODE_BROADCAST:
+ /*
+ * Sort of a repeat of the above...
+ */
+ if ((restrict & RES_NOPEER) || !sys_bclient)
+ return;
+ mymode = MODE_MCLIENT;
+ break;
+ }
+
+ /*
+ * Okay, we're going to keep him around. Allocate him
+ * some memory.
+ */
+ peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, mymode, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, hiskeyid);
+
+ if (peer == 0) {
+ /*
+ * The only way this can happen is if the
+ * source address looks like a reference
+ * clock. Since this is an illegal address
+ * this is one of those "can't happen" things.
+ */
+ syslog(LOG_ERR,
+ "receive() failed to peer with %s, mode %d",
+ ntoa(&rbufp->recv_srcadr), mymode);
+ return;
+ }
+ }
+
+ /*
+ * Mark the time of reception
+ */
+ peer->timereceived = current_time;
+
+ /*
+ * If the peer isn't configured, set his keyid and authenable
+ * status based on the packet.
+ */
+ if (!(peer->flags & FLAG_CONFIG)) {
+ if (has_mac) {
+ if (!(peer->reach && peer->keyid != hiskeyid)) {
+ peer->keyid = hiskeyid;
+ peer->flags |= FLAG_AUTHENABLE;
+ }
+ } else {
+ peer->keyid = 0;
+ peer->flags &= ~FLAG_AUTHENABLE;
+ }
+ }
+
+
+ /*
+ * If this message was authenticated properly, note this
+ * in the flags.
+ */
+ if (is_authentic) {
+ peer->flags |= FLAG_AUTHENTIC;
+ } else {
+ /*
+ * If this guy is authenable, and has been authenticated
+ * in the past, but just failed the authentic test,
+ * report the event.
+ */
+ if (peer->flags & FLAG_AUTHENABLE
+ && peer->flags & FLAG_AUTHENTIC)
+ report_event(EVNT_PEERAUTH, peer);
+ peer->flags &= ~FLAG_AUTHENTIC;
+ }
+
+ /*
+ * Determine if this guy is basically trustable.
+ */
+ if (restrict & RES_DONTTRUST)
+ trustable = 0;
+ else
+ trustable = 1;
+
+ if (sys_authenticate && trustable) {
+ if (!(peer->flags & FLAG_CONFIG) ||
+ (peer->flags & FLAG_AUTHENABLE)) {
+ if (has_mac && is_authentic)
+ trustable = 1;
+ else
+ trustable = 0;
+ }
+ }
+
+ /*
+ * Dispose of the packet based on our respective modes. We
+ * don't drive this with a table, though we probably could.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ case MODE_CLIENT:
+ /*
+ * Active mode associations are configured. If the data
+ * isn't trustable, ignore it and hope this guy
+ * brightens up. Else accept any data we get and process
+ * it.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ process_packet(peer, pkt, &(rbufp->recv_time),
+ has_mac, trustable);
+ break;
+
+ case MODE_CLIENT:
+ if (peer->hmode == MODE_ACTIVE)
+ fast_xmit(rbufp, hismode, is_authentic);
+ return;
+ }
+ break;
+
+ case MODE_PASSIVE:
+ /*
+ * Passive mode associations are (in the current
+ * implementation) always dynamic. If we get an invalid
+ * header, break the connection. I hate doing this since
+ * it seems like a waste. Oh, well.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ if (process_packet(peer, pkt,
+ &(rbufp->recv_time),
+ has_mac, trustable) == 0) {
+ unpeer(peer);
+ clock_select();
+ fast_xmit(rbufp, (int)hismode, is_authentic);
+ }
+ break;
+
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ /*
+ * These are errors. Just ignore the packet.
+ * If he doesn't straighten himself out this
+ * association will eventually be disolved.
+ */
+ break;
+
+ case MODE_CLIENT:
+ fast_xmit(rbufp, hismode, is_authentic);
+ return;
+ }
+ break;
+
+
+ case MODE_BCLIENT:
+ /*
+ * Broadcast client pseudo-mode. We accept both server
+ * and broadcast data. Passive mode data is an error.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ /*
+ * This guy wants to give us real time when
+ * we've been existing on lousy broadcasts!
+ * Create a passive mode association and do it
+ * that way, but keep the old one in case the
+ * packet turns out to be bad.
+ */
+ peer2 = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_PASSIVE,
+ PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXPOLL, 0, hiskeyid);
+ if (process_packet(peer2, pkt,
+ &rbufp->recv_time, has_mac, trustable) == 0) {
+ /*
+ * Strange situation. We've been
+ * receiving broadcasts from him which
+ * we liked, but we don't like his
+ * active mode stuff. Keep his old peer
+ * structure and send him some time
+ * quickly, we'll figure it out later.
+ */
+ unpeer(peer2);
+ fast_xmit(rbufp, (int)hismode,
+ is_authentic);
+ } else
+ /*
+ * Drop the old association
+ */
+ unpeer(peer);
+ break;
+
+ case MODE_PASSIVE:
+ break;
+
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ process_packet(peer, pkt, &rbufp->recv_time,
+ has_mac, trustable);
+ /*
+ * We don't test for invalid headers.
+ * Let him time out.
+ */
+ break;
+ }
+ break;
+
+ case MODE_MCLIENT:
+ /*
+ * This mode is temporary and does not appear outside
+ * this routine. It lasts only from the time the
+ * broadcast/multicast is recognized until the
+ * association is instantiated. Note that we start up in
+ * client/server mode to initially synchronize the
+ * clock.
+ */
+ switch (hismode) {
+ case MODE_BROADCAST:
+ peer->flags |= FLAG_MCAST1 | FLAG_MCAST2;
+ peer->hmode = MODE_CLIENT;
+ process_packet(peer, pkt, &rbufp->recv_time,
+ has_mac, trustable);
+ break;
+
+ case MODE_SERVER:
+ case MODE_PASSIVE:
+ case MODE_ACTIVE:
+ case MODE_CLIENT:
+ break;
+ }
+ }
+}
+
+
+/*
+ * process_packet - Packet Procedure, a la Section 3.4.3 of the
+ * specification. Or almost, at least. If we're in here we have a
+ * reasonable expectation that we will be having a long term
+ * relationship with this host.
+ */
+int
+process_packet(peer, pkt, recv_ts, has_mac, trustable)
+ register struct peer *peer;
+ register struct pkt *pkt;
+ l_fp *recv_ts;
+ int has_mac;
+ int trustable; /* used as "valid header" */
+{
+ l_fp t10, t23;
+ s_fp di, ei, p_dist, p_disp;
+ l_fp ci, p_rec, p_xmt, p_org;
+ int randomize;
+ u_char ostratum, oreach;
+ U_LONG temp;
+ u_fp precision;
+
+ sys_processed++;
+ peer->processed++;
+ p_dist = NTOHS_FP(pkt->rootdelay);
+ p_disp = NTOHS_FP(pkt->rootdispersion);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST)
+ NTOHL_FP(&pkt->org, &p_org);
+ else
+ p_org = peer->rec;
+ peer->rec = *recv_ts;
+ peer->flash = 0;
+ randomize = POLL_RANDOMCHANGE;
+
+ /*
+ * Test for old or duplicate packets (tests 1 through 3).
+ */
+ if (L_ISHIS(&peer->org, &p_xmt)) /* count old packets */
+ peer->oldpkt++;
+ if (L_ISEQU(&peer->org, &p_xmt)) /* test 1 */
+ peer->flash |= TEST1; /* duplicate packet */
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) {
+ if (!L_ISEQU(&peer->xmt, &p_org)) { /* test 2 */
+ randomize = POLL_MAKERANDOM;
+ peer->bogusorg++;
+ peer->flash |= TEST2; /* bogus packet */
+ }
+ if (L_ISZERO(&p_rec) || L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ } else {
+ if (L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ }
+ peer->org = p_xmt; /* reuse byte-swapped pkt->xmt */
+ peer->ppoll = pkt->ppoll;
+
+ /*
+ * Call poll_update(). This will either start us, if the
+ * association is new, or drop the polling interval if the
+ * association is existing and ppoll has been reduced.
+ */
+ poll_update(peer, peer->hpoll, randomize);
+
+
+ /*
+ * Test for valid header (tests 5 through 8)
+ */
+ if (trustable == 0) /* test 5 */
+ peer->flash |= TEST5; /* authentication failed */
+ temp = ntohl(pkt->reftime.l_ui);
+ if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || /* test 6 */
+ p_xmt.l_ui < temp || p_xmt.l_ui >= temp + NTP_MAXAGE)
+ peer->flash |= TEST6; /* peer clock unsynchronized */
+ if (!(peer->flags & FLAG_CONFIG) && /* test 7 */
+ (PKT_TO_STRATUM(pkt->stratum) >= NTP_MAXSTRATUM ||
+ PKT_TO_STRATUM(pkt->stratum) > sys_stratum))
+ peer->flash |= TEST7; /* peer stratum out of bounds */
+ if (p_dist >= NTP_MAXDISPERSE /* test 8 */
+ || p_dist <= (-NTP_MAXDISPERSE)
+ || p_disp >= NTP_MAXDISPERSE)
+ peer->flash |= TEST8; /* delay/dispersion too big */
+
+ /*
+ * If the packet header is invalid (tests 5 through 8), exit
+ */
+ if (peer->flash & (TEST5 | TEST6 | TEST7 | TEST8)) {
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("invalid packet header %s %02x\n",
+ ntoa(&peer->srcadr), peer->flash);
+#endif
+
+ return(0);
+ }
+
+ /*
+ * Valid header; update our state.
+ */
+ peer->leap = PKT_LEAP(pkt->li_vn_mode);
+ peer->pmode = PKT_MODE(pkt->li_vn_mode);
+ if (has_mac)
+ peer->pkeyid = ntohl(pkt->keyid);
+ else
+ peer->pkeyid = 0;
+ ostratum = peer->stratum;
+ peer->stratum = PKT_TO_STRATUM(pkt->stratum);
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_dist;
+ peer->rootdispersion = p_disp;
+ peer->refid = pkt->refid;
+ NTOHL_FP(&pkt->reftime, &peer->reftime);
+ oreach = peer->reach;
+ if (peer->reach == 0) {
+ peer->timereachable = current_time;
+ /*
+ * If this guy was previously unreachable, set his
+ * polling interval to the minimum and reset the
+ * unreach counter.
+ */
+ peer->unreach = 0;
+ peer->hpoll = peer->minpoll;
+ }
+ peer->reach |= 1;
+
+ /*
+ * If running in a client/server association, calculate the
+ * clock offset c, roundtrip delay d and dispersion e. We use
+ * the equations (reordered from those in the spec). Note that,
+ * in a broadcast association, org has been set to the time of
+ * last reception. Note the computation of dispersion includes
+ * the system precision plus that due to the frequency error
+ * since the originate time.
+ *
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ * d = (t2 - t3) - (t1 - t0)
+ * e = (org - rec) (seconds only)
+ */
+ t10 = p_xmt; /* compute t1 - t0 */
+ L_SUB(&t10, &peer->rec);
+ t23 = p_rec; /* compute t2 - t3 */
+ L_SUB(&t23, &p_org);
+ ci = t10;
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ ei = precision + peer->rec.l_ui - p_org.l_ui;
+
+ /*
+ * If running in a broacast association, the clock offset is (t1
+ * - t0) corrected by the one-way delay, but we can't measure
+ * that directly; therefore, we start up in client/server mode,
+ * calculate the clock offset, using the engineered refinement
+ * algorithms, while also receiving broadcasts. When a broadcast
+ * is received in client/server mode, we calculate a correction
+ * factor to use after switching back to broadcast mode. We know
+ * NTP_SKEWFACTOR == 16, which accounts for the simplified ei
+ * calculation.
+ *
+ * If FLAG_MCAST2 is set, we are a broadcast/multicast client.
+ * If FLAG_MCAST1 is set, we haven't calculated the propagation
+ * delay. If hmode is MODE_CLIENT, we haven't set the local
+ * clock in client/server mode. Initially, we come up
+ * MODE_CLIENT. When the clock is first updated and FLAG_MCAST2
+ * is set, we switch from MODE_CLIENT to MODE_BCLIENT.
+ */
+ if (peer->pmode == MODE_BROADCAST) {
+ if (peer->flags & FLAG_MCAST1) {
+ if (peer->hmode == MODE_BCLIENT)
+ peer->flags &= ~FLAG_MCAST1;
+ L_SUB(&ci, &peer->offset);
+ L_NEG(&ci);
+ peer->estbdelay = LFPTOFP(&ci);
+ return (1);
+
+ }
+ FPTOLFP(peer->estbdelay, &t10);
+ L_ADD(&ci, &t10);
+ di = peer->delay;
+
+ } else {
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("offset: %s, delay %s, error %s\n",
+ lfptoa(&ci, 6), fptoa(di, 5), fptoa(ei, 5));
+#endif
+ if (di >= NTP_MAXDISPERSE || di <= (-NTP_MAXDISPERSE)
+ || ei >= NTP_MAXDISPERSE) /* test 4 */
+ peer->flash |= TEST4; /* delay/dispersion too big */
+
+ /*
+ * If the packet data is invalid (tests 1 through 4), exit.
+ */
+ if (peer->flash) {
+
+#ifdef DEBUG
+ if (debug)
+ printf("invalid packet data %s %02x\n",
+ ntoa(&peer->srcadr), peer->flash);
+#endif
+
+ /*
+ * If there was a reachability change report it even
+ * though the packet was bogus.
+ */
+ if (oreach == 0)
+ report_event(EVNT_REACH, peer);
+ return(1);
+ }
+
+ /*
+ * This one is valid. Mark it so, give it to clock_filter().
+ */
+ clock_filter(peer, &ci, di, (u_fp)ei);
+
+ /*
+ * If this guy was previously unreachable, report him reachable.
+ * Note we do this here so that the peer values we return are
+ * the updated ones.
+ */
+ if (oreach == 0)
+ report_event(EVNT_REACH, peer);
+
+ /*
+ * Now update the clock. If we have found a system peer and this
+ * is a broadcast/multicast client, switch to listen mode.
+ */
+ clock_update(peer);
+ if (sys_peer && peer->flags & FLAG_MCAST2)
+ peer->hmode = MODE_BCLIENT;
+ return(1);
+}
+
+
+/*
+ * clock_update - Clock-update procedure, see section 3.4.5.
+ */
+void
+clock_update(peer)
+ struct peer *peer;
+{
+ u_char oleap;
+ u_char ostratum;
+ s_fp d;
+ extern u_char leap_mask;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clock_update(%s)\n", ntoa(&peer->srcadr));
+#endif
+
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ &peer->offset, peer->delay, peer->dispersion);
+
+ /*
+ * Call the clock selection algorithm to see if this update
+ * causes the peer to change. If this is not the system peer,
+ * quit now.
+ */
+ clock_select();
+ if (peer != sys_peer)
+ return;
+
+ /*
+ * Update the system state. This updates the system stratum,
+ * leap bits, root delay, root dispersion, reference ID and
+ * reference time. We also update select dispersion and max
+ * frequency error.
+ */
+ oleap = sys_leap;
+ ostratum = sys_stratum;
+ sys_stratum = peer->stratum + 1;
+ if (sys_stratum == 1)
+ sys_refid = peer->refid;
+ else
+ sys_refid = peer->srcadr.sin_addr.s_addr;
+ sys_reftime = peer->rec;
+ d = peer->delay;
+ if (d < 0)
+ d = -d;
+ sys_rootdelay = peer->rootdelay + d;
+ d = peer->soffset;
+ if (d < 0)
+ d = -d;
+ d += peer->dispersion + peer->selectdisp;
+ if (!peer->flags & FLAG_REFCLOCK && d < NTP_MINDISPERSE)
+ d = NTP_MINDISPERSE;
+ sys_rootdispersion = peer->rootdispersion + d;
+
+ /*
+ * Reset/adjust the system clock. Watch for timewarps here.
+ */
+ switch (local_clock(&sys_offset, peer)) {
+ case -1:
+
+ /*
+ * Clock is too screwed up. Just exit for now.
+ */
+ report_event(EVNT_SYSFAULT, (struct peer *)0);
+ exit(1);
+ /*NOTREACHED*/
+ case 0:
+
+ /*
+ * Clock was slewed. Continue on normally.
+ */
+ sys_leap = leap_consensus & leap_mask;
+ L_CLR(&sys_refskew);
+ break;
+
+ case 1:
+
+ /*
+ * Clock was stepped. Clear filter registers
+ * of all peers.
+ */
+ clear_all();
+ leap_process(); /* reset the leap interrupt */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_refskew.l_i = NTP_MAXSKEW; sys_refskew.l_f = 0;
+ report_event(EVNT_CLOCKRESET, (struct peer *)0);
+ break;
+ }
+ sys_maxd = peer->dispersion + peer->selectdisp;
+ if (oleap != sys_leap)
+ report_event(EVNT_SYNCCHG, (struct peer *)0);
+ if (ostratum != sys_stratum)
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+}
+
+
+/*
+ * poll_update - update peer poll interval. See Section 3.4.8 of the
+ * spec.
+ */
+void
+poll_update(peer, new_hpoll, randomize)
+ struct peer *peer;
+ unsigned int new_hpoll;
+ int randomize;
+{
+ register struct event *evp;
+ register u_long new_timer;
+ u_char newpoll, oldpoll;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("poll_update(%s, %d, %d)\n", ntoa(&peer->srcadr),
+ new_hpoll, randomize);
+#endif
+ /*
+ * Catch reference clocks here. The polling interval for a
+ * reference clock is fixed and needn't be maintained by us.
+ */
+ if (peer->flags & FLAG_REFCLOCK || peer->hmode ==
+ MODE_BROADCAST)
+ return;
+
+ /*
+ * This routine * will randomly perturb the new peer.timer if
+ * requested, to try to prevent synchronization with the remote
+ * peer from occuring. There are three options, based on the
+ * value of randomize:
+ *
+ * POLL_NOTRANDOM - essentially the spec algorithm. If
+ * peer.timer is greater than the new polling interval,
+ * drop it to the new interval.
+ *
+ * POLL_RANDOMCHANGE - make changes randomly. If peer.timer
+ * must be changed, based on the comparison about, randomly
+ * perturb the new value of peer.timer.
+ *
+ * POLL_MAKERANDOM - make next interval random. Calculate
+ * a randomly perturbed poll interval. If this value is
+ * less that peer.timer, update peer.timer.
+ */
+ oldpoll = peer->hpoll;
+ if (peer->hmode == MODE_BCLIENT)
+ peer->hpoll = peer->ppoll;
+ else if ((peer->flags & FLAG_SYSPEER) && new_hpoll > sys_poll)
+ peer->hpoll = max(peer->minpoll, sys_poll);
+ else {
+ if (new_hpoll > peer->maxpoll)
+ peer->hpoll = peer->maxpoll;
+ else if (new_hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+ else
+ peer->hpoll = new_hpoll;
+ }
+
+ /* hpoll <= maxpoll for sure */
+ newpoll = max((u_char)min(peer->ppoll, peer->hpoll),
+ peer->minpoll);
+ if (randomize == POLL_MAKERANDOM || (randomize ==
+ POLL_RANDOMCHANGE && newpoll != oldpoll))
+ new_timer = (1 << (newpoll - 1))
+ + ranp2(newpoll - 1) + current_time;
+ else
+ new_timer = (1 << newpoll) + current_time;
+ evp = &(peer->event_timer);
+ if (evp->next == 0 || evp->event_time > new_timer) {
+ TIMER_DEQUEUE(evp);
+ evp->event_time = new_timer;
+ TIMER_ENQUEUE(timerqueue, evp);
+ }
+}
+
+/*
+ * clear_all - clear all peer filter registers. This is done after
+ * a step change in the time.
+ */
+static void
+clear_all()
+{
+ register int i;
+ register struct peer *peer;
+
+ for (i = 0; i < HASH_SIZE; i++)
+ for (peer = peer_hash[i]; peer != 0; peer = peer->next) {
+ peer_clear(peer);
+ }
+
+ /*
+ * Clear sys_peer. We'll sync to one later.
+ */
+ sys_peer = 0;
+ sys_stratum = STRATUM_UNSPEC;
+}
+
+
+/*
+ * clear - clear peer filter registers. See Section 3.4.7 of the spec.
+ */
+void
+peer_clear(peer)
+ register struct peer *peer;
+{
+ register int i;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clear(%s)\n", ntoa(&peer->srcadr));
+#endif
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO);
+ peer->hpoll = peer->minpoll;
+ peer->dispersion = NTP_MAXDISPERSE;
+ for (i = 0; i < NTP_SHIFT; i++)
+ peer->filter_error[i] = NTP_MAXDISPERSE;
+ poll_update(peer, peer->minpoll, POLL_RANDOMCHANGE);
+ clock_select();
+
+ /*
+ * Clear out the selection counters
+ */
+ peer->candidate = 0;
+ peer->select = 0;
+ peer->correct = 0;
+ peer->was_sane = 0;
+
+ /*
+ * Since we have a chance to correct possible funniness in
+ * our selection of interfaces on a multihomed host, do so
+ * by setting us to no particular interface.
+ */
+ peer->dstadr = any_interface;
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(peer, sample_offset, sample_delay, sample_error)
+ register struct peer *peer;
+ l_fp *sample_offset;
+ s_fp sample_delay;
+ u_fp sample_error;
+{
+ register int i, j, k, n;
+ register u_char *ord;
+ s_fp distance[NTP_SHIFT];
+ long skew, skewmax;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter(%s, %s, %s, %s)\n",
+ ntoa(&peer->srcadr), lfptoa(sample_offset, 6),
+ fptoa(sample_delay, 5), ufptoa(sample_error, 5));
+#endif
+
+ /*
+ * Update sample errors and calculate distances. Also initialize
+ * sort index vector. We know NTP_SKEWFACTOR == 16
+ */
+ skew = sys_clock - peer->update;
+ peer->update = sys_clock;
+ ord = peer->filter_order;
+ j = peer->filter_nextpt;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_error[j] += (u_fp)skew;
+ if (peer->filter_error[j] > NTP_MAXDISPERSE)
+ peer->filter_error[j] = NTP_MAXDISPERSE;
+ distance[i] = peer->filter_error[j] +
+ (peer->filter_delay[j] >> 1);
+ ord[i] = j;
+ if (--j < 0)
+ j += NTP_SHIFT;
+ }
+
+ /*
+ * Insert the new sample at the beginning of the register.
+ */
+ peer->filter_delay[peer->filter_nextpt] = sample_delay;
+ peer->filter_offset[peer->filter_nextpt] = *sample_offset;
+ peer->filter_soffset[peer->filter_nextpt] =
+ LFPTOFP(sample_offset);
+ peer->filter_error[peer->filter_nextpt] = sample_error;
+ distance[0] = sample_error + (sample_delay >> 1);
+
+ /*
+ * Sort the samples in the register by distance. The winning
+ * sample will be in ord[0]. Sort the samples only if the
+ * samples are not too old and the delay is meaningful.
+ */
+ skewmax = 0;
+ for (n = 0; n < NTP_SHIFT && sample_delay; n++) {
+ for (j = 0; j < n && skewmax <
+ CLOCK_MAXSEC; j++) {
+ if (distance[j] > distance[n]) {
+ s_fp ftmp;
+
+ ftmp = distance[n];
+ k = ord[n];
+ distance[n] = distance[j];
+ ord[n] = ord[j];
+ distance[j] = ftmp;
+ ord[j] = k;
+ }
+ }
+ skewmax += (1 << peer->hpoll);
+ }
+ peer->filter_nextpt++;
+ if (peer->filter_nextpt >= NTP_SHIFT)
+ peer->filter_nextpt = 0;
+
+ /*
+ * We compute the dispersion as per the spec. Note that, to make
+ * things simple, both the l_fp and s_fp offsets are retained
+ * and that the s_fp could be nonsense if the l_fp is greater
+ * than about 32000 s. However, the sanity checks in
+ * ntp_loopfilter() require the l_fp offset to be less than 1000
+ * s anyway, so not to worry.
+ */
+ if (peer->filter_error[ord[0]] >= NTP_MAXDISPERSE) {
+ peer->dispersion = NTP_MAXDISPERSE;
+ } else {
+ s_fp d;
+ u_fp y;
+
+ peer->delay = peer->filter_delay[ord[0]];
+ peer->offset = peer->filter_offset[ord[0]];
+ peer->soffset = LFPTOFP(&peer->offset);
+ peer->dispersion = peer->filter_error[ord[0]];
+
+ y = 0;
+ for (i = NTP_SHIFT - 1; i > 0; i--) {
+ if (peer->filter_error[ord[i]] >=
+ NTP_MAXDISPERSE)
+ d = NTP_MAXDISPERSE;
+ else {
+ d = peer->filter_soffset[ord[i]] -
+ peer->filter_soffset[ord[0]];
+ if (d < 0)
+ d = -d;
+ if (d > NTP_MAXDISPERSE)
+ d = NTP_MAXDISPERSE;
+ }
+ /*
+ * XXX This *knows* NTP_FILTER is 1/2
+ */
+ y = ((u_fp)d + y) >> 1;
+ }
+ peer->dispersion += y;
+
+ /*
+ * Calculate synchronization distance backdated to
+ * sys_lastselect (clock_select will fix it). We know
+ * NTP_SKEWFACTOR == 16.
+ */
+ d = peer->delay;
+ if (d < 0)
+ d = -d;
+ d += peer->rootdelay;
+ peer->synch = (d >> 1) + peer->rootdispersion +
+ peer->dispersion - (sys_clock - sys_lastselect);
+ }
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ */
+void
+clock_select()
+{
+ register struct peer *peer;
+ register int i;
+ register int nlist, nl3;
+ register s_fp d, e;
+ register int j;
+ register int n;
+ register int allow, found, k;
+ s_fp low = 0x7fffffff;
+ s_fp high = -0x7ffffff;
+ u_fp synch[NTP_MAXCLOCK], error[NTP_MAXCLOCK];
+ struct peer *osys_peer;
+ struct peer *typeacts = 0;
+ struct peer *typelocal = 0;
+ struct peer *typepps = 0;
+ struct peer *typeprefer = 0;
+ struct peer *typesystem = 0;
+
+ static int list_alloc = 0;
+ static struct endpoint *endpoint;
+ static int *index;
+ static struct peer **peer_list;
+ static int endpoint_size = 0, index_size = 0, peer_list_size = 0;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("clock_select()\n");
+#endif
+
+ /*
+ * Initizialize. If a prefer peer does not survive this thing,
+ * the pps_update switch will remain zero.
+ */
+ pps_update = 0;
+ nlist = 0;
+ for (n = 0; n < HASH_SIZE; n++)
+ nlist += peer_hash_count[n];
+ if (nlist > list_alloc) {
+ if (list_alloc > 0) {
+ free(endpoint);
+ free(index);
+ free(peer_list);
+ }
+ while (list_alloc < nlist) {
+ list_alloc += 5;
+ endpoint_size += 5 * 3 * sizeof *endpoint;
+ index_size += 5 * 3 * sizeof *index;
+ peer_list_size += 5 * sizeof *peer_list;
+ }
+ endpoint = (struct endpoint *)emalloc(endpoint_size);
+ index = (int *)emalloc(index_size);
+ peer_list = (struct peer **)emalloc(peer_list_size);
+ }
+
+ /*
+ * This first chunk of code is supposed to go through all
+ * peers we know about to find the NTP_MAXLIST peers which
+ * are most likely to succeed. We run through the list
+ * doing the sanity checks and trying to insert anyone who
+ * looks okay. We are at all times aware that we should
+ * only keep samples from the top two strata and we only need
+ * NTP_MAXLIST of them.
+ */
+ nlist = nl3 = 0; /* none yet */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = peer->next) {
+ /*
+ * Clear peer selection stats
+ */
+ peer->was_sane = 0;
+ peer->correct = 0;
+ peer->candidate = 0;
+ peer->select = 0;
+
+ peer->flags &= ~FLAG_SYSPEER;
+ /*
+ * Update synch distance (NTP_SKEWFACTOR == 16).
+ * Note synch distance check instead of spec
+ * dispersion check. Naughty.
+ */
+ peer->synch += (sys_clock - sys_lastselect);
+
+ if (peer->reach == 0)
+ continue; /* unreachable */
+ if (peer->stratum > 1 && peer->refid ==
+ peer->dstadr->sin.sin_addr.s_addr)
+ continue; /* sync loop */
+ if (peer->stratum >= NTP_MAXSTRATUM ||
+ peer->stratum > sys_stratum)
+ continue; /* bad stratum */
+
+ if (peer->dispersion >= NTP_MAXDISTANCE) {
+ peer->seldisptoolarge++;
+ continue; /* too noisy or broken */
+ }
+ if (peer->org.l_ui < peer->reftime.l_ui) {
+ peer->selbroken++;
+ continue; /* very broken host */
+ }
+
+ /*
+ * Don't allow the local-clock or acts drivers
+ * in the kitchen at this point, unless the
+ * prefer peer. Do that later, but only if
+ * nobody else is around.
+ */
+ if (peer->refclktype == REFCLK_LOCALCLOCK) {
+ typelocal = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no local clock */
+ }
+ if (peer->refclktype == REFCLK_NIST_ACTS) {
+ typeacts = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no acts */
+ }
+
+ /*
+ * If we get this far, we assume the peer is
+ * acceptable.
+ */
+ peer->was_sane = 1;
+ peer_list[nlist++] = peer;
+
+ /*
+ * Insert each interval endpoint on the sorted
+ * list.
+ */
+ e = peer->soffset + peer->synch; /* Upper end */
+ for (i = nl3 - 1; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 3] = index[i];
+ }
+ index[i + 3] = nl3;
+ endpoint[nl3].type = 1;
+ endpoint[nl3++].val = e;
+
+ e -= peer->synch; /* Center point */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 2] = index[i];
+ }
+ index[i + 2] = nl3;
+ endpoint[nl3].type = 0;
+ endpoint[nl3++].val = e;
+
+ e -= peer->synch; /* Lower end */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 1] = index[i];
+ }
+ index[i + 1] = nl3;
+ endpoint[nl3].type = -1;
+ endpoint[nl3++].val = e;
+ }
+ }
+ sys_lastselect = sys_clock;
+
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nl3; i++)
+ printf("select: endpoint %2d %s\n",
+ endpoint[index[i]].type,
+ fptoa(endpoint[index[i]].val, 6));
+#endif
+
+ i = 0;
+ j = nl3 - 1;
+ allow = nlist; /* falsetickers assumed */
+ found = 0;
+ while (allow > 0) {
+ allow--;
+ for (n = 0; i <= j; i++) {
+ n += endpoint[index[i]].type;
+ if (n < 0)
+ break;
+ if (endpoint[index[i]].type == 0)
+ found++;
+ }
+ for (n = 0; i <= j; j--) {
+ n += endpoint[index[j]].type;
+ if (n > 0)
+ break;
+ if (endpoint[index[j]].type == 0)
+ found++;
+ }
+ if (found > allow)
+ break;
+ low = endpoint[index[i++]].val;
+ high = endpoint[index[j--]].val;
+ }
+
+ /*
+ * If no survivors remain at this point, check if the acts or
+ * local clock drivers have been found. If so, nominate one of
+ * them as the only survivor. Otherwise, give up and declare us
+ * unsynchronized.
+ */
+ if ((allow << 1) >= nlist) {
+ if (typeacts != 0) {
+ typeacts->was_sane = 1;
+ peer_list[0] = typeacts;
+ nlist = 1;
+ } else if (typelocal != 0) {
+ typelocal->was_sane = 1;
+ peer_list[0] = typelocal;
+ nlist = 1;
+ } else {
+ if (sys_peer != 0)
+ report_event(EVNT_PEERSTCHG,
+ (struct peer *)0);
+ sys_peer = 0;
+ sys_stratum = STRATUM_UNSPEC;
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("select: low %s high %s\n", fptoa(low, 6),
+ fptoa(high, 6));
+#endif
+
+ /*
+ * Clustering algorithm. Process intersection list to discard
+ * outlyers. Construct candidate list in cluster order
+ * determined by the sum of peer synchronization distance plus
+ * scaled stratum. We must find at least one peer.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ if (nlist > 1 && (peer->soffset < low || high <
+ peer->soffset))
+ continue;
+ peer->correct = 1;
+ d = peer->synch + ((u_long)peer->stratum <<
+ NTP_DISPFACTOR);
+ if (j >= NTP_MAXCLOCK) {
+ if (d >= synch[j - 1])
+ continue;
+ else
+ j--;
+ }
+ for (k = j; k > 0; k--) {
+ if (d >= synch[k - 1])
+ break;
+ synch[k] = synch[k - 1];
+ peer_list[k] = peer_list[k - 1];
+ }
+ peer_list[k] = peer;
+ synch[k] = d;
+ j++;
+ }
+ nlist = j;
+
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nlist; i++)
+ printf("select: candidate %s cdist %s\n",
+ ntoa(&peer_list[i]->srcadr),
+ fptoa(synch[i], 6));
+#endif
+
+ /*
+ * Now, prune outlyers by root dispersion. Continue as long as
+ * there are more than NTP_MINCLOCK survivors and the minimum
+ * select dispersion is greater than the maximum peer
+ * dispersion. Stop if we are about to discard a preferred peer.
+ */
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ peer->candidate = i + 1;
+ error[i] = peer_list[i]->rootdispersion +
+ peer_list[i]->dispersion +
+ (sys_clock - peer_list[i]->update);
+ }
+ while (1) {
+ u_fp maxd = 0;
+ e = error[0];
+ for (k = i = nlist - 1; i >= 0; i--) {
+ u_fp sdisp = 0;
+
+ for (j = nlist - 1; j > 0; j--) {
+ d = peer_list[i]->soffset
+ - peer_list[j]->soffset;
+ if (d < 0)
+ d = -d;
+ sdisp += d;
+ sdisp = ((sdisp >> 1) + sdisp) >> 1;
+ }
+ peer_list[i]->selectdisp = sdisp;
+ if (sdisp > maxd) {
+ maxd = sdisp;
+ k = i;
+ }
+ if (error[i] < e)
+ e = error[i];
+ }
+ if (nlist <= NTP_MINCLOCK || maxd <= e ||
+ peer_list[k]->flags & FLAG_PREFER)
+ break;
+ for (j = k + 1; j < nlist; j++) {
+ peer_list[j - 1] = peer_list[j];
+ error[j - 1] = error[j];
+ }
+ nlist--;
+ }
+
+#ifdef DEBUG
+ if (debug > 1) {
+ for (i = 0; i < nlist; i++)
+ printf("select: survivor %s offset %s, cdist %s\n",
+ ntoa(&peer_list[i]->srcadr),
+ lfptoa(&peer_list[i]->offset, 6),
+ fptoa(synch[i], 5));
+ }
+#endif
+
+ /*
+ * What remains is a list of not greater than NTP_MINCLOCK
+ * peers. We want only a peer at the lowest stratum to become
+ * the system peer, although all survivors are eligible for the
+ * combining algorithm. First record their order, diddle the
+ * flags and clamp the poll intervals. Then, consider the peers
+ * at the lowest stratum. Of these, OR the leap bits on the
+ * assumption that, if some of them honk nonzero bits, they must
+ * know what they are doing. Also, check for prefer and pps
+ * peers. If a prefer peer is found within CLOCK_MAX, update the
+ * pps switch. Of the other peers not at the lowest stratum,
+ * check if the system peer is among them and, if found, zap
+ * him. We note that the head of the list is at the lowest
+ * stratum and that unsynchronized peers cannot survive this
+ * far.
+ */
+ leap_consensus = 0;
+ for (i = nlist - 1; i >= 0; i--) {
+ peer_list[i]->select = i + 1;
+ peer_list[i]->flags |= FLAG_SYSPEER;
+ poll_update(peer_list[i], peer_list[i]->hpoll,
+ POLL_RANDOMCHANGE);
+ if (peer_list[i]->stratum == peer_list[0]->stratum) {
+ leap_consensus |= peer_list[i]->leap;
+ if (peer_list[i]->refclktype == REFCLK_ATOM_PPS)
+ typepps = peer_list[i];
+ if (peer_list[i] == sys_peer)
+ typesystem = peer_list[i];
+ if (peer_list[i]->flags & FLAG_PREFER) {
+ typeprefer = peer_list[i];
+ if (typeprefer->soffset >= -CLOCK_MAX_FP &&
+ typeprefer->soffset < CLOCK_MAX_FP)
+ pps_update = 1;
+ }
+ } else {
+ if (peer_list[i] == sys_peer)
+ sys_peer = 0;
+ }
+ }
+
+ /*
+ * Mitigation rules of the game. There are several types of
+ * peers that make a difference here: (1) prefer local peers
+ * (type REFCLK_LOCALCLOCK with FLAG_PREFER) or prefer acts
+ * peers (type REFCLK_NIST_ATOM with FLAG_PREFER), (2) pps peers
+ * (type REFCLK_ATOM_PPS), (3) remaining prefer peers (flag
+ * FLAG_PREFER), (4) the existing system peer, if any, (5) the
+ * head of the survivor list. Note that only one peer can be
+ * declared prefer. The order of preference is in the order
+ * stated. Note that all of these must be at the lowest stratum,
+ * i.e., the stratum of the head of the survivor list.
+ */
+ osys_peer = sys_peer;
+ if (typeprefer && (typeprefer == typelocal || typeprefer ==
+ typeacts || !typepps)) {
+ sys_peer = typeprefer;
+ sys_peer->selectdisp = 0;
+ sys_offset = sys_peer->offset;
+#ifdef DEBUG
+ if (debug)
+ printf("select: prefer offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ } else if (typepps) {
+ sys_peer = typepps;
+ sys_peer->selectdisp = 0;
+ sys_offset = sys_peer->offset;
+#ifdef DEBUG
+ if (debug)
+ printf("select: pps offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ } else {
+ if (!typesystem)
+ sys_peer = peer_list[0];
+ clock_combine(peer_list, nlist);
+#ifdef DEBUG
+ if (debug)
+ printf("select: combine offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ }
+
+ /*
+ * If we got a new system peer from all of this, report the
+ * event and clamp the system poll interval.
+ */
+ if (osys_peer != sys_peer) {
+ sys_poll = sys_peer->minpoll;
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+ }
+}
+
+/*
+ * clock_combine - combine offsets from selected peers
+ *
+ * Note: this routine uses only those peers at the lowest stratum.
+ * Strictly speaking, this is at variance with the spec.
+ */
+void
+clock_combine(peers, npeers)
+ struct peer **peers;
+ int npeers;
+{
+ register int i, j, k;
+ register u_fp a, b, d;
+ u_fp synch[NTP_MAXCLOCK];
+ l_fp coffset[NTP_MAXCLOCK];
+ l_fp diff;
+
+ /*
+ * Sort the offsets by synch distance.
+ */
+ k = 0;
+ for (i = 0; i < npeers; i++) {
+ if (peers[i]->stratum > sys_peer->stratum)
+ continue;
+ d = peers[i]->synch;
+ for (j = k; j > 0; j--) {
+ if (synch[j - 1] <= d)
+ break;
+ synch[j] = synch[j - 1];
+ coffset[j] = coffset[j - 1];
+ }
+ synch[j] = d;
+ coffset[j] = peers[i]->offset;
+ k++;
+ }
+
+ /*
+ * Succesively combine the two offsets with the highest
+ * distance and enter the result into the sorted list.
+ */
+ for (i = k - 2; i >= 0; i--) {
+ /*
+ * The possible weights for the most distant offset
+ * are 1/2, 1/4, 1/8 and zero. We combine the synch
+ * distances as if they were variances of the offsets;
+ * the given weights allow us to stay within 16/15 of
+ * the optimum combined variance at each step, and
+ * within 8/7 on any series.
+ *
+ * The breakeven points for the weigths are found
+ * where the smaller distance is 3/8, 3/16 and 1/16
+ * of the sum, respectively.
+ */
+ d = synch[i];
+ a = (d + synch[i + 1]) >> 2; /* (d1+d2)/4 */
+ b = a>>1; /* (d1+d2)/8 */
+ if (d <= (b>>1)) /* d1 <= (d1+d2)/16 */
+ /*
+ * Below 1/16, no combination is done,
+ * we just drop the distant offset.
+ */
+ continue;
+
+ /*
+ * The offsets are combined by shifting their
+ * difference the appropriate number of times and
+ * adding it back in.
+ */
+ diff = coffset[i + 1];
+ L_SUB(&diff, &coffset[i]);
+ L_RSHIFT(&diff);
+ if (d >= a + b) { /* d1 >= 3(d1+d2)/8 */
+ /*
+ * Above 3/8, the weight is 1/2, and the
+ * combined distance is (d1+d2)/4
+ */
+ d = a;
+ } else {
+ a >>= 2; /* (d1+d2)/16 */
+ L_RSHIFT(&diff);
+ if (d >= a + b) { /* d1 >= 3(d1+d2)/16 */
+ /*
+ * Between 3/16 and 3/8, the weight
+ * is 1/4, and the combined distance
+ * is (9d1+d2)/16 = d1/2 + (d1+d2)/16
+ */
+ d = (d>>1) + a;
+ } else {
+ /*
+ * Between 1/16 and 3/16, the weight
+ * is 1/8, and the combined distance
+ * is (49d1+d2)/64 = 3d1/4+(d1+d2)/64
+ * (We know d > a, so the shift is safe).
+ */
+ L_RSHIFT(&diff);
+ d -= (d - a)>>2;
+ }
+ }
+ /*
+ * Now we can make the combined offset and insert it
+ * in the list.
+ */
+ L_ADD(&diff, &coffset[i]);
+ for (j = i; j > 0; j--) {
+ if (d >= synch[j - 1])
+ break;
+ synch[j] = synch[j - 1];
+ coffset[j] = coffset[j - 1];
+ }
+ synch[j] = d;
+ coffset[j] = diff;
+ }
+
+ /*
+ * The result is put where clock_update() can find it.
+ */
+ sys_offset = coffset[0];
+}
+
+
+/*
+ * fast_xmit - fast path send for stateless (non-)associations
+ */
+void
+fast_xmit(rbufp, rmode, authentic)
+ struct recvbuf *rbufp;
+ int rmode;
+ int authentic;
+{
+ struct pkt xpkt;
+ register struct pkt *rpkt;
+ u_char xmode;
+ u_short xkey = 0;
+ int docrypt = 0;
+ l_fp xmt_ts;
+ u_fp precision;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("fast_xmit(%s, %d)\n", ntoa(&rbufp->recv_srcadr), rmode);
+#endif
+
+ /*
+ * Make up new packet and send it quick
+ */
+ rpkt = &rbufp->recv_pkt;
+ if (rmode == MODE_ACTIVE)
+ xmode = MODE_PASSIVE;
+ else
+ xmode = MODE_SERVER;
+
+ if (rbufp->recv_length >= LEN_PKT_MAC) {
+ docrypt = rbufp->recv_length - LEN_PKT_NOMAC;
+ if (authentic)
+ xkey = ntohl(rpkt->keyid);
+ }
+
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = max(NTP_MINPOLL, rpkt->ppoll);
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(sys_rootdelay);
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ xpkt.rootdispersion = HTONS_FP(sys_rootdispersion +
+ precision + LFPTOFP(&sys_refskew));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+
+ /*
+ * If we are encrypting, do it. Else don't. Easy.
+ */
+ if (docrypt) {
+ int maclen;
+
+ xpkt.keyid = htonl(xkey);
+ auth1crypt(xkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ get_systime(&xmt_ts);
+ L_ADDUF(&xmt_ts, sys_authdelay);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ maclen = auth2crypt(xkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -9, &xpkt,
+ LEN_PKT_NOMAC + maclen);
+ } else {
+ /*
+ * Get xmt timestamp, then send it without mac field
+ */
+ get_systime(&xmt_ts);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -10, &xpkt,
+ LEN_PKT_NOMAC);
+ }
+}
+
+/*
+ * Find the precision of this particular machine
+ */
+#define DUSECS 1000000 /* us in a s */
+#define HUSECS (1 << 20) /* approx DUSECS for shifting etc */
+#define MINSTEP 5 /* minimum clock increment (ys) */
+#define MAXSTEP 20000 /* maximum clock increment (us) */
+#define MINLOOPS 5 /* minimum number of step samples */
+
+/*
+ * This routine calculates the differences between successive calls to
+ * gettimeofday(). If a difference is less than zero, the us field
+ * has rolled over to the next second, so we add a second in us. If
+ * the difference is greater than zero and less than MINSTEP, the
+ * clock has been advanced by a small amount to avoid standing still.
+ * If the clock has advanced by a greater amount, then a timer interrupt
+ * has occurred and this amount represents the precision of the clock.
+ * In order to guard against spurious values, which could occur if we
+ * happen to hit a fat interrupt, we do this for MINLOOPS times and
+ * keep the minimum value obtained.
+ */
+int default_get_precision()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ long usec;
+
+ usec = 0;
+ val = MAXSTEP;
+ GETTIMEOFDAY(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = 0; i < MINLOOPS && usec < HUSECS;) {
+ GETTIMEOFDAY(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ last = tp.tv_usec;
+ if (diff < 0)
+ diff += DUSECS;
+ usec += diff;
+ if (diff > MINSTEP) {
+ i++;
+ if (diff < val)
+ val = diff;
+ }
+ }
+ syslog(LOG_INFO, "precision = %d usec", val);
+ if (usec >= HUSECS)
+ val = MINSTEP; /* val <= MINSTEP; fast machine */
+ diff = HUSECS;
+ for (i = 0; diff > val; i--)
+ diff >>= 1;
+ return (i);
+}
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto()
+{
+ l_fp dummy;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, don't authenticate.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ sys_precision = (s_char)default_get_precision();
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ sys_refid = 0;
+ L_CLR(&sys_reftime);
+ sys_refskew.l_i = NTP_MAXSKEW; sys_refskew.l_f = 0;
+ sys_peer = 0;
+ sys_poll = NTP_MINPOLL;
+ get_systime(&dummy);
+ sys_lastselect = sys_clock;
+
+ sys_bclient = 0;
+ sys_bdelay = DEFBROADDELAY;
+ sys_authenticate = 0;
+ sys_authdelay = DEFAUTHDELAY;
+
+ sys_stattime = 0;
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_badlength = 0;
+ sys_unknownversion = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+
+ /*
+ * Default these to enable
+ */
+ pll_enable = 1;
+ stats_control = 1;
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(item, value)
+ int item;
+ u_long value;
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ switch (item) {
+ case PROTO_PLL:
+ /*
+ * Turn on/off pll clock correction
+ */
+ pll_enable = (int)value;
+ break;
+
+ case PROTO_MONITOR:
+ /*
+ * Turn on/off monitoring
+ */
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ case PROTO_FILEGEN:
+ /*
+ * Turn on/off statistics
+ */
+ stats_control = (int)value;
+ break;
+
+ case PROTO_BROADCLIENT:
+ /*
+ * Turn on/off facility to listen to broadcasts
+ */
+ sys_bclient = (int)value;
+ if (value)
+ io_setbclient();
+ else
+ io_unsetbclient();
+ break;
+
+ case PROTO_MULTICAST_ADD:
+ /*
+ * Add muliticast group address
+ */
+ sys_bclient = 1;
+ io_multicast_add(value);
+ break;
+
+ case PROTO_MULTICAST_DEL:
+ /*
+ * Delete multicast group address
+ */
+ sys_bclient = 1;
+ io_multicast_del(value);
+ break;
+
+ case PROTO_PRECISION:
+ /*
+ * Set system precision
+ */
+ sys_precision = (s_char)value;
+ break;
+
+ case PROTO_BROADDELAY:
+ /*
+ * Set default broadcast delay (s_fp)
+ */
+ if (sys_bdelay < 0)
+ sys_bdelay = -(-value >> 16);
+ else
+ sys_bdelay = value >> 16;
+ break;
+
+ case PROTO_AUTHENTICATE:
+ /*
+ * Specify the use of authenticated data
+ */
+ sys_authenticate = (int)value;
+ break;
+
+
+ case PROTO_AUTHDELAY:
+ /*
+ * Set authentication delay (l_fp fraction)
+ */
+ sys_authdelay = value;
+ break;
+
+ default:
+ /*
+ * Log this error
+ */
+ syslog(LOG_ERR, "proto_config: illegal item %d, value %ld",
+ item, value);
+ break;
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats()
+{
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_unknownversion = 0;
+ sys_badlength = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+ sys_stattime = current_time;
+ sys_limitrejected = 0;
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_refclock.c b/usr.sbin/xntpd/xntpd/ntp_refclock.c
new file mode 100644
index 0000000..29c80d9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_refclock.c
@@ -0,0 +1,1286 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef REFCLOCK
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with a
+ * reference clock, however, we replace the transmit, receive and packet
+ * procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, edit input lines to extract
+ * embedded timestamps and to peform various debugging functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants of
+ * the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes the
+ * receive timestamp, exception report, statistics tallies, etc. In
+ * addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a pointer
+ * to the refclockproc structure, which in turn containes a pointer to
+ * the unit structure, if used. In addition, some routines expect an
+ * address in the dotted quad form 127.127.t.u, where t is the clock
+ * type and u the unit. A table typeunit[type][unit] contains the peer
+ * structure pointer for each configured clock type and unit.
+ *
+ * Most drivers support the 1-pps signal provided by some radios and
+ * connected via a level converted described in the gadget directory.
+ * The signal is captured using a separate, dedicated serial port and
+ * the tty_clk line discipline/streams modules described in the kernel
+ * directory. For the highest precision, the signal is captured using
+ * the carrier-detect line of the same serial port using the ppsclock
+ * streams module described in the ppsclock directory.
+ */
+#define REFCLOCKMAXDISPERSE (FP_SECOND/4) /* max sample dispersion */
+#define MAXUNIT 44 /* max units */
+#ifndef CLKLDISC
+#define CLKLDISC 10 /* XXX temp tty_clk line discipline */
+#endif
+#ifndef CHULDISC
+#define CHULDISC 10 /* XXX temp tty_chu line discipline */
+#endif
+
+/*
+ * The refclock configuration table. Imported from refclock_conf
+ */
+extern struct refclock *refclock_conf[];
+extern u_char num_refclock_conf;
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+extern struct interface *loopback_interface;
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from the main and peer modules. We use the same algorithm
+ * for spacing out timers at configuration time that the peer module
+ * does.
+ */
+extern u_long init_peer_starttime;
+extern int initializing;
+extern int debug;
+
+/*
+ * Type/unit peer index. Used to find the peer structure for control and
+ * debugging. When all clock drivers have been converted to new style,
+ * this dissapears.
+ */
+static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];
+
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(peer, code)
+ struct peer *peer;
+ u_char code;
+{
+ struct refclockproc *pp;
+
+ if (!(pp = peer->procptr))
+ return;
+ if (code == CEVNT_BADREPLY)
+ pp->badformat++;
+ if (code == CEVNT_BADTIME)
+ pp->baddata++;
+ if (code == CEVNT_TIMEOUT)
+ pp->noreply++;
+ if (pp->currentstatus != code) {
+ pp->currentstatus = code;
+ if (code == CEVNT_NOMINAL)
+ return;
+ pp->lastevent = code;
+ if (code == CEVNT_FAULT)
+ syslog(LOG_ERR,
+ "clock %s fault %x", ntoa(&peer->srcadr), code);
+ else {
+ syslog(LOG_INFO,
+ "clock %s event %x", ntoa(&peer->srcadr), code);
+ }
+ }
+}
+
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock()
+{
+ int i, j;
+
+ for (i = 0; i < num_refclock_conf; i++) {
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+ for (j = 0; j < MAXUNIT; j++)
+ typeunit[i][j] = 0;
+ }
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it * down first.
+ */
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ syslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ syslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+ refclock_unpeer(peer);
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ if (!(pp = (struct refclockproc *)
+ emalloc(sizeof(struct refclockproc))))
+ return (0);
+ memset((char *)pp, 0, sizeof(struct refclockproc));
+ typeunit[clktype][unit] = peer;
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->event_timer.peer = peer;
+ peer->event_timer.event_handler = refclock_transmit;
+ pp->type = clktype;
+ pp->timestarted = current_time;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ peer->maxpoll = peer->minpoll;
+
+ /*
+ * Do driver dependent initialization
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ free(pp);
+ return (0);
+ }
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->maxpoll;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+
+ /*
+ * Set up the timeout for polling and reachability determination
+ */
+ if (initializing) {
+ init_peer_starttime += (1 << EVENT_TIMEOUT);
+ if (init_peer_starttime >= (1 << peer->minpoll))
+ init_peer_starttime = (1 << EVENT_TIMEOUT);
+ peer->event_timer.event_time = init_peer_starttime;
+ } else {
+ peer->event_timer.event_time = current_time +
+ (1 << peer->hpoll);
+ }
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (!peer->procptr)
+ return;
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = 0;
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ u_char opeer_reach;
+
+ pp = peer->procptr;
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+
+ /*
+ * The transmit procedure is supposed to freeze a timestamp.
+ * Get one just for fun, and to tell when we last were here.
+ */
+ get_systime(&peer->xmt);
+
+ /*
+ * Fiddle reachability.
+ */
+ opeer_reach = peer->reach;
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+ /*
+ * Clear this one out. No need to redo selection since
+ * this fellow will definitely be suffering from
+ * dispersion madness.
+ */
+ if (opeer_reach != 0) {
+ peer_clear(peer);
+ peer->timereachable = current_time;
+ report_event(EVNT_UNREACH, peer);
+ }
+
+ /*
+ * Update reachability and poll variables
+ */
+ } else if ((opeer_reach & 3) == 0) {
+ l_fp off;
+
+ if (peer->valid > 0)
+ peer->valid--;
+ L_CLR(&off);
+ clock_filter(peer, &off, 0, NTP_MAXDISPERSE);
+ if (peer->flags & FLAG_SYSPEER)
+ clock_select();
+ } else if (peer->valid < NTP_SHIFT)
+ peer->valid++;
+
+ /*
+ * If he wants to be polled, do it. New style drivers do not use
+ * the unit argument, since the fudge stuff is in the
+ * refclockproc structure.
+ */
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+
+ /*
+ * Finally, reset the timer
+ */
+ peer->event_timer.event_time += (1 << peer->hpoll);
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+}
+
+
+/*
+ * Compare two l_fp's - used with qsort()
+ */
+static int
+refclock_cmpl_fp(p1, p2)
+ register void *p1, *p2; /* l_fp to compare */
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+
+/*
+ * refclock_process - process a pile of samples from the clock
+ *
+ * This routine converts the timecode in the form days, hours, miinutes,
+ * seconds, milliseconds/microseconds to internal timestamp format. It
+ * then calculates the difference from the receive timestamp and
+ * assembles the samples in a shift register. It implements a recursive
+ * median filter to suppress spikes in the data, as well as determine a
+ * rough dispersion estimate. A configuration constant time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns one if success and zero if
+ * failure due to invalid timecode data or very noisy offsets.
+ */
+int
+refclock_process(pp, nstart, nskeep)
+ struct refclockproc *pp; /* peer structure pointer */
+ int nstart; /* stages of median filter */
+ int nskeep; /* stages after outlyer trim */
+{
+ int i, n;
+ l_fp offset, median, lftmp;
+ l_fp off[MAXSTAGE];
+ u_fp disp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ pp->nstages = nstart;
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &pp->lastref.l_ui))
+ return (0);
+ if (pp->usec) {
+ TVUTOTSF(pp->usec, pp->lastref.l_uf);
+ } else {
+ MSUTOTSF(pp->msec, pp->lastref.l_uf);
+ }
+
+ /*
+ * Subtract the receive timestamp from the timecode timestamp
+ * to form the raw offset. Insert in the median filter shift
+ * register.
+ */
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ offset = pp->lastref;
+ L_SUB(&offset, &pp->lastrec);
+ pp->filter[i] = offset;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+
+ /*
+ * Copy the raw offsets and sort into ascending order
+ */
+ for (i = 0; i < pp->nstages; i++)
+ off[i] = pp->filter[i];
+ qsort((char *)off, pp->nstages, sizeof(l_fp), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of nstages samples until
+ * nskeep samples remain.
+ */
+ i = 0;
+ n = pp->nstages;
+ while ((n - i) > nskeep) {
+ lftmp = off[n - 1];
+ median = off[(n + i) / 2];
+ L_SUB(&lftmp, &median);
+ L_SUB(&median, &off[i]);
+ if (L_ISHIS(&median, &lftmp)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. Add to this the time since
+ * the last clock update, which represents the dispersion
+ * increase with time. We know that NTP_MAXSKEW is 16. If the
+ * sum is greater than the allowed sample dispersion, bail out.
+ * If the loop is unlocked, return the most recent offset;
+ * otherwise, return the median offset. In either case include
+ * the configured fudgetime1 adjustment.
+ */
+ lftmp = off[n - 1];
+ L_SUB(&lftmp, &off[i]);
+ disp = LFPTOFP(&lftmp) + current_time - pp->lasttime;
+ if (disp > REFCLOCKMAXDISPERSE)
+ return (0);
+ pp->offset = offset;
+ L_ADD(&pp->offset, &pp->fudgetime1);
+ pp->dispersion = disp;
+ return (1);
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(peer, offset, delay, dispersion, reftime, rectime, leap)
+ struct peer *peer; /* peer structure pointer */
+ l_fp *offset; /* computed offset (s) */
+ s_fp delay; /* computed delay to peer */
+ u_fp dispersion; /* computed dispersion to peer */
+ l_fp *reftime; /* time at last clock update */
+ l_fp *rectime; /* time at last peer update */
+ int leap; /* synchronization/leap code */
+{
+ int restrict;
+ int trustable;
+ u_fp precision;
+
+ peer->received++;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: %s %s %s %s)\n",
+ ntoa(&peer->srcadr), lfptoa(offset, 6),
+ fptoa(delay, 5), ufptoa(dispersion, 5));
+#endif
+
+ /*
+ * The authentication and access-control machinery works, but
+ * its utility may be questionable.
+ */
+ restrict = restrictions(&peer->srcadr);
+ if (restrict & (RES_IGNORE|RES_DONTSERVE))
+ return;
+ peer->processed++;
+ peer->timereceived = current_time;
+ if (restrict & RES_DONTTRUST)
+ trustable = 0;
+ else
+ trustable = 1;
+
+ if (peer->flags & FLAG_AUTHENABLE) {
+ if (trustable)
+ peer->flags |= FLAG_AUTHENTIC;
+ else
+ peer->flags &= ~FLAG_AUTHENTIC;
+ }
+ peer->leap = leap;
+
+ /*
+ * Set the timestamps. rec and org are in local time, while ref
+ * is in timecode time.
+ */
+ peer->rec = peer->org = *rectime;
+ peer->reftime = *reftime;
+
+ /*
+ * If the interface has been set to any_interface, set it to the
+ * loopback address if we have one. This is so that peers which
+ * are unreachable are easy to see in the peer display.
+ */
+ if (peer->dstadr == any_interface && loopback_interface != 0)
+ peer->dstadr = loopback_interface;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Abandon ship if the radio came bum. We only got this far
+ * in order to make pretty billboards, even if bum.
+ */
+ if (leap == LEAP_NOTINSYNC)
+ return;
+ /*
+ * If this guy was previously unreachable, report him
+ * reachable.
+ */
+ if (peer->reach == 0) report_event(EVNT_REACH, peer);
+ peer->reach |= 1;
+
+ /*
+ * Give the data to the clock filter and update the clock. Note
+ * the clock reading precision initialized by the driver is
+ * added at this point.
+ */
+ precision = FP_SECOND >> -(int)peer->precision;
+ if (precision == 0)
+ precision = 1;
+ refclock_report(peer, CEVNT_NOMINAL);
+ clock_filter(peer, offset, delay, dispersion + precision);
+ clock_update(peer);
+}
+
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * removes the parity bit and control characters. If a timestamp is
+ * present in the timecode, as produced by the tty_clk line
+ * discipline/streams module, it returns that as the timestamp;
+ * otherwise, it returns the buffer timestamp. The routine return code
+ * is the number of characters in the line.
+ */
+int
+refclock_gtlin(rbufp, lineptr, bmax, tsptr)
+ struct recvbuf *rbufp; /* receive buffer pointer */
+ char *lineptr; /* current line pointer */
+ int bmax; /* remaining characters in line */
+ l_fp *tsptr; /* pointer to timestamp returned */
+{
+ char *dpt, *dpend, *dp;
+ int i;
+ l_fp trtmp, tstmp;
+ char c;
+
+ /*
+ * Check for the presence of a timestamp left by the tty_clock
+ * line discipline/streams module and, if present, use that
+ * instead of the buffer timestamp captured by the I/O routines.
+ * We recognize a timestamp by noting its value is earlier than
+ * the buffer timestamp, but not more than one second earlier.
+ */
+ dpt = (char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ trtmp = rbufp->recv_time;
+ if (dpend >= dpt + 8) {
+ if (buftvtots(dpend - 8, &tstmp)) {
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug) {
+ printf(
+ "refclock_gtlin: fd %d ldisc %s",
+ rbufp->fd,
+ lfptoa(&trtmp, 6));
+ gettstamp(&trtmp);
+ L_SUB(&trtmp, &tstmp);
+ printf(" sigio %s\n",
+ lfptoa(&trtmp, 6));
+ }
+#endif
+ dpend -= 8;
+ trtmp = tstmp;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+ }
+
+ /*
+ * Edit timecode to remove control chars. Don't monkey with the
+ * line buffer if the input buffer contains no ASCII printing
+ * characters.
+ */
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++) {
+ c = *dpt & 0x7f;
+ if (c >= ' ')
+ *dp++ = c;
+ }
+ i = dp - lineptr;
+ if (i > 0)
+ *dp = '\0';
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
+#endif
+ *tsptr = trtmp;
+ return (i);
+}
+
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if success and zero if failure.
+ */
+int
+refclock_open(dev, speed, flags)
+ char *dev; /* device name pointer */
+ int speed; /* serial port speed (code) */
+ int flags; /* line discipline flags */
+{
+ int fd;
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+#ifdef HAVE_MODEM_CONTROL
+ u_long ltemp;
+#endif /* HAVE_MODEM_CONTROL */
+
+ /*
+ * Open serial port and set default options
+ */
+ fd = open(dev, O_RDWR, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "refclock_open: %s: %m", dev);
+ return (0);
+ }
+
+ /*
+ * The following sections initialize the serial line port in
+ * canonical (line-oriented) mode and set the specified line
+ * speed, 8 bits and no parity. The modem control, break, erase
+ * and kill functions are normally disabled. There is a
+ * different section for each terminal interface, as selected at
+ * compile time.
+ */
+ ttyp = &ttyb;
+#ifdef HAVE_TERMIOS
+
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcgetattr %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ (void)cfsetispeed(&ttyb, speed);
+ (void)cfsetospeed(&ttyb, speed);
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+#ifdef HAVE_MODEM_CONTROL
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET %m", fd);
+#if DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status %lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* HAVE_MODEM_CONTROL */
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcsetattr %m", fd);
+ return (0);
+ }
+ if (tcflush(fd, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcflush %m", fd);
+ return (0);
+ }
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TCGETA %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+#ifdef HAVE_MODEM_CONTROL
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET %m", fd);
+#if DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status %lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* HAVE_MODEM_CONTROL */
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TCSETA %m", fd);
+ return (0);
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCGETP %m", fd);
+ return (0);
+ }
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: TIOCSETP %m");
+ return (0);
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ if (!refclock_ioctl(fd, flags)) {
+ (void)close(fd);
+ syslog(LOG_ERR, "refclock_open: fd %d ioctl fails",
+ fd);
+ return (0);
+ }
+ return (fd);
+}
+
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up the tty_clk, chu_clk and ppsclock streams module/line discipline,
+ * if compiled in the daemon and requested in the call. The routine
+ * returns one if success and zero if failure.
+ */
+int
+refclock_ioctl(fd, flags)
+ int fd; /* file descriptor */
+ int flags; /* line discipline flags */
+{
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_ioctl: fd %d flags %x\n",
+ fd, flags);
+#endif
+
+ /*
+ * The following sections select optional features, such as
+ * modem control, line discipline and so forth. Some require
+ * specific operating system support in the form of streams
+ * modules, which can be loaded and unloaded at run time without
+ * rebooting the kernel, or line discipline modules, which must
+ * be compiled in the kernel. The streams modules require System
+ * V STREAMS support, while the line discipline modules require
+ * 4.3bsd or later. The checking frenzy is attenuated here,
+ * since the device is already open.
+ *
+ * Note that both the clk and ppsclock modules are optional; the
+ * dang thing still works, but the accuracy improvement using
+ * them will not be available. The ppsclock module is associated
+ * with a specific, declared line and should be used only once.
+ * If requested, the chu module is mandatory, since the driver
+ * will not work without it.
+ *
+ * Use the LDISC_PPS option ONLY with Sun baseboard ttya or
+ * ttyb. Using it with the SPIF multipexor crashes the kernel.
+ */
+ if (flags == 0)
+ return (1);
+
+#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS))
+ if (flags & (LDISC_CLK | LDISC_CHU | LDISC_PPS | LDISC_ACTS))
+ syslog(LOG_ERR,
+ "refclock_ioctl: unsupported terminal interface");
+ return (0);
+#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */
+
+ ttyp = &ttyb;
+
+#ifdef STREAM
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module and System V STREAMS
+ * support.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ if (ioctl(fd, I_PUSH, "clk") < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk streams module unavailable");
+ else {
+ char *str;
+
+ if (flags & LDISC_PPS)
+ str = "\377";
+ else if (flags & LDISC_ACTS)
+ str = "*";
+ else
+ str = "\n";
+ if (ioctl(fd, CLK_SETSTR, str) < 0)
+ syslog(LOG_ERR,
+ "refclock_ioctl: CLK_SETSTR %m");
+ }
+ }
+
+ /*
+ * The ACTS line discipline requires additional line-ending
+ * character '*'.
+ */
+ if (flags & LDISC_ACTS) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_cc[VEOL] = '*';
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk streams module unsupported");
+#endif /* CLK */
+#ifdef CHU
+
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu streams module and System V
+ * STREAMS support.
+ */
+ if (flags & LDISC_CHU) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ (void)tcflush(fd, TCIOFLUSH);
+ while (ioctl(fd, I_POP, 0) >= 0);
+ if (ioctl(fd, I_PUSH, "chu") < 0) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu streams module unavailable");
+ return (0);
+ }
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu streams module unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#ifdef PPS
+
+ /*
+ * The PPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and System V STREAMS
+ * support.
+ */
+ if (flags & LDISC_PPS) {
+ if (fdpps != -1) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: ppsclock already configured");
+ return (0);
+ }
+ if (ioctl(fd, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional ppsclock streams module unavailable");
+ else
+ fdpps = fd;
+ }
+#else
+ if (flags & LDISC_PPS)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional ppsclock streams module unsupported");
+#endif /* PPS */
+
+#else /* STREAM */
+
+#ifdef HAVE_TERMIOS
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level. It
+ * requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ if (flags & LDISC_CLKPPS)
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\377';
+ else if (flags & LDISC_ACTS) {
+ ttyp->c_cc[VERASE] = '*';
+ ttyp->c_cc[VKILL] = '#';
+ } else
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\n';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ ttyp->c_line = CLKLDISC;
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ (void)tcflush(fd, TCIOFLUSH);
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unsupported");
+#endif /* CLK */
+#ifdef CHU
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu line disciplne and 4.3bsd
+ * or later.
+ */
+ if (flags & LDISC_CHU) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\r';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ ttyp->c_line = CHULDISC;
+ (void)tcsetattr(fd, TCSANOW, ttyp) < 0);
+ (void)tcflush(fd, TCIOFLUSH);
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_BSD_TTYS
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level. It
+ * requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ int ldisc = CLKLDISC;
+
+ (void)ioctl(fd, TIOCGETP, (char *)ttyp);
+ if (flags & LDISC_CLKPPS)
+ ttyp->sg_erase = ttyp->sg_kill = '\377';
+ else if (flags & LDISC_ACTS) {
+ ttyp->sg_erase = '*';
+ ttyp->sg_kill = '#';
+ } else
+ ttyp->sg_erase = ttyp->sg_kill = '\r';
+ ttyp->sg_flags = RAW;
+ (void)ioctl(fd, TIOCSETP, ttyp);
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unavailable");
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unsupported");
+
+#endif /* CLK */
+#ifdef CHU
+
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu line disciplne and 4.3bsd
+ * or later.
+ */
+ if (flags & LDISC_CHU) {
+ int ldisc = CHULDISC;
+
+ (void)ioctl(fd, TIOCGETP, (char *)ttyp);
+ ttyp->sg_erase = ttyp->sg_kill = '\r';
+ ttyp->sg_flags = RAW;
+ (void)ioctl(fd, TIOCSETP, (char *)ttyp);
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unavailable");
+ return (0);
+ }
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#endif /* HAVE_BSD_TTYS */
+
+#endif /* STREAM */
+
+ return (1);
+}
+
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * xntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(srcadr, in, out)
+ struct sockaddr_in *srcadr;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ peer->stratum = in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ pp->refid = in->fudgeval2;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ if (in->flags & CLK_FLAG3)
+ (void)refclock_ioctl(pp->io.fd, LDISC_PPS);
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != 0) {
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG4;
+ out->fudgetime1 = pp->fudgetime1;
+ out->fudgetime2 = pp->fudgetime2;
+ out->fudgeval1 = peer->stratum;
+ out->fudgeval2 = pp->refid;
+ out->flags = pp->sloppyclockflag;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = pp->lencode;
+ out->lastcode = pp->lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * xntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(srcadr, bug)
+ struct sockaddr_in *srcadr; /* clock address */
+ struct refclockbug *bug; /* output structure */
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ int i;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->msec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+
+ bug->ntimes = pp->nstages + 3;
+ if (bug->ntimes > NCLKBUGTIMES)
+ bug->ntimes = NCLKBUGTIMES;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ UFPTOLFP(pp->dispersion, &bug->times[2]);
+ for (i = 0; i < bug->ntimes; i++)
+ bug->times[i + 3] = pp->filter[i];
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug);
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/ntp_request.c b/usr.sbin/xntpd/xntpd/ntp_request.c
new file mode 100644
index 0000000..9f58d22
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_request.c
@@ -0,0 +1,2453 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "signal.h"
+#include "ntp_request.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#ifdef KERNEL_PLL
+#ifdef HAVE_SYS_TIMEX_H
+#include <sys/timex.h>
+#else
+#include "sys/timex.h"
+#endif
+
+#ifndef NTP_SYSCALLS_LIBC
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#endif
+#endif /* KERNEL_PLL */
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item */
+ void (*handler)(); /* routine to handle request */
+};
+
+/*
+ * Universal request codes
+ */
+static struct req_proc univ_codes[] = {
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+static void req_ack P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static char * prepare_pkt P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_int));
+static char * more_pkt P((void));
+static void flush_pkt P((void));
+static void peer_list P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_list_sum P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mem_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void io_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void timer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void loop_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_conf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unconf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void clr_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long));
+static void do_monitor P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_nomonitor P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_ressubflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unrestrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void mon_getlist_0 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mon_getlist_1 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_peer P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_key_reread P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_dirty_hack P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void dont_dirty_hack P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void trust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void untrust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_trustkey P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void get_auth_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_auth_stats P((void));
+static void req_get_traps P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_set_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_clr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_setclr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void set_request_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_control_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void get_ctl_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void get_leap_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#ifdef KERNEL_PLL
+static void get_kernel_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_clock_fudge P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+static void set_precision P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#ifdef REFCLOCK
+static void get_clkbug_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+
+/*
+ * Xntpd request codes
+ */
+static struct req_proc xntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, peer_list },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, peer_list_sum },
+ { REQ_PEER_INFO, NOAUTH, sizeof(struct info_peer_list), peer_info },
+ { REQ_PEER_STATS, NOAUTH, sizeof(struct info_peer_list), peer_stats },
+ { REQ_SYS_INFO, NOAUTH, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, timer_stats },
+ { REQ_CONFIG, AUTH, sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_MONITOR, AUTH, 0, do_monitor },
+ { REQ_NOMONITOR, AUTH, 0, do_nomonitor },
+ { REQ_GET_RESTRICT, NOAUTH, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, mon_getlist_0 },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, mon_getlist_1 },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), reset_stats },
+ { REQ_RESET_PEER, AUTH, sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, do_key_reread },
+ { REQ_DO_DIRTY_HACK, AUTH, 0, do_dirty_hack },
+ { REQ_DONT_DIRTY_HACK, AUTH, 0, dont_dirty_hack },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, get_ctl_stats },
+ { REQ_GET_LEAPINFO, NOAUTH, 0, get_leap_info },
+ { REQ_SET_PRECISION, AUTH, sizeof(long), set_precision },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info },
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(U_LONG), get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(U_LONG), get_clkbug_info },
+#endif
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+u_long info_auth_keyid;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long numrequests; /* number of requests we've received */
+u_long numresppkts; /* number of resp packets sent with data */
+
+u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */
+ /* by the error code */
+
+#if defined(KERNEL_PLL) && !defined(NTP_SYSCALLS_LIBC)
+extern int syscall P((int, void *, ...));
+#endif /* KERNEL_PLL */
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from the main routines
+ */
+extern int debug;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_loopfilter.c
+ */
+extern int pll_control;
+extern int pll_enable;
+extern int pps_control;
+
+/*
+ * Imported from ntp_monitor.c
+ */
+extern int mon_enabled;
+
+/*
+ * Imported from ntp_util.c
+ */
+extern int stats_control;
+
+extern struct peer *peer_hash[];
+extern struct peer *sys_peer;
+
+/*
+ * A hack. To keep the authentication module clear of xntp-ism's, we
+ * include a time reset variable for its stats here.
+ */
+static u_long auth_timereset;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct resp_pkt rpkt;
+static int seqno;
+static int nitems;
+static int itemsize;
+static int databytes;
+static char exbuf[RESP_DATA_SIZE];
+static int usingexbuf;
+static struct sockaddr_in *toaddr;
+static struct interface *frominter;
+
+/*
+ * init_request - initialize request data
+ */
+void
+init_request()
+{
+ int i;
+
+ numrequests = 0;
+ numresppkts = 0;
+ auth_timereset = 0;
+ info_auth_keyid = 0; /* by default, can't do this */
+
+ for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++)
+ errorcounter[i] = 0;
+}
+
+
+/*
+ * req_ack - acknowledge request with no data
+ */
+static void
+req_ack(srcadr, inter, inpkt, errcode)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int errcode;
+{
+ /*
+ * fill in the fields
+ */
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0);
+ rpkt.auth_seq = AUTH_SEQ(0, 0);
+ rpkt.implementation = inpkt->implementation;
+ rpkt.request = inpkt->request;
+ rpkt.err_nitems = ERR_NITEMS(errcode, 0);
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+
+ /*
+ * send packet and bump counters
+ */
+ sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE);
+ errorcounter[errcode]++;
+}
+
+
+/*
+ * prepare_pkt - prepare response packet for transmission, return pointer
+ * to storage for data item.
+ */
+static char *
+prepare_pkt(srcadr, inter, pkt, structsize)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *pkt;
+ u_int structsize;
+{
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: preparing pkt\n");
+#endif
+
+ /*
+ * Fill in the implementation, reqest and itemsize fields
+ * since these won't change.
+ */
+ rpkt.implementation = pkt->implementation;
+ rpkt.request = pkt->request;
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize);
+
+ /*
+ * Compute the static data needed to carry on.
+ */
+ toaddr = srcadr;
+ frominter = inter;
+ seqno = 0;
+ nitems = 0;
+ itemsize = structsize;
+ databytes = 0;
+ usingexbuf = 0;
+
+ /*
+ * return the beginning of the packet buffer.
+ */
+ return &rpkt.data[0];
+}
+
+
+/*
+ * more_pkt - return a data pointer for a new item.
+ */
+static char *
+more_pkt()
+{
+ /*
+ * If we were using the extra buffer, send the packet.
+ */
+ if (usingexbuf) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: sending pkt\n");
+#endif
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+
+ /*
+ * Copy data out of exbuf into the packet.
+ */
+ memmove(&rpkt.data[0], exbuf, itemsize);
+ seqno++;
+ databytes = 0;
+ nitems = 0;
+ usingexbuf = 0;
+ }
+
+ databytes += itemsize;
+ nitems++;
+ if (databytes + itemsize <= RESP_DATA_SIZE) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: giving him more data\n");
+#endif
+ /*
+ * More room in packet. Give him the
+ * next address.
+ */
+ return &rpkt.data[databytes];
+ } else {
+ /*
+ * No room in packet. Give him the extra
+ * buffer unless this was the last in the sequence.
+ */
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: into extra buffer\n");
+#endif
+ if (seqno == MAXSEQ)
+ return (char *)0;
+ else {
+ usingexbuf = 1;
+ return exbuf;
+ }
+ }
+}
+
+
+/*
+ * flush_pkt - we're done, return remaining information.
+ */
+static void
+flush_pkt()
+{
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: flushing packet, %d items\n", nitems);
+#endif
+ /*
+ * Must send the last packet. If nothing in here and nothing
+ * has been sent, send an error saying no data to be found.
+ */
+ if (seqno == 0 && nitems == 0)
+ req_ack(toaddr, frominter, (struct req_pkt *)&rpkt,
+ INFO_ERR_NODATA);
+ else {
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+ }
+}
+
+
+
+/*
+ * process_private - process private mode (7) packets
+ */
+void
+process_private(rbufp, mod_okay)
+ struct recvbuf *rbufp;
+ int mod_okay;
+{
+ struct req_pkt *inpkt;
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_proc *proc;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("prepare_pkt: impl %d req %d\n",
+ inpkt->implementation, inpkt->request);
+#endif
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ if (ISRESPONSE(inpkt->rm_vn_mode)
+ || ISMORE(inpkt->rm_vn_mode)
+ || INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION
+ || INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION
+ || INFO_SEQ(inpkt->auth_seq) != 0
+ || INFO_ERR(inpkt->err_nitems) != 0
+ || INFO_MBZ(inpkt->mbz_itemsize) != 0
+ || rbufp->recv_length > REQ_LEN_MAC
+ || rbufp->recv_length < REQ_LEN_NOMAC) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+
+ /*
+ * Get the appropriate procedure list to search.
+ */
+ if (inpkt->implementation == IMPL_UNIV)
+ proc = univ_codes;
+ else if (inpkt->implementation == IMPL_XNTPD)
+ proc = xntp_codes;
+ else {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
+ return;
+ }
+
+
+ /*
+ * Search the list for the request codes. If it isn't one
+ * we know, return an error.
+ */
+ while (proc->request_code != NO_REQUEST) {
+ if (proc->request_code == (short) inpkt->request)
+ break;
+ proc++;
+ }
+ if (proc->request_code == NO_REQUEST) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("found request in tables\n");
+#endif
+
+ /*
+ * If we need to authenticate, do so. Note that an
+ * authenticatable packet must include a mac field, must
+ * have used key info_auth_keyid and must have included
+ * a time stamp in the appropriate field. The time stamp
+ * must be within INFO_TS_MAXSKEW of the receive
+ * time stamp.
+ */
+ if (proc->needs_auth) {
+ l_fp ftmp;
+
+ /*
+ * If this guy is restricted from doing this, don't let him
+ * If wrong key was used, or packet doesn't have mac, return.
+ */
+ if (!INFO_IS_AUTH(inpkt->auth_seq) || info_auth_keyid == 0
+ || ntohl(inpkt->keyid) != info_auth_keyid) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf(
+ "failed auth %d info_auth_keyid %lu pkt keyid %u\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ info_auth_keyid, ntohl(inpkt->keyid));
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ if (rbufp->recv_length > REQ_LEN_MAC) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("bad pkt length %d\n",
+ rbufp->recv_length);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (!mod_okay || !authhavekey(info_auth_keyid)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth mod_okay %d\n", mod_okay);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * calculate absolute time difference between xmit time stamp
+ * and receive time stamp. If too large, too bad.
+ */
+ NTOHL_FP(&inpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ if (L_ISNEG(&ftmp))
+ L_NEG(&ftmp);
+
+ if (ftmp.l_ui >= INFO_TS_MAXSKEW_UI) {
+ /*
+ * He's a loser. Tell him.
+ */
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * So far so good. See if decryption works out okay.
+ */
+ if (!authdecrypt(info_auth_keyid, (U_LONG *)inpkt,
+ REQ_LEN_NOMAC)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+ if (INFO_ITEMSIZE(inpkt->mbz_itemsize) != proc->sizeofitem) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (proc->sizeofitem != 0)
+ if (proc->sizeofitem*INFO_NITEMS(inpkt->err_nitems)
+ > sizeof(inpkt->data)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("process_private: all okay, into handler\n");
+#endif
+
+ /*
+ * Packet is okay. Call the handler to send him data.
+ */
+ (proc->handler)(srcadr, inter, inpkt);
+}
+
+
+/*
+ * peer_list - send a list of the peers
+ */
+static void
+peer_list(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ip;
+ register struct peer *pp;
+ register int i;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_list));
+ for (i = 0; i < HASH_SIZE && ip != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ip != 0) {
+ ip->address = pp->srcadr.sin_addr.s_addr;
+ ip->port = pp->srcadr.sin_port;
+ ip->hmode = pp->hmode;
+ ip->flags = 0;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip = (struct info_peer_list *)more_pkt();
+ pp = pp->next;
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_list_sum - return extended peer list
+ */
+static void
+peer_list_sum(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ register int i;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_summary));
+ for (i = 0; i < HASH_SIZE && ips != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ips != 0) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("sum: got one\n");
+#endif
+ ips->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 1 : 5;
+ ips->srcadr = pp->srcadr.sin_addr.s_addr;
+ ips->srcport = pp->srcadr.sin_port;
+ ips->stratum = pp->stratum;
+ ips->hpoll = pp->hpoll;
+ ips->ppoll = pp->ppoll;
+ ips->reach = pp->reach;
+ ips->flags = 0;
+ if (pp == sys_peer)
+ ips->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ips->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ips->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ips->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ips->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ips->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ips->flags |= INFO_FLAG_SHORTLIST;
+ ips->hmode = pp->hmode;
+ ips->delay = HTONS_FP(pp->delay);
+ HTONL_FP(&pp->offset, &ips->offset);
+ ips->dispersion = HTONS_FP(pp->dispersion);
+
+ pp = pp->next;
+ ips = (struct info_peer_summary *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_info - send information for one or more peers
+ */
+static void
+peer_info (srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer *ip;
+ register int items;
+ register int i, j;
+ struct sockaddr_in addr;
+ extern struct peer *sys_peer;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 2 : 6;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->leap = pp->leap;
+ ip->hmode = pp->hmode;
+ ip->keyid = pp->keyid;
+ ip->pkeyid = pp->pkeyid;
+ ip->stratum = pp->stratum;
+ ip->ppoll = pp->ppoll;
+ ip->hpoll = pp->hpoll;
+ ip->precision = pp->precision;
+ ip->version = pp->version;
+ ip->valid = pp->valid;
+ ip->reach = pp->reach;
+ ip->unreach = pp->unreach;
+ ip->flash = pp->flash;
+ ip->estbdelay = HTONS_FP(pp->estbdelay);
+ ip->ttl = pp->ttl;
+ ip->associd = htons(pp->associd);
+ ip->rootdelay = HTONS_FP(pp->rootdelay);
+ ip->rootdispersion = HTONS_FP(pp->rootdispersion);
+ ip->refid = pp->refid;
+ ip->timer = htonl(pp->event_timer.event_time - current_time);
+ HTONL_FP(&pp->reftime, &ip->reftime);
+ HTONL_FP(&pp->org, &ip->org);
+ HTONL_FP(&pp->rec, &ip->rec);
+ HTONL_FP(&pp->xmt, &ip->xmt);
+ j = pp->filter_nextpt - 1;
+ for (i = 0; i < NTP_SHIFT; i++, j--) {
+ if (j < 0)
+ j = NTP_SHIFT-1;
+ ip->filtdelay[i] = HTONS_FP(pp->filter_delay[j]);
+ HTONL_FP(&pp->filter_offset[j], &ip->filtoffset[i]);
+ ip->order[i] = (pp->filter_nextpt+NTP_SHIFT-1)
+ - pp->filter_order[i];
+ if (ip->order[i] >= NTP_SHIFT)
+ ip->order[i] -= NTP_SHIFT;
+ }
+ HTONL_FP(&pp->offset, &ip->offset);
+ ip->delay = HTONS_FP(pp->delay);
+ ip->dispersion = HTONS_FP(pp->dispersion);
+ ip->selectdisp = HTONS_FP(pp->selectdisp);
+ ip = (struct info_peer *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer_stats *ip;
+ register int items;
+ struct sockaddr_in addr;
+ extern struct peer *sys_peer;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 3 : 7;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->timereceived = htonl(current_time - pp->timereceived);
+ ip->timetosend
+ = htonl(pp->event_timer.event_time - current_time);
+ ip->timereachable = htonl(current_time - pp->timereachable);
+ ip->sent = htonl(pp->sent);
+ ip->processed = htonl(pp->processed);
+ ip->badauth = htonl(pp->badauth);
+ ip->bogusorg = htonl(pp->bogusorg);
+ ip->oldpkt = htonl(pp->oldpkt);
+ ip->seldisp = htonl(pp->seldisptoolarge);
+ ip->selbroken = htonl(pp->selbroken);
+ ip->candidate = pp->candidate;
+ ip = (struct info_peer_stats *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * sys_info - return system info
+ */
+static void
+sys_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_sys *is;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_char sys_leap;
+ extern u_char sys_stratum;
+ extern s_char sys_precision;
+ extern s_fp sys_rootdelay;
+ extern u_fp sys_rootdispersion;
+ extern u_long sys_refid;
+ extern l_fp sys_reftime;
+ extern u_char sys_poll;
+ extern struct peer *sys_peer;
+ extern int sys_bclient;
+ extern s_fp sys_bdelay;
+ extern int sys_authenticate;
+ extern u_long sys_authdelay;
+ extern u_fp clock_stability;
+ extern s_fp clock_frequency;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys));
+
+ if (sys_peer != 0) {
+ is->peer = NSRCADR(&sys_peer->srcadr);
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 0;
+ is->peer_mode = 0;
+ }
+ is->leap = sys_leap;
+ is->stratum = sys_stratum;
+ is->precision = sys_precision;
+ is->rootdelay = htonl(sys_rootdelay);
+ is->rootdispersion = htonl(sys_rootdispersion);
+ is->frequency = htonl(clock_frequency);
+ is->stability = htonl(clock_stability);
+ is->refid = sys_refid;
+ HTONL_FP(&sys_reftime, &is->reftime);
+
+ is->poll = sys_poll;
+
+ is->flags = 0;
+ if (sys_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (pll_enable)
+ is->flags |= INFO_FLAG_PLL;
+ if (pll_control)
+ is->flags |= INFO_FLAG_PLL_SYNC;
+ if (pps_control)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ if (stats_control)
+ is->flags |= INFO_FLAG_FILEGEN;
+ is->bdelay = HTONS_FP(sys_bdelay);
+ HTONL_UF(sys_authdelay, &is->authdelay);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * sys_stats - return system statistics
+ */
+static void
+sys_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_sys_stats *ss;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_long sys_stattime;
+ extern u_long sys_badstratum;
+ extern u_long sys_oldversionpkt;
+ extern u_long sys_newversionpkt;
+ extern u_long sys_unknownversion;
+ extern u_long sys_badlength;
+ extern u_long sys_processed;
+ extern u_long sys_badauth;
+ extern u_long sys_limitrejected;
+
+ ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys_stats));
+
+ ss->timeup = htonl(current_time);
+ ss->timereset = htonl(current_time - sys_stattime);
+ ss->badstratum = htonl(sys_badstratum);
+ ss->oldversionpkt = htonl(sys_oldversionpkt);
+ ss->newversionpkt = htonl(sys_newversionpkt);
+ ss->unknownversion = htonl(sys_unknownversion);
+ ss->badlength = htonl(sys_badlength);
+ ss->processed = htonl(sys_processed);
+ ss->badauth = htonl(sys_badauth);
+ ss->limitrejected = htonl(sys_limitrejected);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_mem_stats *ms;
+ register int i;
+
+ /*
+ * Importations from the peer module
+ */
+ extern int peer_hash_count[HASH_SIZE];
+ extern int peer_free_count;
+ extern u_long peer_timereset;
+ extern u_long findpeer_calls;
+ extern u_long peer_allocations;
+ extern u_long peer_demobilizations;
+ extern int total_peer_structs;
+
+ ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_mem_stats));
+
+ ms->timereset = htonl(current_time - peer_timereset);
+ ms->totalpeermem = htons((u_short)total_peer_structs);
+ ms->freepeermem = htons((u_short)peer_free_count);
+ ms->findpeer_calls = htonl(findpeer_calls);
+ ms->allocations = htonl(peer_allocations);
+ ms->demobilizations = htonl(peer_demobilizations);
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ if (peer_hash_count[i] > 255)
+ ms->hashcount[i] = 255;
+ else
+ ms->hashcount[i] = (u_char)peer_hash_count[i];
+ }
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * io_stats - return io statistics
+ */
+static void
+io_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_io_stats *io;
+
+ /*
+ * Importations from the io module
+ */
+ extern u_long io_timereset;
+ extern u_long full_recvbufs;
+ extern u_long free_recvbufs;
+ extern u_long total_recvbufs;
+ extern u_long lowater_additions;
+ extern u_long packets_dropped;
+ extern u_long packets_ignored;
+ extern u_long packets_received;
+ extern u_long packets_sent;
+ extern u_long packets_notsent;
+ extern u_long handler_calls;
+ extern u_long handler_pkts;
+
+ io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_io_stats));
+
+ io->timereset = htonl(current_time - io_timereset);
+ io->totalrecvbufs = htons((u_short) total_recvbufs);
+ io->freerecvbufs = htons((u_short) free_recvbufs);
+ io->fullrecvbufs = htons((u_short) full_recvbufs);
+ io->lowwater = htons((u_short) lowater_additions);
+ io->dropped = htonl(packets_dropped);
+ io->ignored = htonl(packets_ignored);
+ io->received = htonl(packets_received);
+ io->sent = htonl(packets_sent);
+ io->notsent = htonl(packets_notsent);
+ io->interrupts = htonl(handler_calls);
+ io->int_received = htonl(handler_pkts);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * timer_stats - return timer statistics
+ */
+static void
+timer_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_timer_stats *ts;
+
+ /*
+ * Importations from the timer module
+ */
+ extern u_long alarm_overflow;
+ extern u_long timer_timereset;
+ extern u_long timer_overflows;
+ extern u_long timer_xmtcalls;
+
+ ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_timer_stats));
+
+ ts->timereset = htonl(current_time - timer_timereset);
+ ts->alarms = htonl(alarm_overflow);
+ ts->overflows = htonl(timer_overflows);
+ ts->xmtcalls = htonl(timer_xmtcalls);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * loop_info - return the current state of the loop filter
+ */
+static void
+loop_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_loop *li;
+ l_fp tmp;
+
+ /*
+ * Importations from the loop filter module
+ */
+ extern l_fp last_offset;
+ extern s_fp drift_comp;
+ extern int tc_counter;
+ extern u_long last_time;
+
+ li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_loop));
+
+ HTONL_FP(&last_offset, &li->last_offset);
+ FPTOLFP(drift_comp, &tmp);
+ HTONL_FP(&tmp, &li->drift_comp);
+ li->compliance = htonl(tc_counter);
+ li->watchdog_timer = htonl(current_time - last_time);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * do_conf - add a peer to the configuration list
+ */
+static void
+do_conf(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_peer *cp;
+ register int items;
+ struct sockaddr_in peeraddr;
+ int fl;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+
+ fl = 0;
+ while (items-- > 0 && !fl) {
+ if (cp->version > NTP_VERSION
+ || cp->version < NTP_OLDVERSION)
+ fl = 1;
+ if (cp->hmode != MODE_ACTIVE
+ && cp->hmode != MODE_CLIENT
+ && cp->hmode != MODE_BROADCAST)
+ fl = 1;
+ if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER))
+ fl = 1;
+ cp++;
+ }
+
+ if (fl) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_in));
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ /*
+ * Make sure the address is valid
+ */
+#ifdef REFCLOCK
+ if (!ISREFCLOCKADR(&peeraddr) && ISBADADR(&peeraddr)) {
+#else
+ if (ISBADADR(&peeraddr)) {
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ fl = 0;
+ if (cp->flags & CONF_FLAG_AUTHENABLE)
+ fl |= FLAG_AUTHENABLE;
+ if (cp->flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, (struct interface *)0,
+ cp->hmode, cp->version, cp->minpoll, cp->maxpoll,
+ fl, cp->ttl, cp->keyid) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in peeraddr;
+ int bad, found;
+
+ /*
+ * This is a bit unstructured, but I like to be careful.
+ * We check to see that every peer exists and is actually
+ * configured. If so, we remove them. If not, we return
+ * an error.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ found = 0;
+ peer = (struct peer *)0;
+ while (!found) {
+ peer = findexistingpeer(&peeraddr, peer);
+ if (peer == (struct peer *)0)
+ break;
+ if (peer->flags & FLAG_CONFIG)
+ found = 1;
+ }
+ if (!found)
+ bad = 1;
+ cp++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer_unconfig(&peeraddr, (struct interface *)0);
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ setclr_flags(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * clr_sys_flag - clear system flags
+ */
+static void
+clr_sys_flag(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ setclr_flags(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * setclr_flags - do the grunge work of flag setting/clearing
+ */
+static void
+setclr_flags(srcadr, inter, inpkt, set)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ u_long set;
+{
+ register u_long flags;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct conf_sys_flags *)inpkt->data)->flags;
+
+ if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_AUTHENTICATE |
+ SYS_FLAG_PLL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set);
+ if (flags & SYS_FLAG_AUTHENTICATE)
+ proto_config(PROTO_AUTHENTICATE, set);
+ if (flags & SYS_FLAG_PLL)
+ proto_config(PROTO_PLL, set);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_monitor - turn on monitoring
+ */
+static void
+do_monitor(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ mon_start(MON_ON);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_nomonitor - turn off monitoring
+ */
+static void
+do_nomonitor(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ mon_stop(MON_ON);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_restrict *ir;
+ register struct restrictlist *rl;
+ extern struct restrictlist *restrictlist;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_restrict));
+ for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) {
+ ir->addr = htonl(rl->addr);
+ ir->mask = htonl(rl->mask);
+ ir->count = htonl(rl->count);
+ ir->flags = htons(rl->flags);
+ ir->mflags = htons(rl->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS);
+}
+
+
+
+/*
+ * do_ressubflags - remove flags from a restrict entry
+ */
+static void
+do_ressubflags(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG);
+}
+
+
+/*
+ * do_unrestrict - remove a restrict entry from the list
+ */
+static void
+do_unrestrict(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE);
+}
+
+
+
+
+
+/*
+ * do_restrict - do the dirty stuff of dealing with restrictions
+ */
+static void
+do_restrict(srcadr, inter, inpkt, op)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int op;
+{
+ register struct conf_restrict *cr;
+ register int items;
+ struct sockaddr_in matchaddr;
+ struct sockaddr_in matchmask;
+ int bad;
+
+ /*
+ * Do a check of the flags to make sure that only
+ * the NTPPORT flag is set, if any. If not, complain
+ * about it. Note we are very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ if (cr->mflags & ~(RESM_NTPONLY))
+ bad = 1;
+ if (cr->flags & ~(RES_ALLFLAGS))
+ bad = 1;
+ if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY))
+ bad = 1;
+ cr++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+ memset((char *)&matchaddr, 0, sizeof(struct sockaddr_in));
+ memset((char *)&matchmask, 0, sizeof(struct sockaddr_in));
+ matchaddr.sin_family = AF_INET;
+ matchmask.sin_family = AF_INET;
+
+ while (items-- > 0) {
+ matchaddr.sin_addr.s_addr = cr->addr;
+ matchmask.sin_addr.s_addr = cr->mask;
+ restrict(op, &matchaddr, &matchmask, cr->mflags,
+ cr->flags);
+ cr++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_0(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_monitor *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 0 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_monitor));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl(current_time - md->lasttime);
+ im->firsttime = htonl(current_time - md->firsttime);
+ if (md->lastdrop)
+ im->lastdrop = htonl(current_time - md->lastdrop);
+ else
+ im->lastdrop = 0;
+ im->count = htonl(md->count);
+ im->addr = md->rmtadr;
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_1(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_monitor_1 *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 1 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_monitor_1));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl(current_time - md->lasttime);
+ im->firsttime = htonl(current_time - md->firsttime);
+ if (md->lastdrop)
+ im->lastdrop = htonl(current_time - md->lastdrop);
+ else
+ im->lastdrop = 0;
+ im->count = htonl(md->count);
+ im->addr = md->rmtadr;
+ im->daddr = md->cast_flags == MDF_BCAST ?
+ md->interface->bcast.sin_addr.s_addr :
+ md->cast_flags ?
+ md->interface->sin.sin_addr.s_addr ?
+ md->interface->sin.sin_addr.s_addr :
+ md->interface->bcast.sin_addr.s_addr :
+ 4;
+ im->flags = md->cast_flags;
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor_1 *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * Module entry points and the flags they correspond with
+ */
+struct reset_entry {
+ int flag; /* flag this corresponds to */
+ void (*handler)(); /* routine to handle request */
+};
+
+struct reset_entry reset_entries[] = {
+ { RESET_FLAG_ALLPEERS, peer_all_reset },
+ { RESET_FLAG_IO, io_clr_stats },
+ { RESET_FLAG_SYS, proto_clr_stats },
+ { RESET_FLAG_MEM, peer_clr_stats },
+ { RESET_FLAG_TIMER, timer_clr_stats },
+ { RESET_FLAG_AUTH, reset_auth_stats },
+ { RESET_FLAG_CTL, ctl_clr_stats },
+ { 0, 0 }
+};
+
+/*
+ * reset_stats - reset statistic counters here and there
+ */
+static void
+reset_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct reset_flags *)inpkt->data)->flags;
+
+ if (flags & ~RESET_ALLFLAGS) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ for (rent = reset_entries; rent->flag != 0; rent++) {
+ if (flags & rent->flag)
+ (rent->handler)();
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * reset_peer - clear a peer's statistics
+ */
+static void
+reset_peer(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0);
+ if (peer == (struct peer *)0)
+ bad++;
+ cp++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0);
+ peer_reset(peer);
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ rereadkeys();
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_dirty_hack
+ */
+static void
+do_dirty_hack(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ /* historical placeholder */
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * dont_dirty_hack
+ */
+static void
+dont_dirty_hack(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ /* historical placeholder */
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * trust_key - make one or more keys trusted
+ */
+static void
+trust_key(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_trustkey(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * untrust_key - make one or more keys untrusted
+ */
+static void
+untrust_key(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_trustkey(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * do_trustkey - make keys either trustable or untrustable
+ */
+static void
+do_trustkey(srcadr, inter, inpkt, trust)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int trust;
+{
+ register u_long *kp;
+ register int items;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ kp = (u_long *)inpkt->data;
+ while (items-- > 0) {
+ authtrust(*kp, trust);
+ kp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * get_auth_info - return some stats concerning the authentication module
+ */
+static void
+get_auth_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_auth *ia;
+
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authnumkeys;
+ extern u_long authnumfreekeys;
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_auth));
+
+ ia->numkeys = htonl(authnumkeys);
+ ia->numfreekeys = htonl(authnumfreekeys);
+ ia->keylookups = htonl(authkeylookups);
+ ia->keynotfound = htonl(authkeynotfound);
+ ia->encryptions = htonl(authencryptions);
+ ia->decryptions = htonl(authdecryptions);
+ ia->keyuncached = htonl(authkeyuncached);
+ ia->timereset = htonl(current_time - auth_timereset);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * reset_auth_stats - reset the authentication stat counters. Done here
+ * to keep xntp-isms out of the authentication module
+ */
+static void
+reset_auth_stats()
+{
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ authkeylookups = 0;
+ authkeynotfound = 0;
+ authencryptions = 0;
+ authdecryptions = 0;
+ authkeyuncached = 0;
+ auth_timereset = current_time;
+}
+
+
+/*
+ * req_get_traps - return information about current trap holders
+ */
+static void
+req_get_traps(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_trap *it;
+ register struct ctl_trap *tr;
+ register int i;
+
+ /*
+ * Imported from the control module
+ */
+ extern struct ctl_trap ctl_trap[];
+ extern int num_ctl_traps;
+
+ if (num_ctl_traps == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = NSRCADR(&tr->tr_localaddr->sin);
+ it->trap_address = NSRCADR(&tr->tr_addr);
+ it->trap_port = NSRCPORT(&tr->tr_addr);
+ it->sequence = htons(tr->tr_sequence);
+ it->settime = htonl(current_time - tr->tr_settime);
+ it->origtime = htonl(current_time - tr->tr_origtime);
+ it->resets = htonl(tr->tr_resets);
+ it->flags = htonl((u_long)tr->tr_flags);
+ it = (struct info_trap *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * req_set_trap - configure a trap
+ */
+static void
+req_set_trap(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_setclr_trap(srcadr, inter, inpkt, 1);
+}
+
+
+
+/*
+ * req_clr_trap - unconfigure a trap
+ */
+static void
+req_clr_trap(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_setclr_trap(srcadr, inter, inpkt, 0);
+}
+
+
+
+/*
+ * do_setclr_trap - do the grunge work of (un)configuring a trap
+ */
+static void
+do_setclr_trap(srcadr, inter, inpkt, set)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int set;
+{
+ register struct conf_trap *ct;
+ register struct interface *linter;
+ int res;
+ struct sockaddr_in laddr;
+
+ /*
+ * Prepare sockaddr_in structure
+ */
+ memset((char *)&laddr, 0, sizeof laddr);
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = ntohs(NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ct = (struct conf_trap *)inpkt->data;
+
+ /*
+ * Look for the local interface. If none, use the default.
+ */
+ if (ct->local_address == 0) {
+ linter = any_interface;
+ } else {
+ laddr.sin_addr.s_addr = ct->local_address;
+ linter = findinterface(&laddr);
+ if (linter == NULL) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ laddr.sin_addr.s_addr = ct->trap_address;
+ if (ct->trap_port != 0)
+ laddr.sin_port = ct->trap_port;
+ else
+ laddr.sin_port = htons(TRAPPORT);
+
+ if (set) {
+ res = ctlsettrap(&laddr, linter, 0,
+ INFO_VERSION(inpkt->rm_vn_mode));
+ } else {
+ res = ctlclrtrap(&laddr, linter, 0);
+ }
+
+ if (!res) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ } else {
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+ }
+ return;
+}
+
+
+
+/*
+ * set_request_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_request_keyid(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_long *)(inpkt->data)));
+ info_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * set_control_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_control_keyid(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long keyid;
+ extern u_long ctl_auth_keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_long *)(inpkt->data)));
+ ctl_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * get_ctl_stats - return some stats concerning the control message module
+ */
+static void
+get_ctl_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_control *ic;
+
+ /*
+ * Importations from the control module
+ */
+ extern u_long ctltimereset;
+ extern u_long numctlreq;
+ extern u_long numctlbadpkts;
+ extern u_long numctlresponses;
+ extern u_long numctlfrags;
+ extern u_long numctlerrors;
+ extern u_long numctltooshort;
+ extern u_long numctlinputresp;
+ extern u_long numctlinputfrag;
+ extern u_long numctlinputerr;
+ extern u_long numctlbadoffset;
+ extern u_long numctlbadversion;
+ extern u_long numctldatatooshort;
+ extern u_long numctlbadop;
+ extern u_long numasyncmsgs;
+
+ ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_control));
+
+ ic->ctltimereset = htonl(current_time - ctltimereset);
+ ic->numctlreq = htonl(numctlreq);
+ ic->numctlbadpkts = htonl(numctlbadpkts);
+ ic->numctlresponses = htonl(numctlresponses);
+ ic->numctlfrags = htonl(numctlfrags);
+ ic->numctlerrors = htonl(numctlerrors);
+ ic->numctltooshort = htonl(numctltooshort);
+ ic->numctlinputresp = htonl(numctlinputresp);
+ ic->numctlinputfrag = htonl(numctlinputfrag);
+ ic->numctlinputerr = htonl(numctlinputerr);
+ ic->numctlbadoffset = htonl(numctlbadoffset);
+ ic->numctlbadversion = htonl(numctlbadversion);
+ ic->numctldatatooshort = htonl(numctldatatooshort);
+ ic->numctlbadop = htonl(numctlbadop);
+ ic->numasyncmsgs = htonl(numasyncmsgs);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * get_leap_info - return some stats concerning the control message module
+ */
+static void
+get_leap_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_leap *il;
+
+ /*
+ * Imported from the protocol module
+ */
+ extern u_char sys_leap;
+
+ /*
+ * Importations from the leap module
+ */
+ extern u_char leap_indicator;
+ extern u_char leap_warning;
+ extern u_char leapbits;
+ extern u_long leap_timer;
+ extern u_long leap_processcalls;
+ extern u_long leap_notclose;
+ extern u_long leap_monthofleap;
+ extern u_long leap_dayofleap;
+ extern u_long leap_hoursfromleap;
+ extern u_long leap_happened;
+
+ il = (struct info_leap *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_leap));
+
+ il->sys_leap = sys_leap;
+ il->leap_indicator = leap_indicator;
+ il->leap_warning = leap_warning;
+ il->leap_bits = (leapbits & INFO_LEAP_MASK)
+ | ((leap_indicator != LEAP_NOWARNING) ? INFO_LEAP_OVERRIDE : 0);
+ il->leap_timer = htonl(leap_timer - current_time);
+ il->leap_processcalls = htonl(leap_processcalls);
+ il->leap_notclose = htonl(leap_notclose);
+ il->leap_monthofleap = htonl(leap_monthofleap);
+ il->leap_dayofleap = htonl(leap_dayofleap);
+ il->leap_hoursfromleap = htonl(leap_hoursfromleap);
+ il->leap_happened = htonl(leap_happened);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+#ifdef KERNEL_PLL
+/*
+ * get_kernel_info - get kernel pll/pps information
+ */
+static void
+get_kernel_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_kernel *ik;
+ struct timex ntx;
+
+ if (!pll_control)
+ return;
+ memset((char *)&ntx, 0, sizeof(ntx));
+ (void)ntp_adjtime(&ntx);
+
+ ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_kernel));
+
+ /*
+ * pll variables
+ */
+ ik->offset = htonl(ntx.offset);
+ ik->freq = htonl(ntx.freq);
+ ik->maxerror = htonl(ntx.maxerror);
+ ik->esterror = htonl(ntx.esterror);
+ ik->status = htons(ntx.status);
+ ik->constant = htonl(ntx.constant);
+ ik->precision = htonl(ntx.precision);
+ ik->tolerance = htonl(ntx.tolerance);
+
+ /*
+ * pps variables
+ */
+ ik->ppsfreq = htonl(ntx.ppsfreq);
+ ik->jitter = htonl(ntx.jitter);
+ ik->shift = htons(ntx.shift);
+ ik->stabil = htonl(ntx.stabil);
+ ik->jitcnt = htonl(ntx.jitcnt);
+ ik->calcnt = htonl(ntx.calcnt);
+ ik->errcnt = htonl(ntx.errcnt);
+ ik->stbcnt = htonl(ntx.stbcnt);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef REFCLOCK
+/*
+ * get_clock_info - get info about a clock
+ */
+static void
+get_clock_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_clock *ic;
+ register U_LONG *clkaddr;
+ register int items;
+ struct refclockstat clock;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (U_LONG *) inpkt->data;
+
+ ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clock));
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&addr, (struct refclockstat *)0, &clock);
+
+ ic->clockadr = addr.sin_addr.s_addr;
+ ic->type = clock.type;
+ ic->flags = clock.flags;
+ ic->lastevent = clock.lastevent;
+ ic->currentstatus = clock.currentstatus;
+ ic->polls = htonl(clock.polls);
+ ic->noresponse = htonl(clock.noresponse);
+ ic->badformat = htonl(clock.badformat);
+ ic->baddata = htonl(clock.baddata);
+ ic->timestarted = htonl(clock.timereset);
+ HTONL_FP(&clock.fudgetime1, &ic->fudgetime1);
+ HTONL_FP(&clock.fudgetime2, &ic->fudgetime2);
+ ic->fudgeval1 = htonl(clock.fudgeval1);
+ ic->fudgeval2 = htonl(clock.fudgeval2);
+
+ free_varlist(clock.kv_list);
+
+ ic = (struct info_clock *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * set_clock_fudge - get a clock's fudge factors
+ */
+static void
+set_clock_fudge(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ memset((char *)&clock, 0, sizeof clock);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *) inpkt->data;
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = cf->clockadr;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ switch(ntohl(cf->which)) {
+ case FUDGE_TIME1:
+ NTOHL_FP(&cf->fudgetime, &clock.fudgetime1);
+ clock.haveflags = CLK_HAVETIME1;
+ break;
+ case FUDGE_TIME2:
+ NTOHL_FP(&cf->fudgetime, &clock.fudgetime2);
+ clock.haveflags = CLK_HAVETIME2;
+ break;
+ case FUDGE_VAL1:
+ clock.fudgeval1 = ntohl(cf->fudgeval_flags);
+ clock.haveflags = CLK_HAVEVAL1;
+ break;
+ case FUDGE_VAL2:
+ clock.fudgeval2 = ntohl(cf->fudgeval_flags);
+ clock.haveflags = CLK_HAVEVAL2;
+ break;
+ case FUDGE_FLAGS:
+ clock.flags = ntohl(cf->fudgeval_flags) & 0xf;
+ clock.haveflags =
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4);
+ break;
+ default:
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ refclock_control(&addr, &clock, (struct refclockstat *)0);
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif
+
+/*
+ * set_precision - set the system precision
+ */
+static void
+set_precision(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register long precision;
+
+ precision = ntohl(*(long *)(inpkt->data));
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1 ||
+ precision > -1 || precision < -20) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ proto_config(PROTO_PRECISION, precision);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+#ifdef REFCLOCK
+/*
+ * get_clkbug_info - get debugging info about a clock
+ */
+static void
+get_clkbug_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register int i;
+ register struct info_clkbug *ic;
+ register U_LONG *clkaddr;
+ register int items;
+ struct refclockbug bug;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (U_LONG *) inpkt->data;
+
+ ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clkbug));
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&bug, 0, sizeof bug);
+ refclock_buginfo(&addr, &bug);
+ if (bug.nvalues == 0 && bug.ntimes == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ic->clockadr = addr.sin_addr.s_addr;
+ i = bug.nvalues;
+ if (i > NUMCBUGVALUES)
+ i = NUMCBUGVALUES;
+ ic->nvalues = (u_char)i;
+ ic->svalues = htons((u_short)bug.svalues & ((1<<i)-1));
+ while (--i >= 0)
+ ic->values[i] = htonl(bug.values[i]);
+
+ i = bug.ntimes;
+ if (i > NUMCBUGTIMES)
+ i = NUMCBUGTIMES;
+ ic->ntimes = (u_char)i;
+ ic->stimes = htonl(bug.stimes);
+ while (--i >= 0) {
+ HTONL_FP(&bug.times[i], &ic->times[i]);
+ }
+
+ ic = (struct info_clkbug *)more_pkt();
+ }
+ flush_pkt();
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/ntp_restrict.c b/usr.sbin/xntpd/xntpd/ntp_restrict.c
new file mode 100644
index 0000000..174d07e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_restrict.c
@@ -0,0 +1,459 @@
+/*
+ * ntp_restrict.c - find out what restrictions this host is running under
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This code keeps a simple address-and-mask list of hosts we want
+ * to place restrictions on (or remove them from). The restrictions
+ * are implemented as a set of flags which tell you what the host
+ * can't do. There is a subroutine entry to return the flags. The
+ * list is kept sorted to reduce the average number of comparisons
+ * and make sure you get the set of restrictions most specific to
+ * the address.
+ *
+ * The algorithm is that, when looking up a host, it is first assumed
+ * that the default set of restrictions will apply. It then searches
+ * down through the list. Whenever it finds a match it adopts the match's
+ * flags instead. When you hit the point where the sorted address is
+ * greater than the target, you return with the last set of flags you
+ * found. Because of the ordering of the list, the most specific match
+ * will provide the final set of flags.
+ *
+ * This was originally intended to restrict you from sync'ing to your
+ * own broadcasts when you are doing that, by restricting yourself
+ * from your own interfaces. It was also thought it would sometimes
+ * be useful to keep a misbehaving host or two from abusing your primary
+ * clock. It has been expanded, however, to suit the needs of those
+ * with more restrictive access policies.
+ */
+
+/*
+ * Memory allocation parameters. We allocate INITRESLIST entries
+ * initially, and add INCRESLIST entries to the free list whenever
+ * we run out.
+ */
+#define INITRESLIST 10
+#define INCRESLIST 5
+
+/*
+ * The restriction list
+ */
+ struct restrictlist *restrictlist;
+static int restrictcount; /* count of entries in the restriction list */
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static struct restrictlist *resfree;
+static int numresfree; /* number of structures on free list */
+
+u_long res_calls;
+u_long res_found;
+u_long res_not_found;
+u_long res_timereset;
+
+/*
+ * Parameters of the RES_LIMITED restriction option.
+ * client_limit is the number of hosts allowed per source net
+ * client_limit_period is the number of seconds after which an entry
+ * is no longer considered for client limit determination
+ */
+u_long client_limit;
+u_long client_limit_period;
+/*
+ * count number of restriction entries referring to RES_LIMITED
+ * controls activation/deactivation of monitoring
+ * (with respect ro RES_LIMITED control)
+ */
+u_long res_limited_refcnt;
+
+/*
+ * Our initial allocation of list entries.
+ */
+static struct restrictlist resinit[INITRESLIST];
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * debug flag
+ */
+extern int debug;
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict()
+{
+ register int i;
+ char bp[80];
+
+ /*
+ * Zero the list and put all but one on the free list
+ */
+ resfree = 0;
+ memset((char *)resinit, 0, sizeof resinit);
+
+ for (i = 1; i < INITRESLIST; i++) {
+ resinit[i].next = resfree;
+ resfree = &resinit[i];
+ }
+
+ numresfree = INITRESLIST-1;
+
+ /*
+ * Put the remaining item at the head of the
+ * list as our default entry. Everything in here
+ * should be zero for now.
+ */
+ resinit[0].addr = htonl(INADDR_ANY);
+ resinit[0].mask = 0;
+ restrictlist = &resinit[0];
+ restrictcount = 1;
+
+
+ /*
+ * fix up stat counters
+ */
+ res_calls = 0;
+ res_found = 0;
+ res_not_found = 0;
+ res_timereset = 0;
+
+ /*
+ * set default values for RES_LIMIT functionality
+ */
+ client_limit = 3;
+ client_limit_period = 3600;
+ res_limited_refcnt = 0;
+
+ sprintf(bp, "client_limit=%ld", client_limit);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ sprintf(bp, "client_limit_period=%ld", client_limit_period);
+ set_sys_var(bp, strlen(bp)+1, RO);
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+int
+restrictions(srcadr)
+ struct sockaddr_in *srcadr;
+{
+ register struct restrictlist *rl;
+ register struct restrictlist *match;
+ register u_long hostaddr;
+ register int isntpport;
+
+ res_calls++;
+ /*
+ * We need the host address in host order. Also need to know
+ * whether this is from the ntp port or not.
+ */
+ hostaddr = SRCADR(srcadr);
+ isntpport = (SRCPORT(srcadr) == NTP_PORT);
+
+ /*
+ * Set match to first entry, which is default entry. Work our
+ * way down from there.
+ */
+ match = restrictlist;
+
+ for (rl = match->next; rl != 0 && rl->addr <= hostaddr; rl = rl->next)
+ if ((hostaddr & rl->mask) == rl->addr) {
+ if ((rl->mflags & RESM_NTPONLY) && !isntpport)
+ continue;
+ match = rl;
+ }
+
+ match->count++;
+ if (match == restrictlist)
+ res_not_found++;
+ else
+ res_found++;
+
+ /*
+ * The following implements limiting the number of clients
+ * accepted from a given network. The notion of "same network"
+ * is determined by the mask and addr fields of the restrict
+ * list entry. The monitor mechanism has to be enabled for
+ * collecting info on current clients.
+ *
+ * The policy is as follows:
+ * - take the list of clients recorded
+ * from the given "network" seen within the last
+ * client_limit_period seconds
+ * - if there are at most client_limit entries:
+ * --> access allowed
+ * - otherwise sort by time first seen
+ * - current client among the first client_limit seen
+ * hosts?
+ * if yes: access allowed
+ * else: eccess denied
+ */
+ if (match->flags & RES_LIMITED) {
+ int lcnt;
+ struct mon_data *md, *this_client;
+ extern int mon_enabled;
+ extern struct mon_data mon_fifo_list, mon_mru_list;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n",
+ client_limit, client_limit_period,
+ netof(hostaddr));
+#endif /*DEBUG*/
+ if (mon_enabled == MON_OFF) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("no limit - monitoring is off\n");
+#endif
+ return (int)(match->flags & ~RES_LIMITED);
+ }
+
+ /*
+ * How nice, MRU list provides our current client as the
+ * first entry in the list.
+ * Monitoring was verified to be active above, thus we
+ * know an entry for our client must exist, or some
+ * brain dead set the memory limit for mon entries to ZERO!!!
+ */
+ this_client = mon_mru_list.mru_next;
+
+ for (md = mon_fifo_list.fifo_next,lcnt = 0;
+ md != &mon_fifo_list;
+ md = md->fifo_next) {
+ if ((current_time - md->lasttime)
+ > client_limit_period) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore: too old: %ld\n",
+ numtoa(md->rmtadr),
+ current_time - md->lasttime);
+#endif
+ continue;
+ }
+ if (md->mode == MODE_BROADCAST ||
+ md->mode == MODE_CONTROL ||
+ md->mode == MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore mode %d\n",
+ numtoa(md->rmtadr),
+ md->mode);
+#endif
+ continue;
+ }
+ if (netof(md->rmtadr) !=
+ netof(hostaddr)) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: different net 0x%lX\n",
+ numtoa(md->rmtadr),
+ netof(md->rmtadr));
+#endif
+ continue;
+ }
+ lcnt++;
+ if (lcnt > client_limit ||
+ md->rmtadr == hostaddr) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("considering %s: found host\n",
+ numtoa(md->rmtadr));
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else {
+ if (debug > 5)
+ printf("considering %s: same net\n",
+ numtoa(md->rmtadr));
+ }
+#endif
+
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ printf("this one is rank %d in list, limit is %lu: %s\n",
+ lcnt, client_limit,
+ (lcnt <= client_limit) ? "ALLOW" : "REJECT");
+#endif
+ if (lcnt <= client_limit) {
+ this_client->lastdrop = 0;
+ return (int)(match->flags & ~RES_LIMITED);
+ } else {
+ this_client->lastdrop = current_time;
+ }
+ }
+ return (int)match->flags;
+}
+
+
+/*
+ * restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+restrict(op, resaddr, resmask, mflags, flags)
+ int op;
+ struct sockaddr_in *resaddr;
+ struct sockaddr_in *resmask;
+ int mflags;
+ int flags;
+{
+ register u_long addr;
+ register u_long mask;
+ register struct restrictlist *rl;
+ register struct restrictlist *rlprev;
+ int i;
+
+ /*
+ * Get address and mask in host byte order
+ */
+ addr = SRCADR(resaddr);
+ mask = SRCADR(resmask);
+ addr &= mask; /* make sure low bits are zero */
+
+ /*
+ * If this is the default address, point at first on list. Else
+ * go searching for it.
+ */
+ if (addr == htonl(INADDR_ANY)) {
+ rlprev = 0;
+ rl = restrictlist;
+ } else {
+ rlprev = restrictlist;
+ rl = rlprev->next;
+ while (rl != 0) {
+ if (rl->addr > addr) {
+ rl = 0;
+ break;
+ } else if (rl->addr == addr) {
+ if (rl->mask == mask) {
+ if ((mflags & RESM_NTPONLY)
+ == (rl->mflags & RESM_NTPONLY))
+ break; /* exact match */
+ if (!(mflags & RESM_NTPONLY)) {
+ /*
+ * No flag fits before flag
+ */
+ rl = 0;
+ break;
+ }
+ /* continue on */
+ } else if (rl->mask > mask) {
+ rl = 0;
+ break;
+ }
+ }
+ rlprev = rl;
+ rl = rl->next;
+ }
+ }
+ /*
+ * In case the above wasn't clear :-), either rl now points
+ * at the entry this call refers to, or rl is zero and rlprev
+ * points to the entry prior to where this one should go in
+ * the sort.
+ */
+
+ /*
+ * Switch based on operation
+ */
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a new
+ * restriction add it.
+ */
+ if (rl == 0) {
+ if (numresfree == 0) {
+ rl = (struct restrictlist *) emalloc(
+ INCRESLIST*sizeof(struct restrictlist));
+ memset((char *)rl, 0,
+ INCRESLIST*sizeof(struct restrictlist));
+
+ for (i = 0; i < INCRESLIST; i++) {
+ rl->next = resfree;
+ resfree = rl;
+ rl++;
+ }
+ numresfree = INCRESLIST;
+ }
+
+ rl = resfree;
+ resfree = rl->next;
+ numresfree--;
+
+ rl->addr = addr;
+ rl->mask = mask;
+ rl->mflags = (u_short)mflags;
+
+ rl->next = rlprev->next;
+ rlprev->next = rl;
+ restrictcount++;
+ }
+ if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
+ res_limited_refcnt++;
+ mon_start(MON_RES); /* ensure data gets collected */
+ }
+ rl->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl != 0) {
+ if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ rl->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ /*
+ * Remove an entry from the table entirely if we found one.
+ * Don't remove the default entry and don't remove an
+ * interface entry.
+ */
+ if (rl != 0
+ && rl->addr != htonl(INADDR_ANY)
+ && !(rl->mflags & RESM_INTERFACE)) {
+ rlprev->next = rl->next;
+ restrictcount--;
+ if (rl->flags & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl, 0, sizeof(struct restrictlist));
+
+ rl->next = resfree;
+ resfree = rl;
+ numresfree++;
+ }
+ break;
+
+ default:
+ /* Oh, well */
+ break;
+ }
+
+ /* done! */
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_timer.c b/usr.sbin/xntpd/xntpd/ntp_timer.c
new file mode 100644
index 0000000..99551f7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_timer.c
@@ -0,0 +1,187 @@
+/*
+ * ntp_event.c - event timer support routines
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/signal.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines provide support for the event timer. The timer is
+ * implemented by an interrupt routine which sets a flag once every
+ * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
+ * is called when the mainline code gets around to seeing the flag.
+ * The timer routine dispatches the clock adjustment code if its time
+ * has come, then searches the timer queue for expiries which are
+ * dispatched to the transmit procedure. Finally, we call the hourly
+ * procedure to do cleanup and print a message.
+ */
+
+/*
+ * Alarm flag. The mainline code imports this.
+ */
+int alarm_flag;
+
+/*
+ * adjust and hourly counters
+ */
+static u_long adjust_timer;
+static u_long hourly_timer;
+
+/*
+ * Imported from the leap module. The leap timer.
+ */
+extern u_long leap_timer;
+
+/*
+ * Statistics counter for the interested.
+ */
+u_long alarm_overflow;
+
+#define HOUR (60*60)
+
+/*
+ * Current_time holds the number of seconds since we started, in
+ * increments of 2**EVENT_TIMEOUT seconds. The timer queue is the
+ * hash into which we sort timer entries.
+ */
+u_long current_time;
+struct event timerqueue[TIMER_NSLOTS];
+
+/*
+ * Stats. Number of overflows and number of calls to transmit().
+ */
+u_long timer_timereset;
+u_long timer_overflows;
+u_long timer_xmtcalls;
+
+static RETSIGTYPE alarming P((int));
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer()
+{
+ register int i;
+ struct itimerval itimer;
+
+ /*
+ * Initialize...
+ */
+ alarm_flag = 0;
+ alarm_overflow = 0;
+ adjust_timer = (1<<CLOCK_ADJ);
+ hourly_timer = HOUR;
+ current_time = 0;
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = 0;
+
+ for (i = 0; i < TIMER_NSLOTS; i++) {
+ /*
+ * Queue pointers should point at themselves. Event
+ * times must be set to 0 since this is used to
+ * detect the queue end.
+ */
+ timerqueue[i].next = &timerqueue[i];
+ timerqueue[i].prev = &timerqueue[i];
+ timerqueue[i].event_time = 0;
+ }
+
+ /*
+ * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT
+ * seconds from now and they continue on every 2**EVENT_TIMEOUT
+ * seconds.
+ */
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+
+
+/*
+ * timer - dispatch anyone who needs to be
+ */
+void
+timer()
+{
+ register struct event *ev;
+ register struct event *tq;
+
+ current_time += (1<<EVENT_TIMEOUT);
+
+ /*
+ * Adjustment timeout first
+ */
+ if (adjust_timer <= current_time) {
+ adjust_timer += (1<<CLOCK_ADJ);
+ adj_host_clock();
+ }
+
+ /*
+ * Leap timer next.
+ */
+ if (leap_timer != 0 && leap_timer <= current_time)
+ leap_process();
+
+ /*
+ * Now dispatch any peers whose event timer has expired.
+ */
+ tq = &timerqueue[TIMER_SLOT(current_time)];
+ ev = tq->next;
+ while (ev->event_time != 0
+ && ev->event_time < (current_time + (1<<EVENT_TIMEOUT))) {
+ tq->next = ev->next;
+ tq->next->prev = tq;
+ ev->prev = ev->next = 0;
+ timer_xmtcalls++;
+ ev->event_handler(ev->peer);
+ ev = tq->next;
+ }
+
+ /*
+ * Finally, call the hourly routine
+ */
+ if (hourly_timer <= current_time) {
+ hourly_timer += HOUR;
+ hourly_stats();
+ }
+}
+
+
+/*
+ * alarming - tell the world we've been alarmed
+ */
+static RETSIGTYPE
+alarming(sig)
+int sig;
+{
+ extern int initializing; /* from main line code */
+
+ if (initializing)
+ return;
+ if (alarm_flag)
+ alarm_overflow++;
+ else
+ alarm_flag++;
+}
+
+
+/*
+ * timer_clr_stats - clear timer module stat counters
+ */
+void
+timer_clr_stats()
+{
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = current_time;
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_unixclock.c b/usr.sbin/xntpd/xntpd/ntp_unixclock.c
new file mode 100644
index 0000000..557ce25
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_unixclock.c
@@ -0,0 +1,634 @@
+/*
+ * ntp_unixclock.c - routines for reading and adjusting a 4BSD-style
+ * system clock
+ */
+
+#include <nlist.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/param.h>
+#include <utmp.h>
+#endif
+
+#if defined(HAVE_GETBOOTFILE)
+#include <paths.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined(HAVE_LIBKVM)
+#if defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/proc.h>
+#endif /* SYS_BSDI */
+#include <kvm.h>
+#include <limits.h>
+
+#ifndef _POSIX2_LINE_MAX
+#define _POSIX2_LINE_MAX 2048
+#endif
+#endif /* HAVE_LIBKVM */
+
+
+#ifdef RS6000
+#undef hz
+#endif /* RS6000 */
+
+#ifdef HAVE_SYSCTL
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+#endif
+
+extern int debug;
+/*
+ * These routines (init_systime, get_systime, step_systime, adj_systime)
+ * implement an interface between the (more or less) system independent
+ * bits of NTP and the peculiarities of dealing with the Unix system
+ * clock. These routines will run with good precision fairly independently
+ * of your kernel's value of tickadj. I couldn't tell the difference
+ * between tickadj==40 and tickadj==5 on a microvax, though I prefer
+ * to set tickadj == 500/hz when in doubt. At your option you
+ * may compile this so that your system's clock is always slewed to the
+ * correct time even for large corrections. Of course, all of this takes
+ * a lot of code which wouldn't be needed with a reasonable tickadj and
+ * a willingness to let the clock be stepped occasionally. Oh well.
+ */
+
+/*
+ * Clock variables. We round calls to adjtime() to adj_precision
+ * microseconds, and limit the adjustment to tvu_maxslew microseconds
+ * (tsf_maxslew fractional sec) in one adjustment interval. As we are
+ * thus limited in the speed and precision with which we can adjust the
+ * clock, we compensate by keeping the known "error" in the system time
+ * in sys_clock_offset. This is added to timestamps returned by get_systime().
+ * We also remember the clock precision we computed from the kernel in
+ * case someone asks us.
+ */
+extern long adj_precision; /* adj precision in usec (tickadj) */
+extern long tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */
+
+extern u_long tsf_maxslew; /* same as above, as long format */
+
+extern l_fp sys_clock_offset; /* correction for current system time */
+
+/*
+ * Import sys_clock (it is updated in get_systime)
+ */
+extern long sys_clock;
+
+static void clock_parms P((u_long *, u_long *));
+
+/*
+ * init_systime - initialize the system clock support code, return
+ * clock precision.
+ *
+ * Note that this code obtains to kernel variables related to the local
+ * clock, tickadj and tick. The code knows how the Berkeley adjtime
+ * call works, and assumes these two variables are obtainable and are
+ * used in the same manner. Tick is supposed to be the number of
+ * microseconds which are added to the system clock at clock interrupt
+ * time when the time isn't being slewed. Tickadj is supposed to be
+ * the number of microseconds which are added or subtracted from tick when
+ * the time is being slewed.
+ *
+ * If either of these two variables is missing, or is there but is used
+ * for a purpose different than that described, you are SOL and may have
+ * to do some custom kludging.
+ *
+ * This really shouldn't be in here.
+ */
+void
+init_systime()
+{
+ u_long tickadj;
+ u_long tick;
+ u_long hz;
+
+ /*
+ * Obtain the values
+ */
+ clock_parms(&tickadj, &tick);
+#ifdef DEBUG
+ if (debug)
+ printf("kernel vars: tickadj = %ld, tick = %ld\n", tickadj, tick);
+#endif
+
+ /*
+ * If tickadj or hz wasn't found, we're doomed. If hz is
+ * unreasonably small, forget it.
+ */
+ if (tickadj == 0 || tick == 0) {
+ syslog(LOG_ERR, "tickadj or tick unknown, exiting");
+ exit(3);
+ }
+ if (tick > 65535) {
+ syslog(LOG_ERR, "tick value of %lu is unreasonably large",
+ tick);
+ exit(3);
+ }
+
+ /*
+ * Estimate hz from tick
+ */
+ hz = 1000000L / tick;
+
+ /*
+ * Set adj_precision and the maximum slew based on this. Note
+ * that maxslew is set slightly shorter than it needs to be as
+ * insurance that all slews requested will complete in 1<<CLOCK_ADJ
+ * seconds.
+ */
+#ifdef ADJTIME_IS_ACCURATE
+ adj_precision = 1;
+#else
+ adj_precision = tickadj;
+#endif /* ADJTIME_IS_ACCURATE */
+#if defined(SLEWALWAYS) && !defined(ADJTIME_IS_ACCURATE)
+ /*
+ * give us more time if we are always slewing... just in case
+ */
+ tvu_maxslew = tickadj * (hz-3) * (1<<CLOCK_ADJ);
+#else
+ tvu_maxslew = tickadj * (hz-1) * (1<<CLOCK_ADJ);
+#endif /* SLEWALWAYS */
+ if (tvu_maxslew > 999990) {
+ /*
+ * Don't let the maximum slew exceed 1 second in 4. This
+ * simplifies calculations a lot since we can then deal
+ * with less-than-one-second fractions.
+ */
+ tvu_maxslew = (999990/adj_precision) * adj_precision;
+ }
+ TVUTOTSF(tvu_maxslew, tsf_maxslew);
+ syslog(LOG_NOTICE, "tickadj = %d, tick = %d, tvu_maxslew = %d",
+ tickadj, tick, tvu_maxslew);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "adj_precision = %ld, tvu_maxslew = %ld, tsf_maxslew = 0.%08lx\n",
+ adj_precision, tvu_maxslew, tsf_maxslew);
+#endif
+
+ /*
+ * Set the current offset to 0
+ */
+ L_CLR(&sys_clock_offset);
+}
+
+#if defined(HAVE_SYSCTL) && defined(KERN_CLOCKRATE)
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ int mib[2];
+ size_t len;
+ struct clockinfo x;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ len = sizeof(x);
+ if (sysctl(mib, 2, &x, &len, NULL, 0) == -1) {
+ syslog(LOG_NOTICE, "sysctl(KERN_CLOCKRATE) failed: %m");
+ exit(3);
+ }
+ *tickadj = x.tickadj;
+ *tick = x.tick;
+}
+#else
+#ifdef HAVE_LIBKVM
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * This version uses the SunOS libkvm (or the bsd compatability version).
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ static struct nlist nl[] = {
+#define N_TICKADJ 0
+ { "_tickadj" },
+#define N_TICK 1
+ { "_tick" },
+ { "" },
+ };
+#if __convex__ /* { */
+ if (K_open((char *)0,O_RDONLY,"/vmunix")!=0) {
+ syslog(LOG_ERR, "K_open failed");
+ exit(3);
+ }
+ kusenlist(1);
+ if (knlist(nl)!=0
+ || nl[N_TICKADJ].n_value==0
+ || nl[N_TICK].n_value==0) {
+ syslog(LOG_ERR, "knlist failed");
+ exit(3);
+ }
+ if (K_read(tickadj,sizeof(*tickadj),nl[N_TICKADJ].n_value) !=
+ sizeof(*tickadj)) {
+ syslog(LOG_ERR, "K_read tickadj failed");
+ exit(3);
+ }
+ if (K_read(tick,sizeof(*tick),nl[N_TICK].n_value) !=
+ sizeof(*tick)) {
+ syslog(LOG_ERR, "K_read tick failed");
+ exit(3);
+ }
+ (void)K_close();
+#else /* }__convex__{ */
+ register kvm_t *kd;
+ if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) {
+ syslog(LOG_ERR, "kvm_open failed");
+ exit(3);
+ }
+ if (kvm_nlist(kd, nl) != 0) {
+ syslog(LOG_ERR, "kvm_nlist failed");
+ exit(3);
+ }
+ if (kvm_read(kd, nl[N_TICKADJ].n_value, (char *)tickadj, sizeof(*tickadj)) !=
+ sizeof(*tickadj)) {
+ syslog(LOG_ERR, "kvm_read tickadj failed");
+ exit(3);
+ }
+ if (kvm_read(kd, nl[N_TICK].n_value, (char *)tick, sizeof(*tick)) !=
+ sizeof(*tick)) {
+ syslog(LOG_ERR, "kvm_read tick failed");
+ exit(3);
+ }
+ if (kvm_close(kd) < 0) {
+ syslog(LOG_ERR, "kvm_close failed");
+ exit(3);
+ }
+#endif /*}convex*/
+#undef N_TICKADJ
+#undef N_TICK
+}
+#endif /* HAVE_LIBKVM */
+
+
+#ifdef HAVE_READKMEM
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version grovels about in /dev/kmem to determine
+ * these values. This probably should be elsewhere.
+ */
+#if defined(SYS_UNIXWARE1)
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * The values set here were determined experimentally on a 486 system
+ * I'm not confident in them. - RAS
+ *
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ *tick = 10000; /* microseconds */
+ *tickadj = 80; /* microseconds */
+}
+#else /* SYS_UNIXWARE1 */
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2) || defined(SYS_SVR4) || defined(SYS_PTX)
+#define K_TICKADJ_NAME "tickadj"
+#define K_TICK_NAME "tick"
+#endif
+
+#ifdef SYS_HPUX
+#if defined(hp9000s300)
+#define K_TICKADJ_NAME "_tickadj"
+#define K_TICK_NAME "_old_tick"
+#else
+#define K_TICKADJ_NAME "tickadj"
+#define K_TICK_NAME "old_tick"
+#endif
+#endif
+
+/* The defaults if not defined previously */
+#if !defined(K_TICKADJ_NAME)
+#define K_TICKADJ_NAME "_tickadj"
+#endif
+#if !defined(K_TICK_NAME)
+#define K_TICK_NAME "_tick"
+#endif
+
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ register int i;
+ int kmem;
+#if defined(HAVE_N_UN)
+#define N_NAME n_un.n_name
+ static struct nlist nl[] =
+ { {{K_TICKADJ_NAME}},
+ {{K_TICK_NAME}},
+ {{""}},
+ };
+#else
+#define N_NAME n_name
+ static struct nlist nl[] =
+ { {K_TICKADJ_NAME},
+ {K_TICK_NAME},
+ {""},
+ };
+#endif
+#ifdef HAVE_GETBOOTFILE
+ const char *kernelname;
+#else
+ static char *kernelnames[] = {
+ "/kernel",
+ "/vmunix",
+ "/unix",
+ "/mach",
+ "/hp-ux",
+ "/386bsd",
+ "/netbsd",
+#ifdef KERNELFILE
+ KERNELFILE,
+#endif
+ NULL
+ };
+#endif
+ struct stat stbuf;
+ int vars[2];
+
+#define K_TICKADJ 0
+#define K_TICK 1
+
+ /*
+ * Check to see what to use for the object file for names and get
+ * the locations of the necessary kernel variables.
+ */
+#ifdef HAVE_GETBOOTFILE
+ kernelname = getbootfile();
+ if (kernelname &&
+ ((stat(kernelname, &stbuf) == -1) || (nlist(kernelname, nl) < 0))) {
+#else
+ for (i = 0; kernelnames[i] != NULL; i++) {
+ if (stat(kernelnames[i], &stbuf) == -1)
+ continue;
+ if (nlist(kernelnames[i], nl) >= 0)
+ break;
+ }
+ if (kernelnames[i] == NULL) {
+#endif
+ syslog(LOG_ERR,
+ "Clock init couldn't find kernel object file");
+ exit(3);
+ }
+
+ /*
+ * Read clock parameters from kernel
+ */
+ kmem = open("/dev/kmem", O_RDONLY);
+ if (kmem < 0) {
+ syslog(LOG_ERR, "Can't open /dev/kmem for reading: %m");
+#ifdef DEBUG
+ if (debug)
+ perror("/dev/kmem");
+#endif
+ exit(3);
+ }
+
+ for (i = 0; i < (sizeof(vars)/sizeof(vars[0])); i++) {
+ off_t where;
+
+ vars[i] = 0;
+ if ((where = nl[i].n_value) == 0) {
+ syslog(LOG_ERR, "Unknown kernal var %s",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (lseek(kmem, where, SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+ }
+ close(kmem);
+
+ *tickadj = (u_long)vars[K_TICKADJ];
+ *tick = (u_long)vars[K_TICK];
+
+#undef K_TICKADJ
+#undef K_TICK
+#undef K_TICKADJ_NAME
+#undef K_TICK_NAME
+#undef N_NAME
+}
+#endif /* SYS_UNIXWARE1 */
+#endif /* HAVE_READKMEM */
+
+#if defined(SOLARIS)&&defined(ADJTIME_IS_ACCURATE)
+/*
+ * clock_parms for Solaris 2.2 and later, with high-res timer kernel code.
+ * The clock code changed in Solaris 2.2, and tickadj went away.
+ * The good news is that ADJTIME_IS_ACCURATE and tick is available through
+ * sysconf().
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ int hz;
+
+ hz = (int) sysconf (_SC_CLK_TCK);
+ *tick = 1000000L/hz;
+ *tickadj = (*tick/16); /* There is no tickadj, and it is only set here
+ for tvu_maxslew calculation above. Really,
+ clock_parms should return adj_precision
+ and tvu_maxslew, instead of the very
+ BSD-centric tickadj */
+
+#ifdef DEBUG
+ if (debug) printf ("Solaris tick = %d\n", *tick);
+#endif
+}
+#endif /* SOLARIS_HRTIME */
+
+
+#if defined(sgi)
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * The values set here were determined experimentally on a 4D/220 and
+ * an R4000-50 server under IRIX 4.0.5.
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ *tick = 10000;
+ *tickadj = 150;
+}
+#endif /* sgi */
+
+#ifdef NOKMEM
+
+#ifndef HZ
+#define HZ 60
+#endif
+
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version uses static values!
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+#ifdef RS6000
+ *tickadj = 1000;
+#else /*RS6000*/
+#if SYS_DOMAINOS
+ *tickadj = 668;
+#else /*SYS_DOMAINOS*/
+ *tickadj = 500 / HZ;
+#endif /*SYS_DOMAINOS*/
+#endif /*RS6000*/
+ *tick = 1000000L / HZ;
+
+#ifdef DEBUG
+ if (debug)
+ printf("NOTE: Using preset values for tick and tickadj !!\n");
+#endif
+}
+#endif /*NOKMEM*/
+
+#if ((defined(SOLARIS)&&!defined(ADJTIME_IS_ACCURATE))|| (defined(RS6000)&&!defined(NOKMEM))||defined(SYS_SINIXM) )
+#ifndef _SC_CLK_TCK
+#include <unistd.h>
+#endif
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version grovels about in /dev/kmem to determine
+ * these values. This probably should be elsewhere.
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ register int i;
+ int kmem;
+#define N_NAME n_name
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {""},
+ };
+ static char *kernelnames[] = {
+ "/kernel/unix",
+ "/unix",
+ NULL
+ };
+ struct stat stbuf;
+ int vars[1];
+
+#define K_TICKADJ 0
+ /*
+ * Read clock parameters from kernel
+ */
+ kmem = open("/dev/kmem", O_RDONLY);
+ if (kmem < 0) {
+ syslog(LOG_ERR, "Can't open /dev/kmem for reading: %m");
+#ifdef DEBUG
+ if (debug)
+ perror("/dev/kmem");
+#endif
+ exit(3);
+ }
+
+ for (i = 0; kernelnames[i] != NULL; i++) {
+ if (stat(kernelnames[i], &stbuf) == -1)
+ continue;
+ if (nlist(kernelnames[i], nl) >= 0)
+ break;
+ }
+ if (kernelnames[i] == NULL) {
+ syslog(LOG_ERR,
+ "Clock init couldn't find kernel as either /vmunix or /unix");
+ exit(3);
+ }
+
+ for (i = 0; i < (sizeof(vars)/sizeof(vars[0])); i++) {
+ off_t where;
+
+ vars[i] = 0;
+ if ((where = nl[i].n_value) == 0) {
+ syslog(LOG_ERR, "Unknown kernal var %s",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (lseek(kmem, where, SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+#if defined(RS6000)
+ /*
+ * Aix requires one more round of indirection.
+ */
+ if (lseek(kmem, vars[i], SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+#endif
+ }
+ close(kmem);
+
+ *tickadj = (u_long)vars[K_TICKADJ];
+ *tick = (u_long)(1000000/sysconf(_SC_CLK_TCK));
+
+#undef K_TICKADJ
+#undef N_NAME
+}
+#endif /* SOLARIS */
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ struct timex txc;
+
+ txc.mode = 0;
+ __adjtimex(&txc);
+
+ *tickadj = (u_long)1; /* our adjtime is accurate */
+ *tick = (u_long)txc.tick;
+}
+#endif /* SYS_LINUX */
+#endif
diff --git a/usr.sbin/xntpd/xntpd/ntp_util.c b/usr.sbin/xntpd/xntpd/ntp_util.c
new file mode 100644
index 0000000..e449276
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_util.c
@@ -0,0 +1,441 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#ifdef DOSYNCTODR
+#include <sys/resource.h>
+#endif
+
+/*
+ * This contains odds and ends. Right now the only thing you'll find
+ * in here is the hourly stats printer and some code to support rereading
+ * the keys file, but I may eventually put other things in here such as
+ * code to do something with the leap bits.
+ */
+
+/*
+ * Name of the keys file
+ */
+static char *key_file_name;
+
+/*
+ * The name of the drift_comp file and the temporary.
+ */
+static char *stats_drift_file;
+static char *stats_temp_file;
+
+/*
+ * Statistics file stuff
+ */
+#ifndef NTP_VAR
+#define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+static char statsdir[MAXPATHLEN] = NTP_VAR;
+
+static FILEGEN peerstats;
+static FILEGEN loopstats;
+static FILEGEN clockstats;
+/*
+ * We query the errno to see what kind of error occured
+ * when opening the drift file.
+ */
+extern int errno;
+
+/*
+ * This controls whether stats are written to the fileset. Provided
+ * so that xntpdc can turn off stats when the file system fills up.
+ */
+int stats_control;
+
+#ifdef DEBUG
+extern int debug;
+#endif
+
+/*
+ * init_util - initialize the utilities
+ */
+void
+init_util()
+{
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ key_file_name = 0;
+
+#define PEERNAME "peerstats"
+#define LOOPNAME "loopstats"
+#define CLOCKNAME "clockstats"
+ peerstats.fp = NULL;
+ peerstats.prefix = &statsdir[0];
+ peerstats.basename = emalloc(strlen(PEERNAME)+1);
+ strcpy(peerstats.basename, PEERNAME);
+ peerstats.id = 0;
+ peerstats.type = FILEGEN_DAY;
+ peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("peerstats", &peerstats);
+
+ loopstats.fp = NULL;
+ loopstats.prefix = &statsdir[0];
+ loopstats.basename = emalloc(strlen(LOOPNAME)+1);
+ strcpy(loopstats.basename, LOOPNAME);
+ loopstats.id = 0;
+ loopstats.type = FILEGEN_DAY;
+ loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("loopstats", &loopstats);
+
+ clockstats.fp = NULL;
+ clockstats.prefix = &statsdir[0];
+ clockstats.basename = emalloc(strlen(CLOCKNAME)+1);
+ strcpy(clockstats.basename, CLOCKNAME);
+ clockstats.id = 0;
+ clockstats.type = FILEGEN_DAY;
+ clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("clockstats", &clockstats);
+
+#undef PEERNAME
+#undef LOOPNAME
+#undef CLOCKNAME
+
+}
+
+
+/*
+ * hourly_stats - print some interesting stats
+ */
+void
+hourly_stats()
+{
+ FILE *fp;
+ extern l_fp last_offset;
+ extern s_fp drift_comp;
+ extern u_char sys_poll;
+ extern int pll_status;
+
+#ifdef DOSYNCTODR
+ struct timeval tv;
+ int o_prio;
+
+ /*
+ * Sometimes having a Sun can be a drag.
+ *
+ * The kernel variable dosynctodr controls whether the system's
+ * soft clock is kept in sync with the battery clock. If it
+ * is zero, then the soft clock is not synced, and the battery
+ * clock is simply left to rot. That means that when the system
+ * reboots, the battery clock (which has probably gone wacky)
+ * sets the soft clock. That means xntpd starts off with a very
+ * confused idea of what time it is. It then takes a large
+ * amount of time to figure out just how wacky the battery clock
+ * has made things drift, etc, etc. The solution is to make the
+ * battery clock sync up to system time. The way to do THAT is
+ * to simply set the time of day to the current time of day, but
+ * as quickly as possible. This may, or may not be a sensible
+ * thing to do.
+ *
+ * CAVEAT: settimeofday() steps the sun clock by about 800 us,
+ * so setting DOSYNCTODR seems a bad idea in the
+ * case of us resolution
+ */
+
+ o_prio=getpriority(PRIO_PROCESS,0); /* Save setting */
+ if (setpriority(PRIO_PROCESS,0,-20) != 0) /* overdrive */
+ {
+ syslog(LOG_ERR, "can't elevate priority: %m");
+ goto skip;
+ }
+ GETTIMEOFDAY(&tv,(struct timezone *)NULL);
+ if (SETTIMEOFDAY(&tv,(struct timezone *)NULL) != 0)
+ {
+ syslog(LOG_ERR, "can't sync battery time: %m");
+ }
+ setpriority(PRIO_PROCESS,0,o_prio); /* downshift */
+
+ skip:
+#endif
+
+ syslog(LOG_INFO, "offset %s freq %s poll %d",
+ lfptoa(&last_offset, 6), fptoa(drift_comp, 3),
+ sys_poll);
+
+ if (stats_drift_file != 0) {
+ if ((fp = fopen(stats_temp_file, "w")) == NULL) {
+ syslog(LOG_ERR, "can't open %s: %m",
+ stats_temp_file);
+ return;
+ }
+ fprintf(fp, "%s %x\n", fptoa(drift_comp, 3),
+ pll_status);
+ (void)fclose(fp);
+ /* atomic */
+ (void) rename(stats_temp_file, stats_drift_file);
+ }
+}
+
+
+/*
+ * stats_config - configure the stats operation
+ */
+void
+stats_config(item, value)
+ int item;
+ char *value; /* only one type so far */
+{
+ FILE *fp;
+ char buf[128];
+ l_fp old_drift;
+ int temp = 0;
+ int len;
+
+ switch(item) {
+ case STATS_FREQ_FILE:
+ if (stats_drift_file != 0) {
+ (void) free(stats_drift_file);
+ (void) free(stats_temp_file);
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ }
+
+ if (value == 0 || (len = strlen(value)) == 0)
+ break;
+
+ stats_drift_file = emalloc((u_int)(len + 1));
+ stats_temp_file = emalloc((u_int)(len +
+ sizeof(".TEMP")));
+ memmove(stats_drift_file, value, len+1);
+ memmove(stats_temp_file, value, len);
+ memmove(stats_temp_file + len, ".TEMP",
+ sizeof(".TEMP"));
+ L_CLR(&old_drift);
+
+ /*
+ * Open drift file and read frequency and mode.
+ */
+ if ((fp = fopen(stats_drift_file, "r")) == NULL) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "can't open %s: %m",
+ stats_drift_file);
+ loop_config(LOOP_DRIFTCOMP, &old_drift, 0);
+ break;
+ }
+
+ if (fscanf(fp, "%s %x", buf, &temp) == 0) {
+ syslog(LOG_ERR, "can't read %s: %m",
+ stats_drift_file);
+ (void) fclose(fp);
+ loop_config(LOOP_DRIFTCOMP, &old_drift, 0);
+ break;
+ }
+ (void) fclose(fp);
+ if (!atolfp(buf, &old_drift)) {
+ syslog(LOG_ERR, "drift value %s invalid", buf);
+ break;
+ }
+ loop_config(LOOP_DRIFTCOMP, &old_drift, temp);
+ break;
+
+ case STATS_STATSDIR:
+ if (strlen(value) >= sizeof(statsdir)) {
+ syslog(LOG_ERR,
+ "value for statsdir too long (>%d, sigh)",
+ sizeof(statsdir)-1);
+ } else {
+ l_fp now;
+
+ gettstamp(&now);
+ strcpy(statsdir,value);
+ if(peerstats.prefix == &statsdir[0] &&
+ peerstats.fp != NULL) {
+ fclose(peerstats.fp);
+ peerstats.fp = NULL;
+ filegen_setup(&peerstats, now.l_ui);
+ }
+ if(loopstats.prefix == &statsdir[0] &&
+ loopstats.fp != NULL) {
+ fclose(loopstats.fp);
+ loopstats.fp = NULL;
+ filegen_setup(&loopstats, now.l_ui);
+ }
+ if(clockstats.prefix == &statsdir[0] &&
+ clockstats.fp != NULL) {
+ fclose(clockstats.fp);
+ clockstats.fp = NULL;
+ filegen_setup(&clockstats, now.l_ui);
+ }
+ }
+ break;
+
+ case STATS_PID_FILE:
+ if ((fp = fopen(value, "w")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s: %m", value);
+ break;
+ }
+ fprintf(fp, "%d", getpid());
+ fclose(fp);;
+ break;
+
+ default:
+ /* oh well */
+ break;
+ }
+}
+
+/*
+ * record_peer_stats - write peer statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * peer status word (hex)
+ * peer offset (s)
+ * peer delay (s)
+ * peer dispersion (s)
+ */
+void
+record_peer_stats(addr, status, offset, delay, dispersion)
+ struct sockaddr_in *addr;
+ int status;
+ l_fp *offset;
+ s_fp delay;
+ u_fp dispersion;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp, "%lu %lu.%03lu %s %x %s %s %s\n",
+ day, sec, msec, ntoa(addr), status, lfptoa(offset, 6),
+ fptoa(delay, 5), ufptoa(dispersion, 5));
+ fflush(peerstats.fp);
+ }
+}
+/*
+ * record_loop_stats - write loop filter statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * offset (s)
+ * frequency (approx ppm)
+ * time constant (log base 2)
+ */
+void
+record_loop_stats(offset, freq, poll)
+ l_fp *offset;
+ s_fp freq;
+ u_char poll;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %lu.%03lu %s %s %d\n",
+ day, sec, msec, lfptoa(offset, 6),
+ fptoa(freq, 4), poll);
+ fflush(loopstats.fp);
+ }
+}
+
+/*
+ * record_clock_stats - write clock statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_clock_stats(addr, text)
+ struct sockaddr_in *addr;
+ char *text;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n",
+ day, sec, msec, ntoa(addr), text);
+ fflush(clockstats.fp);
+ }
+}
+
+/*
+ * getauthkeys - read the authentication keys from the specified file
+ */
+void
+getauthkeys(keyfile)
+ char *keyfile;
+{
+ int len;
+
+ len = strlen(keyfile);
+ if (len == 0)
+ return;
+
+ if (key_file_name != 0) {
+ if (len > (int)strlen(key_file_name)) {
+ (void) free(key_file_name);
+ key_file_name = 0;
+ }
+ }
+
+ if (key_file_name == 0)
+ key_file_name = emalloc((u_int)(len + 1));
+
+ memmove(key_file_name, keyfile, len+1);
+
+ authreadkeys(key_file_name);
+}
+
+
+/*
+ * rereadkeys - read the authentication key file over again.
+ */
+void
+rereadkeys()
+{
+ if (key_file_name != 0)
+ authreadkeys(key_file_name);
+}
diff --git a/usr.sbin/xntpd/xntpd/ntpd.c b/usr.sbin/xntpd/xntpd/ntpd.c
new file mode 100644
index 0000000..ae06f56
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntpd.c
@@ -0,0 +1,461 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#if defined(SYS_HPUX)
+#include <sys/lock.h>
+#include <sys/rtprio.h>
+#endif
+
+#if defined(SYS_SVR4) || defined (SYS_UNIXWARE1)
+#include <termios.h>
+#endif
+
+#if (defined(SYS_SOLARIS)&&!defined(bsd)) || defined(__svr4__)
+#include <termios.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#ifdef LOCK_PROCESS
+#ifdef SYS_SOLARIS
+#include <sys/mman.h>
+#else
+#include <sys/lock.h>
+#endif
+#endif
+
+/*
+ * Signals we catch for debugging. If not debugging we ignore them.
+ */
+#define MOREDEBUGSIG SIGUSR1
+#define LESSDEBUGSIG SIGUSR2
+
+/*
+ * Signals which terminate us gracefully.
+ */
+#define SIGDIE1 SIGHUP
+#define SIGDIE2 SIGINT
+#define SIGDIE3 SIGQUIT
+#define SIGDIE4 SIGTERM
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPD_PRIO (-12)
+
+/*
+ * Debugging flag
+ */
+int debug;
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing;
+
+/*
+ * Version declaration
+ */
+extern char *Version;
+
+/*
+ * Alarm flag. Imported from timer module
+ */
+extern int alarm_flag;
+
+#if !defined(SYS_386BSD) && !defined(SYS_BSDI) && !defined(SYS_44BSD)
+/*
+ * We put this here, since the argument profile is syscall-specific
+ */
+extern int syscall P((int, struct timeval *, struct timeval *));
+#endif /* !SYS_386BSD */
+
+#ifdef SIGDIE1
+static RETSIGTYPE finish P((int));
+#endif /* SIGDIE1 */
+
+#ifdef DEBUG
+static RETSIGTYPE moredebug P((int));
+static RETSIGTYPE lessdebug P((int));
+#endif /* DEBUG */
+
+/*
+ * Main program. Initialize us, disconnect us from the tty if necessary,
+ * and loop waiting for I/O and/or timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+ int was_alarmed;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+
+ initializing = 1; /* mark that we are initializing */
+ debug = 0; /* no debugging by default */
+
+ getstartup(argc, argv); /* startup configuration, may set debug */
+
+#ifndef NODETACH
+ /*
+ * Detach us from the terminal. May need an #ifndef GIZMO.
+ */
+#ifdef DEBUG
+ if (!debug) {
+#endif /* DEBUG */
+#undef BSD19906
+#if defined(BSD)&&!defined(sun)&&!defined(SYS_SINIXM)
+#if (BSD >= 199006 && !defined(i386))
+#define BSD19906
+#endif /* BSD... */
+#endif /* BSD sun */
+#if defined(BSD19906) || defined(SYS_44BSD)
+ daemon(0, 0);
+#else /* BSD19906 */
+ if (fork())
+ exit(0);
+
+ {
+ u_long s;
+ int max_fd;
+#if defined(NTP_POSIX_SOURCE) && !defined(SYS_386BSD)
+ max_fd = sysconf(_SC_OPEN_MAX);
+#else /* NTP_POSIX_SOURCE */
+ max_fd = getdtablesize();
+#endif /* NTP_POSIX_SOURCE */
+ for (s = 0; s < max_fd; s++)
+ (void) close(s);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+#ifdef NTP_POSIX_SOURCE
+#if defined(SOLARIS) || defined(SYS_PTX) || defined(SYS_AUX3) || defined(SYS_AIX) || defined(SYS_ULTRIX)
+ (void) setsid();
+#else
+ (void) setpgid(0, 0);
+#endif
+#else /* NTP_POSIX_SOURCE */
+#ifdef HAVE_ATT_SETPGRP
+ (void) setpgrp();
+#else /* HAVE_ATT_SETPGRP */
+ (void) setpgrp(0, getpid());
+#endif /* HAVE_ATT_SETPGRP */
+#if defined(SYS_HPUX)
+ if (fork())
+ exit(0);
+#else /* SYS_HPUX */
+#ifdef SYS_DOMAINOS
+/*
+ * This breaks... the program fails to listen to any packets coming
+ * in on the UDP socket. So how do you break terminal affiliation?
+ */
+#else /* SYS_DOMAINOS */
+ {
+ int fid;
+
+ fid = open("/dev/tty", 2);
+ if (fid >= 0) {
+ (void) ioctl(fid, (u_long) TIOCNOTTY,
+ (char *) 0);
+ (void) close(fid);
+ }
+ (void) setpgrp(0, getpid());
+ }
+#endif /* SYS_DOMAINOS */
+#endif /* SYS_HPUX */
+#endif /* NTP_POSIX_SOURCE */
+ }
+#endif /* BSD19906 */
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+#endif /* NODETACH */
+
+ /*
+ * Logging. This may actually work on the gizmo board. Find a name
+ * to log with by using the basename of argv[0]
+ */
+ cp = strrchr(argv[0], '/');
+ if (cp == 0)
+ cp = argv[0];
+ else
+ cp++;
+
+#ifndef LOG_DAEMON
+ openlog(cp, LOG_PID);
+#else
+
+#ifndef LOG_NTP
+#define LOG_NTP LOG_DAEMON
+#endif
+ openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP);
+#ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+#endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+#endif /* LOG_DAEMON */
+
+ syslog(LOG_NOTICE, Version);
+
+
+#if defined(SYS_HPUX)
+ /*
+ * Lock text into ram, set real time priority
+ */
+ if (plock(TXTLOCK) < 0)
+ syslog(LOG_ERR, "plock() error: %m");
+ if (rtprio(0, 120) < 0)
+ syslog(LOG_ERR, "rtprio() error: %m");
+#else
+#if defined(LOCK_PROCESS)
+#if defined(MCL_CURRENT) && defined(MCL_FUTURE)
+ /*
+ * lock the process into memory
+ */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0)
+ syslog(LOG_ERR, "mlockall(): %m");
+#else
+#if defined(PROCLOCK)
+ /*
+ * lock the process into memory
+ */
+ if (plock(PROCLOCK) < 0)
+ syslog(LOG_ERR, "plock(): %m");
+#endif
+#endif
+#endif
+#if defined(NTPD_PRIO) && NTPD_PRIO != 0
+ /*
+ * Set the priority.
+ */
+#ifdef HAVE_ATT_NICE
+ nice (NTPD_PRIO);
+#endif /* HAVE_ATT_NICE */
+#ifdef HAVE_BSD_NICE
+ (void) setpriority(PRIO_PROCESS, 0, NTPD_PRIO);
+#endif /* HAVE_BSD_NICE */
+
+#endif /* !PROCLOCK || !LOCK_PROCESS */
+#endif /* SYS_HPUX */
+
+ /*
+ * Set up signals we pay attention to locally.
+ */
+#ifdef SIGDIE1
+ (void) signal_no_reset(SIGDIE1, finish);
+#endif /* SIGDIE1 */
+#ifdef SIGDIE2
+ (void) signal_no_reset(SIGDIE2, finish);
+#endif /* SIGDIE2 */
+#ifdef SIGDIE3
+ (void) signal_no_reset(SIGDIE3, finish);
+#endif /* SIGDIE3 */
+#ifdef SIGDIE4
+ (void) signal_no_reset(SIGDIE4, finish);
+#endif /* SIGDIE4 */
+
+#ifdef DEBUG
+ (void) signal_no_reset(MOREDEBUGSIG, moredebug);
+ (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
+#else
+ (void) signal_no_reset(MOREDEBUGSIG, SIG_IGN);
+ (void) signal_no_reset(LESSDEBUGSIG, SIG_IGN);
+#endif /* DEBUG */
+
+ /*
+ * Set up signals we should never pay attention to.
+ */
+#ifdef SIGPIPE
+ (void) signal_no_reset(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+ /*
+ * Call the init_ routines to initialize the data structures.
+ * Note that init_systime() may run a protocol to get a crude
+ * estimate of the time as an NTP client when running on the
+ * gizmo board. It is important that this be run before
+ * init_subs() since the latter uses the time of day to seed
+ * the random number generator. That is not the only
+ * dependency between these, either, be real careful about
+ * reordering.
+ */
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_systime();
+ init_timer();
+ init_lib();
+ init_random();
+ init_request();
+ init_control();
+ init_leap();
+ init_peer();
+#ifdef REFCLOCK
+ init_refclock();
+#endif
+ init_proto();
+ init_io();
+ init_loopfilter();
+
+ mon_start(MON_ON); /* monitor on by default now */
+ /* turn off in config if unwanted */
+
+ /*
+ * Get configuration. This (including argument list parsing) is
+ * done in a separate module since this will definitely be different
+ * for the gizmo board.
+ */
+ getconfig(argc, argv);
+ initializing = 0;
+
+ /*
+ * Report that we're up to any trappers
+ */
+ report_event(EVNT_SYSRESTART, (struct peer *)0);
+
+ /*
+ * Use select() on all on all input fd's for unlimited
+ * time. select() will terminate on SIGALARM or on the
+ * reception of input. Using select() means we can't do
+ * robust signal handling and we get a potential race
+ * between checking for alarms and doing the select().
+ * Mostly harmless, I think.
+ */
+ was_alarmed = 0;
+ rbuflist = (struct recvbuf *)0;
+ for (;;) {
+#ifndef HAVE_SIGNALED_IO
+ extern fd_set activefds;
+ extern int maxactivefd;
+
+ fd_set rdfdes;
+ int nfound;
+#else
+ block_io_and_alarm();
+#endif
+
+
+ rbuflist = getrecvbufs(); /* get received buffers */
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+
+ if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+#ifndef HAVE_SIGNALED_IO
+ rdfdes = activefds;
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+ if (nfound > 0) {
+ l_fp ts;
+
+ get_systime(&ts);
+ (void)input_handler(&ts);
+ } else if (nfound == -1 && errno != EINTR)
+ syslog(LOG_ERR, "select() error: %m");
+#else
+ wait_for_signal();
+#endif
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+ }
+#ifdef HAVE_SIGNALED_IO
+ unblock_io_and_alarm();
+#endif
+
+ /*
+ * Out here, signals are unblocked. Call timer routine
+ * to process expiry.
+ */
+ if (was_alarmed) {
+ timer();
+ was_alarmed = 0;
+ }
+
+ /*
+ * Call the data procedure to handle each received
+ * packet.
+ */
+ while (rbuflist != (struct recvbuf *)0) {
+ rbuf = rbuflist;
+ rbuflist = rbuf->next;
+ (rbuf->receiver)(rbuf);
+ freerecvbuf(rbuf);
+ }
+ /*
+ * Go around again
+ */
+ }
+}
+
+
+#ifdef SIGDIE1
+/*
+ * finish - exit gracefully
+ */
+static RETSIGTYPE
+finish(sig)
+int sig;
+{
+
+ /*
+ * Log any useful info before exiting.
+ */
+#ifdef notdef
+ log_exit_stats();
+#endif
+ exit(0);
+}
+#endif /* SIGDIE1 */
+
+
+#ifdef DEBUG
+/*
+ * moredebug - increase debugging verbosity
+ */
+static RETSIGTYPE
+moredebug(sig)
+int sig;
+{
+ if (debug < 255) {
+ debug++;
+ syslog(LOG_DEBUG, "debug raised to %d", debug);
+ }
+}
+
+/*
+ * lessdebug - decrease debugging verbosity
+ */
+static RETSIGTYPE
+lessdebug(sig)
+int sig;
+{
+ if (debug > 0) {
+ debug--;
+ syslog(LOG_DEBUG, "debug lowered to %d", debug);
+ }
+}
+#endif /* DEBUG */
diff --git a/usr.sbin/xntpd/xntpd/refclock_acts.c b/usr.sbin/xntpd/xntpd/refclock_acts.c
new file mode 100644
index 0000000..ad3c33a
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_acts.c
@@ -0,0 +1,895 @@
+/*
+ * refclock_acts - clock driver for the NIST Automated Computer Time
+ * Service aka Amalgamated Containerized Trash Service (ACTS)
+ */
+#if defined(REFCLOCK) && defined(ACTS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NIST Automated Computer Time Service (ACTS).
+ * It periodically dials a prespecified telephone number, receives the
+ * NIST timecode data and calculates the local clock correction. It is
+ * designed primarily for use as a backup when neither a radio clock nor
+ * connectivity to Internet time servers is available. For the best
+ * accuracy, the individual telephone line/modem delay needs to be
+ * calibrated using outside sources.
+ *
+ * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ * toll call from a residence telephone in Newark, DE, costs between 14
+ * and 27 cents, depending on time of day, and from a campus telephone
+ * between 3 and 4 cents, although it is not clear what carrier and time
+ * of day discounts apply in this case. The modem dial string will
+ * differ depending on local telephone configuration, etc., and is
+ * specified by the phone command in the configuration file. The
+ * argument to this command is an AT command for a Hayes compatible
+ * modem.
+ *
+ * The accuracy produced by this driver should be in the range of a
+ * millisecond or two, but may need correction due to the delay
+ * characteristics of the individual modem involved. For undetermined
+ * reasons, some modems work with the ACTS echo-delay measurement scheme
+ * and some don't. This driver tries to do the best it can with what it
+ * gets. Initial experiments with a Practical Peripherals 9600SA modem
+ * here in Delaware suggest an accuracy of a millisecond or two can be
+ * achieved without the scheme by using a fudge time1 value of 65.0 ms.
+ * In either case, the dispersion for a single call involving ten
+ * samples is about 1.3 ms.
+ *
+ * The driver can operate in either of three modes, as determined by
+ * the mode parameter in the server configuration command. In mode 0
+ * (automatic) the driver operates continuously at intervals depending
+ * on the prediction error, as measured by the driver, usually in the
+ * order of several hours. In mode 1 (backup) the driver is enabled in
+ * automatic mode only when no other source of synchronization is
+ * available and when more than MAXOUTAGE (3600 s) have elapsed since
+ * last synchronized by other sources. In mode 2 (manual) the driver
+ * operates only when enabled using a fudge flags switch, as described
+ * below.
+ *
+ * For reliable call management, this driver requires a 1200-bps modem
+ * with a Hayes-compatible command set and control over the modem data
+ * terminal ready (DTR) control line. Present restrictions require the
+ * use of a POSIX-compatible programming interface, although other
+ * interfaces may work as well. The modem setup string is hard-coded in
+ * the driver and may require changes for nonstandard modems or special
+ * circumstances.
+ *
+ * Further information can be found in the README.refclock file in the
+ * xntp3 distribution.
+ *
+ * Fudge Factors
+ *
+ * Ordinarily, the propagation time correction is computed automatically
+ * by ACTS and the driver. When this is not possible or erratic due to
+ * individual modem characteristics, the fudge flag2 switch should be
+ * set to disable the ACTS echo-delay scheme. In any case, the fudge
+ * time1 parameter can be used to adjust the propagation delay as
+ * required.
+ *
+ * The ACTS call interval is determined in one of three ways. In manual
+ * mode a call is initiated by setting fudge flag1 using xntpdc, either
+ * manually or via a cron job. In AUTO mode this flag is set by the peer
+ * timer, which is controlled by the sys_poll variable in response to
+ * measured errors. In backup mode the driver is ordinarily asleep, but
+ * awakes (in auto mode) if all other synchronization sources are lost.
+ * In either auto or backup modes, the call interval increases as long
+ * as the measured errors do not exceed the value of the fudge time2
+ * parameter.
+ *
+ * When the fudge flag1 is set, the ACTS calling program is activated.
+ * This program dials each number listed in the phones command of the
+ * configuration file in turn. If a call attempt fails, the next number
+ * in the list is dialed. The fudge flag1 and counter are reset and the
+ * calling program terminated if (a) a valid clock update has been
+ * determined, (b) no more numbers remain in the list, (c) a device
+ * fault or timeout occurs or (d) fudge flag1 is reset manually using
+ * xntpdc.
+ *
+ * In automatic and backup modes, the driver determines the call
+ * interval using a procedure depending on the measured prediction
+ * error and the fudge time2 parameter. If the error exceeds time2 for a
+ * number of times depending on the current interval, the interval is
+ * decreased, but not less than about 1000 s. If the error is less than
+ * time2 for some number of times, the interval is increased, but not
+ * more than about 18 h. With the default value of zero for fudge time2,
+ * the interval will increase from 1000 s to the 4000-8000-s range, in
+ * which the expected accuracy should be in the 1-2-ms range. Setting
+ * fudge time2 to a large value, like 0.1 s, may result in errors of
+ * that order, but increase the call interval to the maximum. The exact
+ * value for each configuration will depend on the modem and operating
+ * system involved, so some experimentation may be necessary.
+ */
+
+/*
+ * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
+ * (reformatted from ACTS on-line computer help information)
+ *
+ * The following is transmitted (at 1200 baud) following completion of
+ * the telephone connection.
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
+ * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
+ * etc..etc...etc.......
+ *
+ * UTC = Universal Time Coordinated, the official world time referred to
+ * the zero meridian.
+ *
+ * DST Daylight savings time characters, valid for the continental
+ * U.S., are set as follows:
+ *
+ * 00 We are on standard time (ST).
+ * 01-49 Now on DST, go to ST when your local time is 2:00 am and
+ * the count is 01. The count is decremented daily at 00
+ * (UTC).
+ * 50 We are on DST.
+ * 51-99 Now on ST, go to DST when your local time is 2:00 am and
+ * the count is 51. The count is decremented daily at 00
+ * (UTC).
+ *
+ * The two DST characters provide up to 48 days advance notice of a
+ * change in time. The count remains at 00 or 50 at other times.
+ *
+ * LS Leap second flag is set to "1" to indicate that a leap second is
+ * to be added as 23:59:60 (UTC) on the last day of the current UTC
+ * month. The LS flag will be reset to "0" starting with 23:59:60
+ * (UTC). The flag will remain on for the entire month before the
+ * second is added. Leap seconds are added as needed at the end of
+ * any month. Usually June and/or December are chosen.
+ *
+ * The leap second flag will be set to a "2" to indicate that a
+ * leap second is to be deleted at 23:59:58--00:00:00 on the last
+ * day of the current month. (This latter provision is included per
+ * international recommendation, however it is not likely to be
+ * required in the near future.)
+ *
+ * DUT1 Approximate difference between earth rotation time (UT1) and
+ * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC.
+ *
+ * MJD Modified Julian Date, often used to tag certain scientific data.
+ *
+ * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity.
+ * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud
+ * the MJD and DUT1 values are deleted and the time is transmitted only
+ * on even seconds.
+ *
+ * Maximum on line time will be 56 seconds. If all lines are busy at any
+ * time, the oldest call will be terminated if it has been on line more
+ * than 28 seconds, otherwise, the call that first reaches 28 seconds
+ * will be terminated.
+ *
+ * Current time is valid at the "on-time" marker (OTM), either "*" or
+ * "#". The nominal on-time marker (*) will be transmitted 45 ms early
+ * to account for the 8 ms required to send 1 character at 1200 Baud,
+ * plus an additional 7 ms for delay from NIST to the user, and
+ * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems.
+ * If the caller echoes all characters, NIST will measure the round trip
+ * delay and advance the on-time marker so that the midpoint of the stop
+ * bit arrives at the user on time. The amount of msADV will reflect the
+ * actual required advance in milliseconds and the OTM will be a "#".
+ *
+ * (The NIST system requires 4 or 5 consecutive delay measurements which
+ * are consistent before switching from "*" to "#". If the user has a
+ * 1200 Baud modem with the same internal delay as that used by NIST,
+ * then the "#" OTM should arrive at the user within +-2 ms of the
+ * correct time.
+ *
+ * However, NIST has studied different brands of 1200 Baud modems and
+ * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM
+ * of +-10 ms. For many computer users, +-10 ms accuracy should be more
+ * than adequate since many computer internal clocks can only be set
+ * with granularity of 20 to 50 ms. In any case, the repeatability of
+ * the offset for the "#" OTM should be within +-2 ms, if the dial-up
+ * path is reciprocal and the user doesn't change the brand or model of
+ * modem used.
+ *
+ * This should be true even if the dial-up path on one day is a land-
+ * line of less than 40 ms (one way) and on the next day is a satellite
+ * link of 260 to 300 ms. In the rare event that the path is one way by
+ * satellite and the other way by land line with a round trip
+ * measurement in the range of 90 to 260 ms, the OTM will remain a "*"
+ * indicating 45 ms advance.
+ *
+ * For user comments write:
+ * NIST-ACTS
+ * Time and Frequency Division
+ * Mail Stop 847
+ * 325 Broadway
+ * Boulder, CO 80303
+ *
+ * Software for setting (PC)DOS compatable machines is available on a
+ * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference
+ * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301)
+ * 975-6776
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "ACTS" /* reference ID */
+#define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define MSGCNT 10 /* we need this many ACTS messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 50 /* length of valid timecode string */
+#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define ACTS_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max before ACTS kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE 15 /* timecode timeout (s) */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern u_long last_time; /* last clock update time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_config module
+ */
+extern char sys_phone[][MAXDIAL]; /* modem dial strings */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* who is running the show */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern struct peer *sys_peer; /* system peer structure pointer */
+
+/*
+ * Tables to compute the ddd of year from icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ struct event timer; /* timeout timer */
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of ACTS messages received */
+ long redial; /* interval to next automatic call */
+ double msADV; /* millisecond advance of last message */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static int acts_write P((struct peer *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used (old acts_control) */
+ noentry, /* not used (old acts_init) */
+ noentry, /* not used (old acts_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * acts_start - open the devices and initialize data for processing
+ */
+static int
+acts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) {
+ syslog(LOG_ERR, "clock %s ACTS no modem control",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct actsunit *)
+ emalloc(sizeof(struct actsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct actsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = ACTS_MINPOLL;
+ peer->maxpoll = ACTS_MAXPOLL;
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!acts_write(peer, MODEM_SETUP)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Set up the driver timeout
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = acts_timeout;
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ TIMER_DEQUEUE(&up->timer);
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ int i;
+ l_fp tstmp;
+ u_fp disp;
+ char hangup = '%'; /* ACTS hangup */
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ u_int dst; /* daylight/standard time indicator */
+ u_int leap; /* leap-second indicator */
+ double dut1; /* DUT adjustment */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* this is NIST and you're not */
+ char flag; /* calibration flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("acts: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 2.
+ */
+ (void)strncpy(str, strtok(pp->lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS modem status %s",
+ ntoa(&peer->srcadr), pp->lastcode);
+ acts_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + CONNECT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->msgcnt = 0;
+ up->state++;
+ }
+ return;
+
+ case 2:
+
+ /*
+ * State 2. The call has been answered and we are
+ * waiting for the first ACTS message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or ACTS is
+ * down.
+ */
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + TIMECODE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->state++;
+ }
+
+ /*
+ * Real yucky things here. Ignore everything except timecode
+ * messages, as determined by the message length. We told the
+ * terminal routines to end the line with '*' and the line
+ * discipline to strike a timestamp on that character. However,
+ * when the ACTS echo-delay scheme works, the '*' eventually
+ * becomes a '#'. In this case the message is ended by the <CR>
+ * that comes about 200 ms after the '#' and the '#' cannot be
+ * echoed at the proper time. But, this may not be a lose, since
+ * we already have good data from prior messages and only need
+ * the millisecond advance calculated by ACTS. So, if the
+ * message is long enough and has an on-time character at the
+ * right place, we consider the message (but not neccesarily the
+ * timestmap) to be valid.
+ */
+ if (pp->lencode != LENCODE)
+ return;
+
+ /*
+ * We apparently have a valid timecode message, so dismember it
+ * with sscan(). This routine does a good job in spotting syntax
+ * errors without becoming overly pedantic.
+ *
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV OTM
+ * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) *
+ */
+ if (sscanf(pp->lastcode,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute,
+ &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Some modems can't be trusted (the Practical Peripherals
+ * 9600SA comes to mind) and, even if they manage to unstick
+ * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2
+ * to disable echoes, if neccessary.
+ */
+ if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag &
+ CLK_FLAG2))
+ (void)write(pp->io.fd, &flag, 1);
+
+ /*
+ * Yes, I know this code incorrectly thinks that 2100 is a leap
+ * year. The ACTS timecode format croaks then anyway. Life is
+ * short. Would only the timecode mavens resist the urge to
+ * express months of the year and days of the month in favor of
+ * days of the year.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received. If we collect MSGCNT samples before the '#' on-time
+ * character, we use the results of the filter as is. If the '#'
+ * is found before that, the adjusted msADV is used to correct
+ * the propagation delay.
+ */
+ up->msgcnt++;
+ if (flag == '#') {
+ L_CLR(&tstmp);
+ TVUTOTSF((long)((msADV - up->msADV) * 1000.),
+ tstmp.l_uf);
+ L_ADD(&pp->offset, &tstmp);
+ } else {
+ up->msADV = msADV;
+ if (!refclock_process(pp, up->msgcnt, up->msgcnt -
+ up->msgcnt / 3)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+ }
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ * The little dance with the '%' character is an undocumented
+ * ACTS feature that hangs up the phone real quick without
+ * waiting for carrier loss or long-space disconnect, but we do
+ * these clumsy things anyway.
+ */
+ disp = LFPTOFP(&pp->fudgetime2);
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion +
+ (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ TIMER_DEQUEUE(&up->timer);
+ (void)write(pp->io.fd, &hangup, 1);
+ up->state = 0;
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call to ACTS. If not, the enable flag can be set using
+ * xntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+/*
+ * acts_timeout - called by the timer interrupt
+ */
+static void
+acts_timeout(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->state) {
+ acts_disc(peer);
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the ACTS calling program is activated
+ * by the xntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the ACTS calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the ACTS calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->refclktype !=
+ REFCLK_NIST_ACTS) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ syslog(LOG_NOTICE,
+ "clock %s ACTS invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via xntpdc, the ACTS calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Initiate a call to the ACTS service. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds. We advance to the next modem dial
+ * string. If none are left, we log a notice and clear the
+ * enable flag. For future enhancement: call the site RP and
+ * leave an obscene message in his voicemail.
+ */
+ if (sys_phone[up->pollcnt][0] == '\0') {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+#ifdef DEBUG
+ if (debug)
+ printf("acts: calling program terminated\n");
+#endif
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Raise DTR, call ACTS and start the answer timeout. We think
+ * it strange if the OK status has not been received from the
+ * modem, but plow ahead anyway.
+ */
+ if (strcmp(pp->lastcode, "OK") != 0)
+ syslog(LOG_NOTICE, "clock %s ACTS no modem status",
+ ntoa(&peer->srcadr));
+ (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ (void)acts_write(peer, sys_phone[up->pollcnt]);
+ syslog(LOG_NOTICE, "clock %s ACTS calling %s\n",
+ ntoa(&peer->srcadr), sys_phone[up->pollcnt]);
+ up->state = 1;
+ up->pollcnt++;
+ pp->polls++;
+ up->timer.event_time = current_time + ANSWER;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+acts_disc(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while ACTS is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ if (up->state > 0) {
+ up->state = 0;
+ syslog(LOG_NOTICE, "clock %s ACTS call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: call failed %d\n", up->state);
+#endif
+ }
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_write - write a message to the serial port
+ */
+static int
+acts_write(peer, str)
+ struct peer *peer;
+ char *str;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_as2201.c b/usr.sbin/xntpd/xntpd/refclock_as2201.c
new file mode 100644
index 0000000..afacb6f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_as2201.c
@@ -0,0 +1,453 @@
+/*
+ * refclock_as2201 - clock driver for the Austron 2201A GPS
+ * Timing Receiver
+ */
+#if defined(REFCLOCK) && defined(AS2201)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * This driver supports the Austron 2200A/2201A GPS Receiver with
+ * Buffered RS-232-C Interface Module. Note that the original 2200/2201
+ * receivers will not work reliably with this driver, since the older
+ * design cannot accept input commands at any reasonable data rate.
+ *
+ * The program sends a "*toc\r" to the radio and expects a response of
+ * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
+ * day of year, hh:mm:ss = second of day and mmm = millisecond of
+ * second. Then, it sends statistics commands to the radio and expects
+ * a multi-line reply showing the corresponding statistics or other
+ * selected data. Statistics commands are sent in order as determined by
+ * a vector of commands; these might have to be changed with different
+ * radio options. If flag4 of the fudge configuration command is set to
+ * 1, the statistics data are written to the clockstats file for later
+ * processing.
+ *
+ * In order for this code to work, the radio must be placed in non-
+ * interactive mode using the "off" command and with a single <cr>
+ * resonse using the "term cr" command. The setting of the "echo"
+ * and "df" commands does not matter. The radio should select UTC
+ * timescale using the "ts utc" command.
+ *
+ * There are two modes of operation for this driver. The first with
+ * default configuration is used with stock kernels and serial-line
+ * drivers and works with almost any machine. In this mode the driver
+ * assumes the radio captures a timestamp upon receipt of the "*" that
+ * begins the driver query. Accuracies in this mode are in the order of
+ * a millisecond or two and the receiver can be connected to only one
+ * host.
+ *
+ * The second mode of operation can be used for SunOS kernels that have
+ * been modified with the ppsclock streams module included in this
+ * distribution. The mode is enabled if flag3 of the fudge configuration
+ * command has been set to 1. In this mode a precise timestamp is
+ * available using a gadget box and 1-pps signal from the receiver. This
+ * improves the accuracy to the order of a few tens of microseconds. In
+ * addition, the serial output and 1-pps signal can be bussed to
+ * additional receivers.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define SMAX 200 /* statistics buffer length */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+#ifdef PPS
+/*
+ * Imported from loop_filter module
+ */
+extern int fdpps; /* ppsclock file descriptor */
+#endif /* PPS */
+
+/*
+ * AS2201 unit control structure.
+ */
+struct as2201unit {
+ int pollcnt; /* poll message counter */
+
+ char *lastptr; /* statistics buffer pointer */
+ char stats[SMAX]; /* statistics buffer */
+
+#ifdef PPS
+ u_long lastev; /* last ppsclock second */
+#endif /* PPS */
+
+ int linect; /* count of lines remaining */
+ int index; /* current statistics command */
+};
+
+/*
+ * Radio commands to extract statitistics
+ *
+ * A command consists of an ASCII string terminated by a <cr> (\r). The
+ * command list consist of a sequence of commands terminated by a null
+ * string ("\0"). One command from the list is sent immediately
+ * following each received timecode (*toc\r command) and the ASCII
+ * strings received from the radio are saved along with the timecode in
+ * the clockstats file. Subsequent commands are sent at each timecode,
+ * with the last one in the list followed by the first one. The data
+ * received from the radio consist of ASCII strings, each terminated by
+ * a <cr> (\r) character. The number of strings for each command is
+ * specified as the first line of output as an ASCII-encode number. Note
+ * that the ETF command requires the Input Buffer Module and the LORAN
+ * commands require the LORAN Assist Module. However, if these modules
+ * are not installed, the radio and this driver will continue to operate
+ * successfuly, but no data will be captured for these commands.
+ */
+static char stat_command[][30] = {
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "ID;OPT;VER\r", /* model; options; software version */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "UTC\r", /* UTC leap info */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
+ "\0" /* end of table */
+};
+
+/*
+ * Function prototypes
+ */
+static int as2201_start P((int, struct peer *));
+static void as2201_shutdown P((int, struct peer *));
+static void as2201_receive P((struct recvbuf *));
+static void as2201_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_as2201 = {
+ as2201_start, /* start up driver */
+ as2201_shutdown, /* shut down driver */
+ as2201_poll, /* transmit poll message */
+ noentry, /* not used (old as2201_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old as2201_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * as2201_start - open the devices and initialize data for processing
+ */
+static int
+as2201_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct as2201unit *)
+ emalloc(sizeof(struct as2201unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct as2201unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = as2201_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+
+ up->lastptr = up->stats;
+ up->index = 0;
+ return (1);
+}
+
+
+/*
+ * as2201_shutdown - shut down the clock
+ */
+static void
+as2201_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * as2201__receive - receive data from the serial interface
+ */
+static void
+as2201_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+#ifdef PPS
+ long ltemp;
+ struct ppsclockev ev;
+#endif /* PPS */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: timecode %d %d %s\n",
+ up->linect, pp->lencode, pp->lastcode);
+#endif
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linect is greater than zero, we must be in the middle of a
+ * statistics operation, so simply tack the received data at the
+ * end of the statistics string. If not, we could either have
+ * just received the timecode itself or a decimal number
+ * indicating the number of following lines of the statistics
+ * reply. In the former case, write the accumulated statistics
+ * data to the clockstats file and continue onward to process
+ * the timecode; in the later case, save the number of lines and
+ * quietly return.
+ */
+ if (up->linect > 0) {
+ up->linect--;
+ if (up->lastptr - up->stats + pp->lencode > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, pp->lastcode);
+ up->lastptr += pp->lencode;
+ return;
+ } else {
+ if (pp->lencode == 1) {
+ up->linect = atoi(pp->lastcode);
+ return;
+ } else {
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, up->stats);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: stat %s\n", up->stats);
+#endif
+ }
+ }
+ up->lastptr = up->stats;
+ *up->lastptr = '\0';
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTOC) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "yy:ddd:hh:mm:ss.mmm"
+ */
+ if (sscanf(pp->lastcode, "%2d:%3d:%2d:%2d:%2d.%3d", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->msec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Test for synchronization (this is a temporary crock).
+ */
+ if (pp->lastcode[2] != ':') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+#ifdef PPS
+
+ /*
+ * If CLK_FLAG3 is set and the local time is within +-0.5 second
+ * of the timecode, use the pps offset instead. Note that we
+ * believe the ppsclock timestamp only if the ioctl works and
+ * the new timestamp is greater than the previous one.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG3 && fdpps != -1) {
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, pp->lastrec.l_ui, &pp->yearstart,
+ &pp->lastref.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(pp->msec, pp->lastref.l_uf);
+ pp->lastrec = trtmp;
+ L_SUB(&trtmp, &pp->lastref);
+ if (L_ISNEG(&trtmp))
+ L_NEG(&trtmp);
+ if (trtmp.l_i < CLOCK_MAX_I || (trtmp.l_i == CLOCK_MAX_I
+ && trtmp.l_uf < CLOCK_MAX_F)) {
+ if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) {
+ if (up->lastev < ev.tv.tv_sec) {
+ TVUTOTSF(ev.tv.tv_usec, ltemp);
+ pp->lastrec = pp->lastref;
+ L_ADDF(&pp->lastrec, ltemp);
+ }
+ up->lastev = ev.tv.tv_sec;
+ }
+ }
+ }
+#endif /* PPS */
+#ifdef DEBUG
+ if (debug)
+ printf("gps: times %s %s %s\n",
+ ulfptoa(&pp->lastref, 6), ulfptoa(&pp->lastrec, 6),
+ lfptoa(&trtmp, 6));
+#endif
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+
+ /*
+ * If CLK_FLAG4 is set, initialize the statistics buffer and
+ * send the next command. If not, simply write the timecode to
+ * the clockstats file.
+ */
+ (void)strcpy(up->lastptr, pp->lastcode);
+ up->lastptr += pp->lencode;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, stat_command[up->index]);
+ up->lastptr += strlen(stat_command[up->index]);
+ up->lastptr--;
+ *up->lastptr = '\0';
+ (void)write(pp->io.fd, stat_command[up->index],
+ strlen(stat_command[up->index]));
+ up->index++;
+ if (*stat_command[up->index] == '\0')
+ up->index = 0;
+ }
+}
+
+
+/*
+ * as2201_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+as2201_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Send a "\r*toc\r" to get things going. We go to great pains
+ * to avoid changing state, since there may be more than one
+ * eavesdropper watching the radio.
+ */
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ gettstamp(&pp->lastrec);
+ if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/refclock_atom.c b/usr.sbin/xntpd/xntpd/refclock_atom.c
new file mode 100644
index 0000000..8df49d2
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_atom.c
@@ -0,0 +1,499 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#if defined(REFCLOCK) && defined(ATOM)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * This driver furnishes an interface for pulse-per-second (PPS) signals
+ * produced by a cesium clock, timing receiver or related equipment. It
+ * can be used to remove accumulated jitter and retime a secondary
+ * server when synchronized to a primary server over a congested, wide-
+ * area network and before redistributing the time to local clients.
+ *
+ * In order for this driver to work, the local clock must be set to
+ * within +-500 ms by another means, such as a radio clock or NTP
+ * itself. The 1-pps signal is connected via a serial port and gadget
+ * box consisting of a one-shot and RS232 level converter. When operated
+ * at 38.4 kbps with a SPARCstation IPC, this arrangement has a worst-
+ * case jitter less than 26 us.
+ *
+ * There are three ways in which this driver can be used. The first way
+ * uses the LDISC_PPS line discipline and works only for the baseboard
+ * serial ports of the Sun SPARCstation. The PPS signal is connected via
+ * a gadget box to the carrier detect (CD) line of a serial port and
+ * flag3 of the driver configured for that port is set. This causes the
+ * ppsclock streams module to be configured for that port and capture a
+ * timestamp at the on-time transition of the PPS signal. This driver
+ * then reads the timestamp directly by a designated ioctl() system
+ * call. This provides the most accurate time and least jitter of any
+ * other scheme. There is no need to configure a dedicated device for
+ * this purpose, which ordinarily is the device used for the associated
+ * radio clock.
+ *
+ * The second way uses the LDISC_CLKPPS line discipline and works for
+ * any architecture supporting a serial port. If after a few seconds
+ * this driver finds no ppsclock module configured, it attempts to open
+ * a serial port device /dev/pps%d, where %d is the unit number, and
+ * assign the LDISC_CLKPPS line discipline to it. If the line discipline
+ * fails, no harm is done except the accuracy is reduced somewhat. The
+ * pulse generator in the gadget box is adjusted to produce a start bit
+ * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line
+ * discipline, this produces an ASCII DEL character ('\377') followed by
+ * a timestamp at each seconds epoch.
+ *
+ * The third way involves an auxiliary radio clock driver which calls
+ * the PPS driver with a timestamp captured by that driver. This use is
+ * documented in the source code for the driver(s) involved.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic and those
+ * explicitly defined above. The fudge time1 parameter can be used to
+ * compensate for miscellaneous UART and OS delays. Allow about 247 us
+ * for uart delays at 38400 bps and about 1 ms for SunOS streams
+ * nonsense.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#ifdef B38400
+#define SPEED232 B38400 /* uart speed (38400 baud) */
+#else
+#define SPEED232 EXTB /* as above */
+#endif
+#define PRECISION (-20) /* precision assumed (about 1 usec) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+
+#define PPSMAXDISPERSE (FP_SECOND / 100) /* max sample dispersion */
+#define NSAMPLES 32 /* final stages of median filter */
+#ifdef PPS
+#define PPS_POLL 2 /* ppsclock poll interval (s) */
+#endif /* PPS */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+extern int pps_update; /* prefer peer valid update */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* somebody in charge */
+
+/*
+ * Unit control structure
+ */
+struct atomunit {
+#ifdef PPS
+ struct event timer; /* pps poll interval timer */
+ struct ppsclockev ev; /* ppsclock control */
+#endif /* PPS */
+ int pollcnt; /* poll message counter */
+};
+
+/*
+ * Global variables
+ */
+struct peer *last_atom_peer; /* peer structure pointer */
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_shutdown P((int, struct peer *));
+static void atom_receive P((struct recvbuf *));
+static void atom_poll P((int, struct peer *));
+#ifdef PPS
+static void atom_pps P((struct peer *));
+#endif /* PPS */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ noentry, /* not used (old atom_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old atom_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct atomunit *)
+ emalloc(sizeof(struct atomunit))))
+ return (0);
+ memset((char *)up, 0, sizeof(struct atomunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ pp->nstages = MAXSTAGE;
+
+#ifdef PPS
+ /*
+ * Arm the timer for the first interrupt. Give it ten seconds to
+ * allow the ppsclock line to be configured, since it could be
+ * assigned to another driver.
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = atom_pps;
+ up->timer.event_time = current_time + 10;
+ TIMER_INSERT(timerqueue, &up->timer);
+#endif /* PPS */
+ last_atom_peer = peer;
+ return (1);
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ if (last_atom_peer == peer)
+ last_atom_peer = 0;
+#ifdef PPS
+ TIMER_DEQUEUE(&up->timer);
+#endif /* PPS */
+ if (pp->io.fd)
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * pps_sample - process pps sample offset -- backwards compatible
+ * interface
+ */
+int
+pps_sample(tsr)
+ l_fp *tsr;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ register struct atomunit *up;
+ int i;
+ l_fp lftemp; /* l_fp temps */
+
+ /*
+ * This routine is called once per second by an auxilliary
+ * routine in another driver. It saves the sign-extended
+ * fraction supplied in the argument in a circular buffer for
+ * processing at the next poll event.
+ */
+ peer = last_atom_peer;
+ if (!peer)
+ return (-1); /* no ATOM configured ? Forget it ! */
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ L_CLR(&lftemp);
+ L_ADDF(&lftemp, tsr->l_f);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftemp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+
+ /* HACK -- use the local UN*X clock to get the time -- this is wrong */
+ pp->lastrec.l_ui = time(0) - 2 + JAN_1970;
+ pp->lastrec.l_uf = 0;
+
+ return (0);
+}
+
+#ifdef PPS
+/*
+ * atom_pps - receive data from the LDISC_PPS discipline
+ */
+static void
+atom_pps(peer)
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ int i;
+
+ /*
+ * This routine is called once per second when the LDISC_PPS
+ * discipline is present. It snatches the pps timestamp from the
+ * kernel and saves the sign-extended fraction in a circular
+ * buffer for processing at the next poll event.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * Arm the timer for the next interrupt
+ */
+ up->timer.event_time = current_time + PPS_POLL;
+ TIMER_INSERT(timerqueue, &up->timer);
+
+ /*
+ * Convert the timeval to l_fp and save for billboards. Sign-
+ * extend the fraction and stash in the buffer. No harm is done
+ * if previous data are overwritten. If the discipline comes bum
+ * or the data grow stale, just forget it.
+ */
+ i = up->ev.serial;
+ if (ioctl(fdpps, CIOGETEV, (caddr_t)&up->ev) < 0)
+ return;
+ if (i == up->ev.serial)
+ return;
+ pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970;
+ TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf);
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ L_NEG(&lftmp);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftmp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+}
+#endif /* PPS */
+
+/*
+ * atom_receive - receive data from the serial line interface
+ */
+static void
+atom_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp lftmp;
+ int i;
+
+ /*
+ * This routine is called once per second when the serial
+ * interface is in use. It snatches the timestamp from the
+ * buffer and saves the sign-extended fraction in a circular
+ * buffer for processing at the next poll event.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * Save the timestamp for billboards. Sign-extend the fraction
+ * and stash in the buffer. No harm is done if previous data are
+ * overwritten.
+ */
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ L_NEG(&lftmp);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftmp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+}
+
+/*
+ * Compare two l_fp's - used with qsort()
+ */
+static int
+atom_cmpl_fp(p1, p2)
+ register void *p1, *p2; /* l_fp to compare */
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+/*
+ * atom_poll - called by the transmit procedure
+ */
+static void
+atom_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ int i, n;
+ l_fp median, lftmp;
+ l_fp off[MAXSTAGE];
+ u_fp disp;
+
+ /*
+ * At each poll we check for timeout. At the first timeout we
+ * test to see if the LDISC_PPS discipline is present and, if
+ * so, use that. If not, we attempt to open a serial line with
+ * LDISC_CLKPPS discipline. If that fails, we bitch to the log
+ * and clam up.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->polls++;
+ if (up->pollcnt == 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ up->pollcnt--;
+ if (up->pollcnt == 0) {
+ if (!pp->io.fd && fdpps == -1) {
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLKPPS line discipline,
+ * if available. If unavailable, the code works
+ * anyway, but at reduced accuracy.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232,
+ LDISC_CLKPPS))) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->io.clock_recv = atom_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Valid time (leap bits zero) is returned only if the prefer
+ * peer has survived the intersection algorithm and within
+ * CLOCK_MAX of local time and not too long ago. This insures
+ * the pps time is within +-0.5 s of the local time and the
+ * seconds numbering is unambiguous.
+ */
+ if (pps_update) {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ } else
+ pp->leap = LEAP_NOTINSYNC;
+
+ /*
+ * Copy the raw offsets and sort into ascending order
+ */
+ for (i = 0; i < MAXSTAGE; i++)
+ off[i] = pp->filter[i];
+ qsort((char *)off, pp->nstages, sizeof(l_fp), atom_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of nstages samples until
+ * nskeep samples remain.
+ */
+ i = 0;
+ n = pp->nstages;
+ while ((n - i) > NSAMPLES) {
+ lftmp = off[n - 1];
+ median = off[(n + i) / 2];
+ L_SUB(&lftmp, &median);
+ L_SUB(&median, &off[i]);
+ if (L_ISHIS(&median, &lftmp)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. Add to this the time since
+ * the last clock update, which represents the dispersion
+ * increase with time. We know that NTP_MAXSKEW is 16. If the
+ * sum is greater than the allowed sample dispersion, bail out.
+ * Otherwise, return the median offset plus the configured
+ * fudgetime1 value.
+ */
+ lftmp = off[n - 1];
+ L_SUB(&lftmp, &off[i]);
+ disp = LFPTOFP(&lftmp) + current_time - pp->lasttime;
+ if (disp > PPSMAXDISPERSE) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->offset = off[(n + 1) / 2];
+ L_ADD(&pp->offset, &pp->fudgetime1);
+ pp->dispersion = disp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_chu.c b/usr.sbin/xntpd/xntpd/refclock_chu.c
new file mode 100644
index 0000000..7b710fc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_chu.c
@@ -0,0 +1,800 @@
+/*
+ * refclock_chu - clock driver for the CHU time code
+ */
+#if defined(REFCLOCK) && defined(CHU)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include <sys/chudefs.h>
+#include "ntp_stdlib.h"
+
+/*
+ * The CHU time signal includes a time code which is modulated at the
+ * standard Bell 103 frequencies (i.e. mark=2225Hz, space=2025Hz).
+ * and formatted into 8 bit characters with one start bit and two
+ * stop bits. The time code is composed of 10 8-bit characters.
+ * The second 5 bytes of the timecode are a redundancy check, and
+ * are a copy of the first 5 bytes.
+ *
+ * It is assumed that you have built or modified a Bell 103 standard
+ * modem, attached the input to the output of a radio and cabled the
+ * output to a serial port on your computer, i.e. what you are receiving
+ * is essentially the output of your radio. It is also assumed you have
+ * installed a special CHU line discipline to condition the output from
+ * the terminal driver and take accurate time stamps.
+ *
+ * There are two types of timecodes. One is sent in the 32nd
+ * through 39th second of the minute.
+ *
+ * 6dddhhmmss6dddhhmmss
+ *
+ * where ddd is the day of the year, hh is the hour (in UTC), mm is
+ * the minute and ss the second. The 6 is a constant. Note that
+ * the code is sent twice.
+ *
+ * The second sort of timecode is sent only during the 31st second
+ * past the minute.
+ *
+ * xdyyyyttabXDYYYYTTAB
+ *
+ * In this case, the second part of the code is the one's complement
+ * of the code. This differentiates it from the other timecode
+ * format.
+ *
+ * d is the absolute value of DUT (in tenths of a second). yyyy
+ * is the year. tt is the difference between UTC and TAI. a is
+ * a canadian daylight time flag and b is a serial number.
+ * x is a bitwise field. The least significant bit of x is
+ * one if DUT is negative. The 2nd bit is set if a leap second
+ * will be added at the next opportunity. The 3rd bit is set if
+ * a leap second will be deleted at the next opportunity.
+ * The 4th bit is an even parity bit for the other three bits
+ * in this nibble.
+ *
+ * The start bit in each character has a precise relationship to
+ * the on-time second. Most often UART's synchronize themselves to the
+ * start bit and will post an interrupt at the center of the first stop
+ * bit. Thus each character's interrupt should occur at a fixed offset
+ * from the on-time second. This means that a timestamp taken at the
+ * arrival of each character in the code will provide an independent
+ * estimate of the offset. Since there are 10 characters in the time
+ * code and the code is sent 9 times per minute, this means you
+ * potentially get 90 offset samples per minute. Much of the code in
+ * here is dedicated to producing a single offset estimate from these
+ * samples.
+ *
+ * A note about the line discipline. It is possible to receive the
+ * CHU time code in raw mode, but this has disadvantages. In particular,
+ * this puts a lot of code between the interrupt and the time you freeze
+ * a time stamp, decreasing precision. It is also expensive in terms of
+ * context switches, and made even more expensive by the way I do I/O.
+ * Worse, since you are listening directly to the output of your radio,
+ * CHU is noisy and will make you spend a lot of time receiving noise.
+ *
+ * The line discipline fixes a lot of this. It knows that the CHU time
+ * code consists of 10 bytes which arrive with an intercharacter
+ * spacing of about 37 ms, and that the data is BCD, and filters on this
+ * basis. It delivers block of ten characters plus their associated time
+ * stamps all at once. The time stamps are hence about as accurate as
+ * a Unix machine can get them, and much of the noise disappears in the
+ * kernel with no context switching cost.
+ *
+ * The kernel module also will insure that the packets that are
+ * delivered have the correct redundancy bytes, and will return
+ * a flag in chutype to differentiate one sort of packet from
+ * the other.
+ */
+
+/*
+ * CHU definitions
+ */
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-9) /* what the heck */
+#define REFID "CHU\0" /* reference ID */
+#define DESCRIPTION "Scratchbuilt CHU Receiver" /* WRU */
+
+#define NCHUCODES 8 /* expect 8 CHU codes per minute */
+#ifndef CHULDISC
+#define CHULDISC 10 /* XXX temp CHU line discipline */
+#endif
+
+/*
+ * To compute a quality for the estimate (a pseudo dispersion) we add a
+ * fixed 10 ms for each missing code in the minute and add to this
+ * the sum of the differences between the remaining offsets and the
+ * estimated sample offset.
+ */
+#define CHUDELAYPENALTY 0x0000028f
+
+/*
+ * Default fudge factors
+ */
+#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
+#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
+ * as an l_fp fraction, NZPOBITS is the number of significant bits
+ * in ZEROPTONE.
+ */
+#define ZEROPTONE 0x1999999a
+#define NZPOBITS 29
+
+static char hexstring[]="0123456789abcdef";
+
+/*
+ * Unit control structure.
+ */
+struct chuunit {
+ struct peer *peer; /* peer structure pointer */
+ struct event chutimer; /* timeout timer structure */
+ l_fp offsets[NCHUCODES]; /* offsets computed from each code */
+ l_fp rectimes[NCHUCODES]; /* times we received this stuff */
+ u_long reftimes[NCHUCODES]; /* time of last code received */
+ u_char lastcode[NCHUCHARS * 4]; /* last code we received */
+ u_char expect; /* the next offset expected */
+ u_short haveoffset; /* flag word indicating valid offsets */
+ u_short flags; /* operational flags */
+ u_long responses; /* number of responses */
+ int pollcnt; /* poll message counter */
+};
+
+#define CHUTIMERSET 0x1 /* timer is set to fire */
+
+
+/*
+ * The CHU table. This gives the expected time of arrival of each
+ * character after the on-time second and is computed as follows:
+ * The CHU time code is sent at 300 bps. Your average UART will
+ * synchronize at the edge of the start bit and will consider the
+ * character complete at the middle of the first stop bit, i.e.
+ * 0.031667 ms later (some UARTS may complete the character at the
+ * end of the stop bit instead of the middle, but you can fudge this).
+ * Thus the expected time of each interrupt is the start bit time plus
+ * 0.031667 seconds. These times are in chutable[].
+ */
+#define CHARDELAY 0x081b4e82
+
+static u_long chutable[NCHUCHARS] = {
+ 0x22222222 + CHARDELAY, /* 0.1333333333 */
+ 0x2b851eb8 + CHARDELAY, /* 0.170 (exactly) */
+ 0x34e81b4e + CHARDELAY, /* 0.2066666667 */
+ 0x3f92c5f9 + CHARDELAY, /* 0.2483333333 */
+ 0x47ae147b + CHARDELAY, /* 0.280 (exactly) */
+ 0x51111111 + CHARDELAY, /* 0.3166666667 */
+ 0x5a740da7 + CHARDELAY, /* 0.3533333333 */
+ 0x63d70a3d + CHARDELAY, /* 0.390 (exactly) */
+ 0x6d3a06d4 + CHARDELAY, /* 0.4266666667 */
+ 0x769d0370 + CHARDELAY, /* 0.4633333333 */
+};
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Function prototypes
+ */
+static int chu_start P((int, struct peer *));
+static void chu_shutdown P((int, struct peer *));
+static void chu_receive P((struct recvbuf *));
+static void chu_process P((struct chuunit *));
+static void chu_poll P((int, struct peer *));
+static void chu_timeout P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chu = {
+ chu_start, /* start up driver */
+ chu_shutdown, /* shut down driver */
+ chu_poll, /* transmit poll message */
+ noentry, /* not used (old chu_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old chu_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chu_start - open the CHU device and initialize data for processing
+ */
+static int
+chu_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port and set CHU line discipline
+ */
+ (void) sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CHU)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chuunit *)
+ emalloc(sizeof(struct chuunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chuunit));
+ up->chutimer.peer = (struct peer *)up;
+ up->chutimer.event_handler = chu_timeout;
+ up->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = chu_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * chu_shutdown - shut down the clock
+ */
+static void
+chu_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * chu_receive - receive data from a CHU clock, do format checks and compute
+ * an estimate from the sample data
+ */
+static void
+chu_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int i;
+ u_long date_ui;
+ u_long tmp;
+ u_char *code;
+ struct chucode *chuc;
+ int isneg;
+ u_long reftime;
+ l_fp off[NCHUCHARS];
+ int day, hour, minute, second;
+
+ /*
+ * Do a length check on the data. Should be what we asked for.
+ */
+ if (rbufp->recv_length != sizeof(struct chucode)) {
+ syslog(LOG_ERR,
+ "chu_receive: received %d bytes, expected %d",
+ rbufp->recv_length, sizeof(struct chucode));
+ return;
+ }
+
+ /*
+ * Get the clock this applies to and a pointer to the data
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ chuc = (struct chucode *)&rbufp->recv_space;
+ up->responses++;
+
+ /*
+ * Just for fun, we can debug the whole frame if
+ * we want.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ pp->lastcode[2 * i] = hexstring[chuc->codechars[i] &
+ 0xf];
+ pp->lastcode[2 * i + 1] = hexstring[chuc->codechars[i]
+ >> 4];
+ }
+ pp->lencode = 2 * i;
+ pp->lastcode[pp->lencode] = '\0';
+#ifdef DEBUG
+ if (debug > 3) {
+ printf("chu: %s packet\n", (chuc->chutype == CHU_YEAR)?
+ "year":"time");
+ for (i = 0; i < NCHUCHARS; i++) {
+ char c[64];
+
+ sprintf(c,"%c%c %s",
+ hexstring[chuc->codechars[i] & 0xf],
+ hexstring[chuc->codechars[i] >> 4],
+ ctime(&(chuc->codetimes[i].tv_sec)));
+ c[strlen(c) - 1] = 0; /* ctime() adds \n */
+ printf("chu: %s .%06d\n", c,
+ chuc->codetimes[i].tv_usec);
+ }
+ }
+#endif
+
+ /*
+ * At this point we're assured that both halves of the
+ * data match because of what the kernel has done.
+ * But there's more than one data format. We need to
+ * check chutype to see what to do now. If it's a
+ * year packet, then we fiddle with it specially.
+ */
+
+ if (chuc->chutype == CHU_YEAR)
+ {
+ u_char leapbits,parity;
+
+ /*
+ * Break out the code into the BCD nibbles.
+ * Put it in the half of lastcode.
+ */
+ code = up->lastcode;
+ code += 2*NCHUCHARS;
+ for (i = 0; i < NCHUCHARS; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ leapbits = chuc->codechars[0]&0xf;
+
+ /*
+ * Now make sure that the leap nibble
+ * is even parity.
+ */
+
+ parity = (leapbits ^ (leapbits >> 2))&0x3;
+ parity = (parity ^ (parity>>1))&0x1;
+ if (parity)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * This just happens to work. :-)
+ */
+
+ pp->leap = (leapbits >> 1) & 0x3;
+
+ return;
+ }
+
+ if (chuc->chutype != CHU_TIME)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Break out the code into the BCD nibbles. Only need to fiddle
+ * with the first half since both are identical. Note the first
+ * BCD character is the low order nibble, the second the high order.
+ */
+ code = up->lastcode;
+ for (i = 0; i < NCHUCHARS; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ /*
+ * Format check. Make sure the two halves match.
+ * There's really no need for this, but it can't hurt.
+ */
+ for (i = 0; i < NCHUCHARS/2; i++)
+ if (chuc->codechars[i] !=
+ chuc->codechars[i+(NCHUCHARS/2)]) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * If the first nibble isn't a 6, we're up the creek
+ */
+ code = up->lastcode;
+ if (*code++ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Collect the day, the hour, the minute and the second.
+ */
+ day = *code++;
+ day = MULBY10(day) + *code++;
+ day = MULBY10(day) + *code++;
+ hour = *code++;
+ hour = MULBY10(hour) + *code++;
+ minute = *code++;
+ minute = MULBY10(minute) + *code++;
+ second = *code++;
+ second = MULBY10(second) + *code++;
+
+ /*
+ * Sanity check the day and time. Note that this
+ * only occurs on the 32st through the 39th second
+ * of the minute.
+ */
+ if (day < 1 || day > 366
+ || hour > 23 || minute > 59
+ || second < 32 || second > 39) {
+ pp->baddata++;
+ if (day < 1 || day > 366) {
+ refclock_report(peer, CEVNT_BADDATE);
+ } else {
+ refclock_report(peer, CEVNT_BADTIME);
+ }
+ return;
+ }
+
+ /*
+ * Compute the NTP date from the input data and the
+ * receive timestamp. If this doesn't work, mark the
+ * date as bad and forget it.
+ */
+ if (!clocktime(day, hour, minute, second, 0,
+ rbufp->recv_time.l_ui, &pp->yearstart, (U_LONG *)&reftime)) {
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+ date_ui = reftime;;
+
+ /*
+ * We've now got the integral seconds part of the time code (we hope).
+ * The fractional part comes from the table. We next compute
+ * the offsets for each character.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ register u_long tmp2;
+
+ off[i].l_ui = date_ui;
+ off[i].l_uf = chutable[i];
+ tmp = chuc->codetimes[i].tv_sec + JAN_1970;
+ TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
+ M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
+ }
+
+ if (!pp->sloppyclockflag) {
+ u_short ord[NCHUCHARS];
+ /*
+ * In here we assume the clock has adequate bits
+ * to take timestamps with reasonable accuracy.
+ * Note that the time stamps may contain errors
+ * for a couple of reasons. Timing is actually
+ * referenced to the start bit in each character
+ * in the time code. If this is obscured by static
+ * you can still get a valid character but have the
+ * timestamp offset by +-1.5 ms. Also, we may suffer
+ * from interrupt delays if the interrupt is being
+ * held off when the character arrives. Note the
+ * latter error is always in the form of a delay.
+ *
+ * After fiddling I arrived at the following scheme.
+ * We sort the times into order by offset. We then
+ * drop the most positive 2 offset values (which may
+ * correspond to a character arriving early due to
+ * static) and the most negative 4 (which may correspond
+ * to delayed characters, either from static or from
+ * interrupt latency). We then take the mean of the
+ * remaining 4 offsets as our estimate.
+ */
+
+ /*
+ * Set up the order array.
+ */
+ for (i = 0; i < NCHUCHARS; i++)
+ ord[i] = (u_short)i;
+
+ /*
+ * Sort them into order. Reuse variables with abandon.
+ */
+ for (tmp = 0; tmp < (NCHUCHARS-1); tmp++) {
+ for (i = (int)tmp+1; i < NCHUCHARS; i++) {
+ if (!L_ISGEQ(&off[ord[i]], &off[ord[tmp]])) {
+ date_ui = (u_long)ord[i];
+ ord[i] = ord[tmp];
+ ord[tmp] = (u_short)date_ui;
+ }
+ }
+ }
+
+ /*
+ * Done the sort. We drop 0, 1, 2 and 3 at the negative
+ * end, and 8 and 9 at the positive. Take the sum of
+ * 4, 5, 6 and 7.
+ */
+ date_ui = off[ord[4]].l_ui;
+ tmp = off[ord[4]].l_uf;
+ for (i = 5; i <= 7; i++)
+ M_ADD(date_ui, tmp, off[ord[i]].l_ui, off[ord[i]].l_uf);
+
+ /*
+ * Round properly, then right shift two bits for the
+ * divide by four.
+ */
+ if (tmp & 0x2)
+ M_ADDUF(date_ui, tmp, 0x4);
+ M_RSHIFT(date_ui, tmp);
+ M_RSHIFT(date_ui, tmp);
+ } else {
+ /*
+ * Here is a *big* problem. On a machine where the
+ * low order bit in the clock is on the order of half
+ * a millisecond or more we don't really have enough
+ * precision to make intelligent choices about which
+ * samples might be in error and which aren't. More
+ * than this, in the case of error free data we can
+ * pick up a few bits of precision by taking the mean
+ * of the whole bunch. This is what we do. The problem
+ * comes when it comes time to divide the 64 bit sum of
+ * the 10 samples by 10, a procedure which really sucks.
+ * Oh, well, grin and bear it. Compute the sum first.
+ */
+ date_ui = 0;
+ tmp = 0;
+ for (i = 0; i < NCHUCHARS; i++)
+ M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
+ if (M_ISNEG(date_ui, tmp))
+ isneg = 1;
+ else
+ isneg = 0;
+
+ /*
+ * Here is a multiply-by-0.1 optimization that should apply
+ * just about everywhere. If the magnitude of the sum
+ * is less than 9 we don't have to worry about overflow
+ * out of a 64 bit product, even after rounding.
+ */
+ if (date_ui < 9 || date_ui > 0xfffffff7) {
+ register u_long prod_ui;
+ register u_long prod_uf;
+
+ prod_ui = prod_uf = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT(date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD(prod_ui, prod_uf, date_ui, tmp);
+ }
+
+ /*
+ * Done, round it correctly. Prod_ui contains the
+ * fraction.
+ */
+ if (prod_uf & 0x80000000)
+ prod_ui++;
+ if (isneg)
+ date_ui = 0xffffffff;
+ else
+ date_ui = 0;
+ tmp = prod_ui;
+ /*
+ * date_ui is integral part, tmp is fraction.
+ */
+ } else {
+ register u_long prod_ovr;
+ register u_long prod_ui;
+ register u_long prod_uf;
+ register u_long highbits;
+
+ prod_ovr = prod_ui = prod_uf = 0;
+ if (isneg)
+ highbits = 0xffffffff; /* sign extend */
+ else
+ highbits = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT3(highbits, date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD3(prod_ovr, prod_uf, prod_ui,
+ highbits, date_ui, tmp);
+ }
+
+ if (prod_uf & 0x80000000)
+ M_ADDUF(prod_ovr, prod_ui, (u_long)1);
+ date_ui = prod_ovr;
+ tmp = prod_ui;
+ }
+ }
+
+ /*
+ * At this point we have the mean offset, with the integral
+ * part in date_ui and the fractional part in tmp. Store
+ * it in the structure.
+ */
+ i = second - 32; /* gives a value 0 through 8 */
+ if (i < (int)up->expect) {
+ /*
+ * This shouldn't actually happen, but might if a single
+ * bit error occurred in the code which fooled us.
+ * Throw away all previous data.
+ */
+ up->expect = 0;
+ up->haveoffset = 0;
+ if (up->flags & CHUTIMERSET) {
+ TIMER_DEQUEUE(&up->chutimer);
+ up->flags &= ~CHUTIMERSET;
+ }
+ }
+
+ up->offsets[i].l_ui = date_ui;
+ up->offsets[i].l_uf = tmp;
+ up->rectimes[i] = rbufp->recv_time;
+ up->reftimes[i] = reftime;
+
+ up->expect = i + 1;
+ up->haveoffset |= (1 << i);
+
+ if (up->expect >= NCHUCODES) {
+ /*
+ * Got a full second's worth. Dequeue timer and
+ * process this.
+ */
+ if (up->flags & CHUTIMERSET) {
+ TIMER_DEQUEUE(&up->chutimer);
+ up->flags &= ~CHUTIMERSET;
+ }
+ chu_process(up);
+ } else if (!(up->flags & CHUTIMERSET)) {
+ /*
+ * Try to take an interrupt sometime after the
+ * 42 second mark (leaves an extra 2 seconds for
+ * slop). Round it up to an even multiple of
+ * 4 seconds.
+ */
+ up->chutimer.event_time =
+ current_time + (u_long)(10 - i) + (1<<EVENT_TIMEOUT);
+ up->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1);
+ TIMER_INSERT(timerqueue, &up->chutimer);
+ up->flags |= CHUTIMERSET;
+ }
+}
+
+
+/*
+ * chu_timeout - process a timeout event
+ */
+static void
+chu_timeout(fakepeer)
+ struct peer *fakepeer;
+{
+ /*
+ * If we got here it means we received some time codes
+ * but didn't get the one which should have arrived on
+ * the 39th second. Process what we have.
+ */
+ ((struct chuunit *)fakepeer)->flags &= ~CHUTIMERSET;
+ chu_process((struct chuunit *)fakepeer);
+}
+
+
+/*
+ * chu_process - process the raw offset estimates we have and pass
+ * the results on to the NTP clock filters.
+ */
+static void
+chu_process(up)
+ register struct chuunit *up;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ int i;
+ s_fp bestoff;
+ s_fp tmpoff;
+ u_fp dispersion;
+ int imax;
+
+ /*
+ * The most positive offset.
+ */
+ peer = up->peer;
+ pp = peer->procptr;
+ imax = NCHUCODES;
+ for (i = 0; i < NCHUCODES; i++)
+ if (up->haveoffset & (1<<i))
+ if (i < imax || L_ISGEQ(&up->offsets[i],
+ &up->offsets[imax]))
+ imax = i;
+
+ /*
+ * The most positive estimate is our best bet. Go through
+ * the list again computing the dispersion.
+ */
+ bestoff = LFPTOFP(&up->offsets[imax]);
+ dispersion = 0;
+ for (i = 0; i < NCHUCODES; i++) {
+ if (up->haveoffset & (1<<i)) {
+ tmpoff = LFPTOFP(&up->offsets[i]);
+ dispersion += (bestoff - tmpoff);
+ } else {
+ dispersion += CHUDELAYPENALTY;
+ }
+ }
+
+ pp->lasttime = current_time;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &up->offsets[imax], 0,
+ dispersion, &up->rectimes[imax], &up->rectimes[imax],
+ pp->leap);
+
+ /*
+ * Zero out unit for next code series
+ */
+ up->haveoffset = 0;
+ up->expect = 0;
+ refclock_report(peer, CEVNT_NOMINAL);
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_conf.c b/usr.sbin/xntpd/xntpd/refclock_conf.c
new file mode 100644
index 0000000..c0674d9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_conf.c
@@ -0,0 +1,185 @@
+/*
+ * refclock_conf.c - reference clock configuration
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+static struct refclock refclock_none = {
+ noentry, noentry, noentry, noentry, noentry, noentry, NOFLAGS
+};
+
+#ifdef LOCAL_CLOCK
+extern struct refclock refclock_local;
+#else
+#define refclock_local refclock_none
+#endif
+
+#if defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS)
+extern struct refclock refclock_trak;
+#else
+#define refclock_trak refclock_none
+#endif
+
+#if defined(PST)
+extern struct refclock refclock_pst;
+#else
+#define refclock_pst refclock_none
+#endif
+
+#if defined(CHU)
+extern struct refclock refclock_chu;
+#else
+#define refclock_chu refclock_none
+#endif
+
+#if defined(GOES) || defined(GOESCLK) || defined(GOESPPS)
+extern struct refclock refclock_goes;
+#else
+#define refclock_goes refclock_none
+#endif
+
+#if defined(WWVB)
+extern struct refclock refclock_wwvb;
+#else
+#define refclock_wwvb refclock_none
+#endif
+
+#if defined(PARSE) || defined(PARSEPPS)
+extern struct refclock refclock_parse;
+#else
+#define refclock_parse refclock_none
+#endif
+
+#if defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS))
+extern struct refclock refclock_mx4200;
+#else
+#define refclock_mx4200 refclock_none
+#endif
+
+#if defined(AS2201)
+extern struct refclock refclock_as2201;
+#else
+#define refclock_as2201 refclock_none
+#endif
+
+#if defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS)
+extern struct refclock refclock_omega;
+#else
+#define refclock_omega refclock_none
+#endif
+
+#if defined(TPRO) && defined(sun) /* XXX sun only */
+extern struct refclock refclock_tpro;
+#else
+#define refclock_tpro refclock_none
+#endif
+
+#if defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS)
+extern struct refclock refclock_leitch;
+#else
+#define refclock_leitch refclock_none
+#endif
+
+#if defined(IRIG) && defined(sun) /* XXX sun only */
+extern struct refclock refclock_irig;
+#else
+#define refclock_irig refclock_none
+#endif
+
+#if defined(MSFEESPPS)
+extern struct refclock refclock_msfees;
+#else
+#define refclock_msfees refclock_none
+#endif
+
+#if defined(GPSTM) || defined(GPSTMCLK) || defined(GPSTMPPS)
+extern struct refclock refclock_gpstm;
+#else
+#define refclock_gpstm refclock_none
+#endif
+
+#if defined(BANC) || defined(BANCCLK) || defined(BANCPPS)
+extern struct refclock refclock_bancomm;
+#else
+#define refclock_bancomm refclock_none
+#endif
+
+#ifdef DATUM
+extern struct refclock refclock_datum;
+#else
+#define refclock_datum refclock_none
+#endif
+
+#ifdef ACTS
+extern struct refclock refclock_acts;
+#else
+#define refclock_acts refclock_none
+#endif
+
+#ifdef HEATH
+extern struct refclock refclock_heath;
+#else
+#define refclock_heath refclock_none
+#endif
+
+#ifdef NMEA
+extern struct refclock refclock_nmea;
+#else
+#define refclock_nmea refclock_none
+#endif
+
+#ifdef MOTO
+extern struct refclock refclock_moto;
+#else
+#define refclock_moto refclock_none
+#endif
+
+#ifdef ATOM
+extern struct refclock refclock_atom;
+#else
+#define refclock_atom refclock_none
+#endif
+
+/*
+ * Order is clock_start(), clock_shutdown(), clock_poll(),
+ * clock_control(), clock_init(), clock_buginfo, clock_flags;
+ *
+ * Types are defined in ntp.h. The index must match this.
+ */
+struct refclock *refclock_conf[] = {
+ &refclock_none, /* 0 REFCLK_NONE */
+ &refclock_local, /* 1 REFCLK_LOCAL */
+ &refclock_trak, /* 2 REFCLK_GPS_TRAK */
+ &refclock_pst, /* 3 REFCLK_WWV_PST */
+ &refclock_wwvb, /* 4 REFCLK_WWVB_SPECTRACOM */
+ &refclock_goes, /* 5 REFCLK_GOES_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU */
+ &refclock_parse, /* 8 REFCLK_PARSE */
+ &refclock_mx4200, /* 9 REFCLK_GPS_MX4200 */
+ &refclock_as2201, /* 10 REFCLK_GPS_AS2201 */
+ &refclock_omega, /* 11 REFCLK_OMEGA_TRUETIME */
+ &refclock_tpro, /* 12 REFCLK_IRIG_TPRO */
+ &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */
+ &refclock_msfees, /* 14 REFCLK_MSF_EES */
+ &refclock_gpstm, /* 15 REFCLK_GPSTM_TRUETIME */
+ &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */
+ &refclock_datum, /* 17 REFCLK_GPS_DATUM */
+ &refclock_acts, /* 18 REFCLK_NIST_ACTS */
+ &refclock_heath, /* 19 REFCLK_WWV_HEATH */
+ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */
+ &refclock_moto, /* 21 REFCLK_GPS_MOTO */
+ &refclock_atom, /* 22 REFCLK_ATOM_PPS */
+ &refclock_none, /* 23 reserved */
+ &refclock_none, /* 24 reserved */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_datum.c b/usr.sbin/xntpd/xntpd/refclock_datum.c
new file mode 100644
index 0000000..00449c0
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_datum.c
@@ -0,0 +1,871 @@
+/*
+** refclock_datum - clock driver for the Datum Programmable Time Server
+**
+** Important note: This driver assumes that you have termios. If you have
+** a system that does not have termios, you will have to modify this driver.
+**
+** Sorry, I have only tested this driver on SUN and HP platforms.
+*/
+
+#if defined(REFCLOCK) && (defined(DATUM) || defined(DATUMCLK) || defined(DATUMPPS))
+
+/*
+** Include Files
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(WWVBCLK)
+#include <sys/clkdefs.h>
+#endif /* WWVBCLK */
+#endif /* STREAM */
+
+#if defined (WWVBPPS)
+#include <sys/ppsclock.h>
+#endif /* WWVBPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+** This driver supports the Datum Programmable Time System (PTS) clock.
+** The clock works in very straight forward manner. When it receives a
+** time code request (e.g., the ascii string "//k/mn"), it responds with
+** a seven byte BCD time code. This clock only responds with a
+** time code after it first receives the "//k/mn" message. It does not
+** periodically send time codes back at some rate once it is started.
+** the returned time code can be broken down into the following fields.
+**
+** _______________________________
+** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+** ===============================
+** byte 0: | - - - - | H D |
+** ===============================
+** byte 1: | T D | U D |
+** ===============================
+** byte 2: | - - | T H | U H |
+** ===============================
+** byte 3: | - | T M | U M |
+** ===============================
+** byte 4: | - | T S | U S |
+** ===============================
+** byte 5: | t S | h S |
+** ===============================
+** byte 6: | m S | - - - - |
+** ===============================
+**
+** In the table above:
+**
+** "-" means don't care
+** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days
+** "T H", and "UH" means Tens and Units of Hours
+** "T M", and "U M" means Tens and Units of Minutes
+** "T S", and "U S" means Tens and Units of Seconds
+** "t S", "h S", and "m S" means tenths, hundredths, and thousandths
+** of seconds
+**
+** The Datum PTS communicates throught the RS232 port on your machine.
+** Right now, it assumes that you have termios. This driver has been tested
+** on SUN and HP workstations. The Datum PTS supports various IRIG and
+** NASA input codes. This driver assumes that the name of the device is
+** /dev/datum. You will need to make a soft link to your RS232 device or
+** create a new driver to use this refclock.
+*/
+
+/*
+** Datum PTS defines
+*/
+
+/*
+** Note that if GMT is defined, then the Datum PTS must use Greenwich
+** time. Otherwise, this driver allows the Datum PTS to use the current
+** wall clock for its time. It determines the time zone offset by minimizing
+** the error after trying several time zone offsets. If the Datum PTS
+** time is Greenwich time and GMT is not defined, everything should still
+** work since the time zone will be found to be 0. What this really means
+** is that your system time (at least to start with) must be within the
+** correct time by less than +- 30 minutes. The default is for GMT to not
+** defined. If you really want to force GMT without the funny +- 30 minute
+** stuff then you must define (uncomment) GMT below.
+*/
+
+/*
+#define GMT
+#define DEBUG_DATUM_PTC
+#define LOG_TIME_ERRORS
+*/
+
+
+#define PTSPRECISION (-10) /* precision assumed 1/1024 ms */
+#define DATMREFID "DATM" /* reference id */
+#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */
+#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */
+
+#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR)
+
+/*
+** External Variables
+*/
+
+extern u_long current_time; /* current time (s) - not really used */
+extern int debug; /* global debug flag - not relly used */
+
+/*
+** The Datum PTS structure
+*/
+
+/*
+** I don't use a fixed array of MAXUNITS like everyone else just because
+** I don't like to program that way. Sorry if this bothers anyone. I assume
+** that you can use any id for your unit and I will search for it in a
+** dynamic array of units until I find it. I was worried that users might
+** enter a bad id in their configuration file (larger than MAXUNITS) and
+** besides, it is just cleaner not to have to assume that you have a fixed
+** number of anything in a program.
+*/
+
+struct datum_pts_unit {
+ struct peer *peer; /* peer used by xntp */
+ struct refclockio io; /* io structure used by xntp */
+ int PTS_fd; /* file descriptor for PTS */
+ u_int unit; /* id for unit */
+ u_long timestarted; /* time started */
+ l_fp lastrec; /* time tag for the receive time (system) */
+ l_fp lastref; /* reference time (Datum time) */
+ u_long yearstart; /* the year that this clock started */
+ int coderecv; /* number of time codes received */
+ int day; /* day */
+ int hour; /* hour */
+ int minute; /* minutes */
+ int second; /* seconds */
+ int msec; /* miliseconds */
+ int usec; /* miliseconds */
+ u_char leap; /* funny leap character code */
+ char retbuf[8]; /* returned time from the datum pts */
+ char nbytes; /* number of bytes received from datum pts */
+ double sigma2; /* average squared error (roughly) */
+ int tzoff; /* time zone offest from GMT */
+};
+
+/*
+** PTS static constant variables for internal use
+*/
+
+static char TIME_REQUEST[6]; /* request message sent to datum for time */
+static FILE *logfile; /* log file for logging information */
+static int nunits; /* number of active units */
+static struct datum_pts_unit
+ **datum_pts_unit; /* dynamic array of datum PTS structures */
+
+/*
+** Callback function prototypes that xntpd needs to know about.
+*/
+
+static int datum_pts_start P((int, struct peer *));
+static void datum_pts_shutdown P((int, struct peer *));
+static void datum_pts_poll P((int, struct peer *));
+static void datum_pts_control P((int, struct refclockstat *,
+ struct refclockstat *));
+static void datum_pts_init P((void));
+static void datum_pts_buginfo P((int, struct refclockbug *));
+
+/*
+** This is the call back function structure that xntpd actually uses for
+** this refclock.
+*/
+
+struct refclock refclock_datum = {
+ datum_pts_start, /* start up a new Datum refclock */
+ datum_pts_shutdown, /* shutdown a Datum refclock */
+ datum_pts_poll, /* sends out the time request */
+ datum_pts_control, /* not used */
+ datum_pts_init, /* initialization (called first) */
+ datum_pts_buginfo, /* not used */
+ NOFLAGS /* we are not setting any special flags */
+};
+
+/*
+** The datum_pts_receive callback function is handled differently from the
+** rest. It is passed to the xntpd io data structure. Basically, every
+** 64 seconds, the datum_pts_poll() routine is called. It sends out the time
+** request message to the Datum Programmable Time System. Then, xntpd
+** waits on a select() call to receive data back. The datum_pts_receive()
+** function is called as data comes back. We expect a seven byte time
+** code to be returned but the datum_pts_receive() function may only get
+** a few bytes passed to it at a time. In other words, this routine may
+** get called by the io stuff in xntpd a few times before we get all seven
+** bytes. Once the last byte is received, we process it and then pass the
+** new time measurement to xntpd for updating the system time. For now,
+** there is no 3 state filtering done on the time measurements. The
+** jitter may be a little high but at least for its current use, it is not
+** a problem. We have tried to keep things as simple as possible. This
+** clock should not jitter more than 1 or 2 mseconds at the most once
+** things settle down. It is important to get the right drift calibrated
+** in the xntpd.drift file as well as getting the right tick set up right
+** using tickadj for SUNs. Tickadj is not used for the HP but you need to
+** remember to bring up the adjtime daemon because HP does not support
+** the adjtime() call.
+*/
+
+static void datum_pts_receive P((struct recvbuf *));
+
+/*......................................................................*/
+/* datum_pts_start - start up the datum PTS. This means open the */
+/* RS232 device and set up the data structure for my unit. */
+/*......................................................................*/
+
+static int datum_pts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct datum_pts_unit **temp_datum_pts_unit;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile, "Starting Datum PTS unit %d\n", unit);
+ fflush(logfile);
+#endif
+
+/*
+** Create the memory for the new unit
+*/
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits+1)*sizeof(struct datum_pts_unit *));
+ if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ nunits*sizeof(struct datum_pts_unit *));
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+ datum_pts_unit[nunits] = (struct datum_pts_unit *)
+ malloc(sizeof(struct datum_pts_unit));
+ datum_pts = datum_pts_unit[nunits];
+
+ datum_pts->unit = unit; /* set my unit id */
+ datum_pts->yearstart = 0; /* initialize the yearstart to 0 */
+ datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */
+
+/*
+** Open the Datum PTS device
+*/
+
+ datum_pts->PTS_fd = open("/dev/datum",O_RDWR);
+
+ fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Opening RS232 port with file descriptor %d\n",
+ datum_pts->PTS_fd);
+ fflush(logfile);
+#endif
+
+/*
+** Set up the RS232 terminal device information. Note that we assume that
+** we have termios. This code has only been tested on SUNs and HPs. If your
+** machine does not have termios then this program exits. You can change this
+** if you want by editing this source. Please give the changes back to the
+** xntp folks so that it can become part of their regular distribution.
+*/
+
+#ifdef HAVE_TERMIOS
+
+ arg.c_iflag = IGNBRK;
+ arg.c_oflag = 0;
+ arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */
+ arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */
+
+ tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg);
+
+#else
+
+ syslog(LOG_ERR, "Datum_PTS: Exiting - Termios not supported in this driver");
+ exit(1);
+
+#endif
+
+/*
+** Initialize the xntpd IO structure
+*/
+
+ datum_pts->peer = peer;
+ datum_pts->timestarted = current_time;
+
+ datum_pts->io.clock_recv = datum_pts_receive;
+ datum_pts->io.srcclock = (caddr_t)datum_pts;
+ datum_pts->io.datalen = 0;
+ datum_pts->io.fd = datum_pts->PTS_fd;
+
+ if (!io_addclock(&(datum_pts->io))) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Problem adding clock\n");
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Problem adding clock");
+
+ }
+
+ peer->precision = PTSPRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = 0;
+ memcpy((char *)&peer->refid, DATMREFID, 4);
+
+/*
+** Now add one to the number of units and return a successful code
+*/
+
+ nunits++;
+ return 1;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_shutdown - this routine shuts doen the device and */
+/* removes the memory for the unit. */
+/*......................................................................*/
+
+static void datum_pts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ int i,j;
+ struct datum_pts_unit **temp_datum_pts_unit;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Shutdown Datum PTS\n");
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS");
+
+/*
+** First we have to find the right unit (i.e., the one with the same id).
+** We do this by looping through the dynamic array of units intil we find
+** it. Note, that I don't simply use an array with a maximimum number of
+** Datum PTS units. Everything is completely dynamic.
+*/
+
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+
+/*
+** We found the unit so close the file descriptor and free up the memory used
+** by the structure.
+*/
+
+ io_closeclock(&datum_pts_unit[i]->io);
+ close(datum_pts_unit[i]->PTS_fd);
+ free(datum_pts_unit[i]);
+
+/*
+** Now clean up the datum_pts_unit dynamic array so that there are no holes.
+** This may mean moving pointers around, etc., to keep things compact.
+*/
+
+ if (nunits > 1) {
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits-1)*sizeof(struct datum_pts_unit *));
+ if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ i*sizeof(struct datum_pts_unit *));
+
+ for (j=i+1; j<nunits; j++) {
+ temp_datum_pts_unit[j-1] = datum_pts_unit[j];
+ }
+
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+
+ }else{
+
+ free(datum_pts_unit);
+ datum_pts_unit = NULL;
+
+ }
+
+ return;
+
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error, could not shut down unit %d\n",unit);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit);
+
+}
+
+/*......................................................................*/
+/* datum_pts_poll - this routine sends out the time request to the */
+/* Datum PTS device. The time will be passed back in the */
+/* datum_pts_receive() routine. */
+/*......................................................................*/
+
+static void datum_pts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ int i;
+ int index;
+ int error_code;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Poll Datum PTS\n");
+ fflush(logfile);
+#endif
+
+/*
+** Find the right unit and send out a time request once it is found.
+*/
+
+ index = -1;
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+ index = i;
+ datum_pts = datum_pts_unit[i];
+ error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6);
+ if (error_code != 6) perror("TIME_REQUEST");
+ datum_pts->nbytes = 0;
+ break;
+ }
+ }
+
+/*
+** Print out an error message if we could not find the right unit.
+*/
+
+ if (index == -1) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error, could not poll unit %d\n",unit);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit);
+ return;
+
+ }
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_control - not used */
+/*......................................................................*/
+
+static void datum_pts_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Control Datum PTS\n");
+ fflush(logfile);
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_init - initializes things for all possible Datum */
+/* time code generators that might be used. In practice, this is */
+/* only called once at the beginning before anything else is */
+/* called. */
+/*......................................................................*/
+
+static void datum_pts_init()
+{
+
+/* */
+/*...... open up the log file if we are debugging ......................*/
+/* */
+
+/*
+** Open up the log file if we are debugging. For now, send data out to the
+** screen (stdout).
+*/
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+/*
+ logfile = fopen("xntpd.log", "w");
+*/
+#endif
+
+ logfile = stdout;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Init Datum PTS\n");
+ fflush(logfile);
+#endif
+
+/*
+** Initialize the time request command string. This is the only message
+** that we ever have to send to the Datum PTS (although others are defined).
+*/
+
+ memcpy(TIME_REQUEST, "//k/mn",6);
+
+/*
+** Initialize the number of units to 0 and set the dynamic array of units to
+** NULL since there are no units defined yet.
+*/
+
+ datum_pts_unit = NULL;
+ nunits = 0;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_buginfo - not used */
+/*......................................................................*/
+
+static void datum_pts_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Buginfo Datum PTS\n");
+ fflush(logfile);
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_receive - receive the time buffer that was read in */
+/* by the xntpd io handling routines. When 7 bytes have been */
+/* received (it may take several tries before all 7 bytes are */
+/* received), then the time code must be unpacked and sent to */
+/* the xntpd clock_receive() routine which causes the systems */
+/* clock to be updated (several layers down). */
+/*......................................................................*/
+
+static void datum_pts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ int i;
+ l_fp tstmp;
+ struct datum_pts_unit *datum_pts;
+ char *dpt;
+ int dpend;
+ int tzoff;
+ int timerr;
+ double ftimerr, abserr;
+ u_fp dispersion;
+ int goodtime;
+
+/*
+** Get the time code (maybe partial) message out of the rbufp buffer.
+*/
+
+ datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock;
+ dpt = (char *)&rbufp->recv_space;
+ dpend = rbufp->recv_length;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Receive Datum PTS: %d bytes\n", dpend);
+ fflush(logfile);
+#endif
+
+/* */
+/*...... save the ntp system time when the first byte is received ......*/
+/* */
+
+/*
+** Save the ntp system time when the first byte is received. Note that
+** because it may take several calls to this routine before all seven
+** bytes of our return message are finally received by the io handlers in
+** xntpd, we really do want to use the time tag when the first byte is
+** received to reduce the jitter.
+*/
+
+ if (datum_pts->nbytes == 0) {
+ datum_pts->lastrec = rbufp->recv_time;
+ }
+
+/*
+** Increment our count to the number of bytes received so far. Return if we
+** haven't gotten all seven bytes yet.
+*/
+
+ for (i=0; i<dpend; i++) {
+ datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i];
+ }
+
+ datum_pts->nbytes += dpend;
+
+ if (datum_pts->nbytes != 7) {
+ return;
+ }
+
+/*
+** Convert the seven bytes received in our time buffer to day, hour, minute,
+** second, and msecond values. The usec value is not used for anything
+** currently. It is just the fractional part of the time stored in units
+** of microseconds.
+*/
+
+ datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) +
+ 10*((datum_pts->retbuf[1] & 0xf0)>>4) +
+ (datum_pts->retbuf[1] & 0x0f);
+
+ datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) +
+ (datum_pts->retbuf[2] & 0x0f);
+
+ datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) +
+ (datum_pts->retbuf[3] & 0x0f);
+
+ datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) +
+ (datum_pts->retbuf[4] & 0x0f);
+
+ datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) +
+ 10*(datum_pts->retbuf[5] & 0x0f) +
+ ((datum_pts->retbuf[6] & 0xf0)>>4);
+
+ datum_pts->usec = 1000*datum_pts->msec;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"day %d, hour %d, minute %d, second %d, msec %d\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec);
+ fflush(logfile);
+#endif
+
+/*
+** Get the GMT time zone offset. Note that GMT should be zero if the Datum
+** reference time is using GMT as its time base. Otherwise we have to
+** determine the offset if the Datum PTS is using time of day as its time
+** base.
+*/
+
+ goodtime = 0; /* We are not sure about the time and offset yet */
+
+#ifdef GMT
+
+/*
+** This is the case where the Datum PTS is using GMT so there is no time
+** zone offset.
+*/
+
+ tzoff = 0; /* set time zone offset to 0 */
+
+#else
+
+/*
+** This is the case where the Datum PTS is using regular time of day for its
+** time so we must compute the time zone offset. The way we do it is kind of
+** funny but it works. We loop through different time zones (0 to 24) and
+** pick the one that gives the smallest error (+- one half hour). The time
+** zone offset is stored in the datum_pts structure for future use. Normally,
+** the clocktime() routine is only called once (unless the time zone offset
+** changes due to daylight savings) since the goodtime flag is set when a
+** good time is found (with a good offset). Note that even if the Datum
+** PTS is using GMT, this mechanism will still work since it should come up
+** with a value for tzoff = 0 (assuming that your system clock is within
+** a half hour of the Datum time (even with time zone differences).
+*/
+
+ for (tzoff=0; tzoff<24; tzoff++) {
+ if (clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ (tzoff + datum_pts->tzoff) % 24,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+ error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error);
+#endif
+
+ if ((error < 1799) && (error > -1799)) {
+ tzoff = (tzoff + datum_pts->tzoff) % 24;
+ datum_pts->tzoff = tzoff;
+ goodtime = 1;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone found (clocktime method) = %d\n",tzoff);
+#endif
+
+ break;
+ }
+
+ }
+ }
+
+#endif
+
+/*
+** Make sure that we have a good time from the Datum PTS. Clocktime() also
+** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e.,
+** the fraction of a second) stuff later.
+*/
+
+ if (!goodtime) {
+
+ if (!clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error: bad clocktime\n");
+ fprintf(logfile,"GMT %d, lastrec %d, yearstart %d, lastref %d\n",
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ datum_pts->yearstart,
+ datum_pts->lastref.l_ui);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Bad clocktime");
+
+ return;
+
+ }else{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Good clocktime\n");
+ fflush(logfile);
+#endif
+
+ }
+
+ }
+
+/*
+** We have datum_pts->lastref.l_ui set (which is the integer part of the
+** time. Now set the microseconds field.
+*/
+
+ TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf);
+
+/*
+** Compute the time correction as the difference between the reference
+** time (i.e., the Datum time) minus the receive time (system time).
+*/
+
+ tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */
+ L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */
+ datum_pts->coderecv++; /* increment a counter */
+
+ dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */
+
+#ifdef DEBUG_DATUM_PTC
+ ftimerr = dispersion;
+ ftimerr /= (1024.0 * 64.0);
+ fprintf(logfile,"dispersion = %d, %f\n", dispersion, ftimerr);
+ fflush(logfile);
+#endif
+
+/*
+** Pass the new time to xntpd through the refclock_receive function. Note
+** that we are not trying to make any corrections due to the time it takes
+** for the Datum PTS to send the message back. I am (erroneously) assuming
+** that the time for the Datum PTS to send the time back to us is negligable.
+** I suspect that this time delay may be as much as 15 ms or so (but probably
+** less). For our needs at JPL, this kind of error is ok so it is not
+** necessary to use fudge factors in the ntp.conf file. Maybe later we will.
+*/
+
+ refclock_receive( datum_pts->peer,
+ &tstmp,
+ tzoff,
+ dispersion,
+ &datum_pts->lastrec,
+ &datum_pts->lastrec,
+ datum_pts->leap );
+
+/*
+** Compute sigma squared (not used currently). Maybe later, this could be
+** used for the dispersion estimate. The problem is that xntpd does not link
+** in the math library so sqrt() is not available. Anyway, this is useful
+** for debugging. Maybe later I will just use absolute values for the time
+** error to come up with my dispersion estimate. Anyway, for now my dispersion
+** is set to 0.
+*/
+
+ timerr = tstmp.l_ui<<20;
+ timerr |= (tstmp.l_uf>>12) & 0x000fffff;
+ ftimerr = timerr;
+ ftimerr /= 1024*1024;
+ abserr = ftimerr;
+ if (ftimerr < 0.0) abserr = -ftimerr;
+
+ if (datum_pts->sigma2 == 0.0) {
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = abserr*abserr;
+ }else{
+ datum_pts->sigma2 = DATUM_MAX_ERROR2;
+ }
+ }else{
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr;
+ }else{
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2;
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Time error = %f seconds\n", ftimerr);
+ fflush(logfile);
+#endif
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+ fprintf(logfile,
+ "PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec,
+ ftimerr);
+ fflush(logfile);
+#endif
+
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_goes.c b/usr.sbin/xntpd/xntpd/refclock_goes.c
new file mode 100644
index 0000000..512131b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_goes.c
@@ -0,0 +1,533 @@
+/*
+ * refclock_goes - clock driver for the Kinemetrics Truetime GOES
+ * Receiver Version 3.0C - tested plain, with CLKLDISC
+ * Developement work being done:
+ * - Properly handle varying satellite positions (more acurately)
+ * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
+ */
+
+#if defined(REFCLOCK) && defined(GOES)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime 468-DC GOES Receiver
+ * OM-DC OMEGA and GPS-TM/TMD support in progress...
+ *
+ * Most of this code is originally from refclock_wwvb.c with thanks.
+ * It has been so mangled that wwvb is not a recognizable ancestor.
+ *
+ * Timcode format: ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * C - Carriage return
+ * L - Line feed
+ *
+ * Quality codes indicate possible error of
+ * 468-DC GOES Receiver:
+ * GPS-TM/TMD Receiver:
+ * ? +/- 500 milliseconds # +/- 50 milliseconds
+ * * +/- 5 milliseconds . +/- 1 millisecond
+ * space less than 1 millisecond
+ * OM-DC OMEGA Receiver:
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ *
+ * The carriage return start bit begins on 0 seconds and extends to 1
+ * bit time
+ *
+ * Notes on 468-DC and OMEGA receiver:
+ *
+ * Send the clock a 'R' or 'C' and once per second a timestamp will
+ * appear. Send a 'P' to get the satellite position once.
+ *
+ * Notes on the 468-DC receiver:
+ *
+ * Unless you live on 125 degrees west longitude, you can't
+ * set your clock propagation delay settings correctly and still use
+ * automatic mode. The manual says to use a compromise when setting the
+ * switches. This results in significant errors. The solution; use fudge
+ * time1 and time2 to incorporate corrections. If your clock is set for
+ * 50 and it should be 58 for using the west and 46 for using the east,
+ * use the line
+ *
+ * fudge 127.127.5.0 time1 +0.008 time2 -0.004
+ *
+ * This corrects the 4 milliseconds advance and 8 milliseconds retard
+ * needed. The software will ask the clock which satellite it sees.
+ *
+ * Ntp.conf parameters:
+ * time1 - offset applied to samples when reading WEST satellite (default = 0)
+ * time2 - offset applied to samples when reading EAST satellite (default = 0)
+ * val1 - stratum to assign to this clock (default = 0)
+ * val2 - refid assigned to this clock (default = "GOES", see below)
+ * flag1 - will silence the clock side of xntpd, just reading the clock
+ * without trying to write to it. (default = 0)
+ * flag2 - not assigned
+ * flag3 - enable ppsclock streams module
+ * flag4 - not assigned
+ *
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/goes%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GOES" /* reference id */
+#define DESCRIPTION "TrueTime GPS/GOES Receivers" /* WRU */
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Tags which station (satellite) we see
+ */
+#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
+#define GOES_EAST 1 /* until you discover otherwise */
+
+/*
+ * used by the state machine
+ */
+enum goes_event {e_Init, e_F18, e_F50, e_F51, e_TS};
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * unit control structure
+ */
+struct goesunit {
+ int pollcnt; /* poll message counter */
+ u_short station; /* which station we are on */
+ u_short polled; /* Hand in a time sample? */
+ enum {Base, Start, F18, F50, F51, F08}
+ State; /* State machine */
+};
+
+/*
+ * Function prototypes
+ */
+static int goes_start P((int, struct peer *));
+static void goes_shutdown P((int, struct peer *));
+static void goes_receive P((struct recvbuf *));
+static void goes_poll P((int, struct peer *));
+static void goes_send P((struct peer *, char *));
+static void goes_initstate P((struct peer *));
+static void goes_doevent P((struct peer *, enum goes_event));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_goes = {
+ goes_start, /* start up driver */
+ goes_shutdown, /* shut down driver */
+ goes_poll, /* transmit poll message */
+ noentry, /* not used (old goes_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old goes_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * goes_start - open the devices and initialize data for processing
+ */
+static int
+goes_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct goesunit *)
+ emalloc(sizeof(struct goesunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct goesunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = goes_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+/* goes_initstate(peer);*/
+ return (1);
+}
+
+
+/*
+ * goes_shutdown - shut down the clock
+ */
+static void
+goes_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * goes_receive - receive data from the serial interface on a
+ * Kinimetrics clock
+ */
+static void
+goes_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp tmp_l_fp;
+ u_short new_station;
+ char sync, c1, c2;
+ int i;
+ int lat, lon, off; /* GOES Satellite position */
+
+ /*
+ * Get the clock this applies to and pointers to the data
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC
+ */
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps
+ */
+ if (pp->lencode == 0)
+ return;
+
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code decodes a multitude of different
+ * clock messages. Timecodes are processed if needed. All replies
+ * will be run through the state machine to tweak driver options
+ * and program the clock.
+ */
+
+ /*
+ * Timecode: "nnnnn+nnn-nnn"
+ */
+ if (sscanf(pp->lastcode, "%5d%c%3d%c%3d",
+ &lon, &c1, &lat, &c2, &off) == 5 &&
+ (c1 == '+' || c1 == '-') &&
+ (c2 == '+' || c2 == '-')) {
+
+ /*
+ * This is less than perfect. Call the (satellite)
+ * either EAST or WEST and adjust slop accodingly
+ * Perfectionists would recalcuted the exact delay
+ * and adjust accordingly...
+ */
+ if (lon > 7000 && lon < 14000) {
+ if (lon < 10000)
+ new_station = GOES_EAST;
+ else
+ new_station = GOES_WEST;
+#ifdef DEBUG
+ if (debug) {
+ if (new_station == GOES_EAST)
+ printf("goes: station EAST\n");
+ if (new_station == GOES_WEST)
+ printf("goes: station WEST\n");
+ }
+#endif
+ if (new_station != up->station) {
+ tmp_l_fp = pp->fudgetime1;
+ pp->fudgetime1 = pp->fudgetime2;
+ pp->fudgetime2 = tmp_l_fp;
+ up->station = new_station;
+ }
+ }
+ else {
+ refclock_report(peer, CEVNT_BADREPLY);
+#ifdef DEBUG
+ if (debug)
+ printf("goes: station UNKNONW\n");
+#endif
+ }
+ /*
+ * Switch back to on-second time codes and return.
+ */
+ goes_send(peer, "C");
+
+ return;
+ }
+
+ /*
+ * Timecode: "Fnn"
+ */
+ if (sscanf(pp->lastcode, "F%2d", &i) == 1 &&
+ i > 0 && i < 80) {
+ enum goes_event event = 0;
+
+ if (i == 50) event = e_F50;
+ if (i == 51) event = e_F51;
+ if (i == 50 || i == 51) {
+ goes_doevent(peer, event);
+ return;
+ }
+ }
+
+ /*
+ * Timecode:" TRUETIME Mk III"
+ */
+ if (strcmp(pp->lastcode, " TRUETIME Mk III") == 0) {
+ enum goes_event event;
+
+ event = e_F18;
+ goes_doevent(peer, event);
+ return;
+ }
+
+ /*
+ * Timecode: "ddd:hh:mm:ssQ"
+ */
+ if (sscanf(pp->lastcode, "%3d:%2d:%2d:%2d%c",
+ &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &sync) == 5) {
+
+ /*
+ * Adjust the synchronize indicator according to timecode
+ */
+ if (sync !=' ' && sync !='.' && sync !='*')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /* goes_doevent(peer, e_TS); */
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /*
+ * After each poll, check the station (satellite)
+ */
+ goes_send(peer, "P");
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+
+ /*
+ * We have succedded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+
+ return;
+ }
+
+ /*
+ * No match to known timecodes, report failure and return
+ */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+}
+
+
+/*
+ * goes_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+goes_send(peer, cmd)
+ struct peer *peer;
+ char *cmd;
+{
+ struct refclockproc *pp;
+ register int len = strlen(cmd);
+
+ pp = peer->procptr;
+ if (!pp->sloppyclockflag & CLK_FLAG1) {
+#ifdef DEBUG
+ if (debug)
+ printf("goes: Send '%s'\n", cmd);
+#endif
+ if (write(pp->io.fd, cmd, len) != len) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else {
+ pp->polls++;
+ }
+ }
+}
+
+
+/*
+ * state machine for initializing the clock
+ */
+static void
+goes_doevent(peer, event)
+ struct peer *peer;
+ enum goes_event event;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+
+#ifdef DEBUG
+ if (debug) {
+ printf("goes_doevent: %d\n", (int)event);
+ }
+#endif
+ if (event == e_TS && up->State != F51 && up->State != F08) {
+ goes_send(peer, "\03\r");
+ }
+
+ switch (event) {
+ case e_Init:
+ goes_send(peer, "F18\r");
+ up->State = Start;
+ break;
+ case e_F18:
+ goes_send(peer, "F50\r");
+ up->State = F18;
+ break;
+ case e_F50:
+ goes_send(peer, "F51\r");
+ up->State = F50;
+ break;
+ case e_F51:
+ goes_send(peer, "F08\r");
+ up->State = F51;
+ break;
+ case e_TS:
+ /* nothing to send - we like this mode */
+ up->State = F08;
+ break;
+ }
+}
+
+static void
+goes_initstate(peer)
+ struct peer *peer;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ up->State = Base; /* just in case */
+ goes_doevent(peer, e_Init);
+}
+
+
+/*
+ * goes_poll - called by the transmit procedure
+ */
+static void
+goes_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ if (up->pollcnt == 0) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ goes_send(peer, "C");
+ }
+ else
+ up->pollcnt--;
+
+ /*
+ * polled every 64 seconds. Ask goes_receive to hand in a
+ * timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_gpstm.c b/usr.sbin/xntpd/xntpd/refclock_gpstm.c
new file mode 100644
index 0000000..9dfc87c
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_gpstm.c
@@ -0,0 +1,999 @@
+/*
+ * refclock_gpstm - clock driver for the Kinimetrics Truetime GPSTM/TMD rcvr
+ * Version 1.0 (from Version 2.0 of the GOES driver, as of 03Jan94)
+ */
+
+#if defined(REFCLOCK) && (defined(GPSTM) || defined(GPSTMCLK) \
+ || defined(GPSTMPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#ifdef SYS_BSDI
+#undef HAVE_BSD_TTYS
+#include <sys/ioctl.h>
+#endif
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(GPSTMCLK)
+#include <clkdefs.h>
+#endif /* GPSTMCLK */
+#endif /* STREAM */
+
+#if defined(GPSTMPPS)
+#include <sys/ppsclock.h>
+#endif /* GPSTMPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime GPS-TM/TMD Receiver
+ *
+ * Most of this code is copied from refclock_goes.c with thanks.
+ *
+ * the time code looks like follows:
+ *
+ * ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * ? +/- 500 milliseconds # +/- 50 milliseconds
+ * * +/- 5 milliseconds . +/- 1 millisecond
+ * space less than 1 millisecond
+ * C - Carriage return
+ * L - Line feed
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ *
+ * Flag1 set to 1 will silence the clock side of xntpd, just reading the
+ * clock without trying to write to it. This is usefull if several
+ * xntpds listen to the same clock. This has not been tested yet...
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* max number of GPSTM units */
+#define GPSTM232 "/dev/gpstm%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Kinemetrics GPS-TM/TMD Receiver" /* who we are */
+#define GMT 0 /* hour offset from Greenwich */
+#define NCODES 3 /* stages of median filter */
+#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */
+#define TIMEOUT 180 /* ping the clock if it's silent this long */
+
+/*
+ * used by the state machine
+ */
+enum gpstm_event {e_Init, e_F18, e_F50, e_F51, e_TS};
+static enum {Base, Start, F18, F50, F51, F08} State[MAXUNITS];
+static void gpstm_doevent P((int, enum gpstm_event));
+static void gpstm_initstate P((int));
+
+/*
+ * Hack to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * GPSTM unit control structure
+ */
+struct gpstm_unit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+ l_fp offset[NCODES]; /* recent sample offsets */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short polled; /* Hand in a time sample? */
+ u_char lencode; /* length of last timecode */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char year; /* year of eternity */
+ u_short day; /* day of year */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ u_short msec; /* millisecond of second */
+ u_char quality; /* quality character */
+ u_long yearstart; /* start of current year */
+ /*
+ * Status tallies
+ */
+ U_LONG polls; /* polls sent */
+ U_LONG noreply; /* no replies to polls */
+ U_LONG coderecv; /* timecodes received */
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct gpstm_unit *gpstm_units[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor1[MAXUNITS];
+static l_fp fudgefactor2[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char readonlyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+/*
+ * Function prototypes
+ */
+static void gpstm_init P((void));
+static int gpstm_start P((int, struct peer *));
+static void gpstm_shutdown P((int, struct peer *));
+static void gpstm_rep_event P((struct gpstm_unit *, int));
+static void gpstm_receive P((struct recvbuf *));
+static char gpstm_process P((struct gpstm_unit *, l_fp *, u_fp *));
+static void gpstm_poll P((int, struct peer *));
+static void gpstm_control P((int, struct refclockstat *,
+ struct refclockstat *));
+static void gpstm_buginfo P((int, struct refclockbug *));
+static void gpstm_send P((struct gpstm_unit *, char *));
+
+struct refclock refclock_gpstm = {
+ gpstm_start, gpstm_shutdown, gpstm_poll,
+ gpstm_control, gpstm_init, gpstm_buginfo, NOFLAGS
+};
+
+/*
+ * gpstm_init - initialize internal driver data
+ */
+static void
+gpstm_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)gpstm_units, 0, sizeof gpstm_units);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor1[i].l_ui = 0;
+ fudgefactor1[i].l_uf = 0;
+ fudgefactor2[i].l_ui = 0;
+ fudgefactor2[i].l_uf = 0;
+ stratumtouse[i] = 0;
+ readonlyclockflag[i] = 0;
+ memcpy((char *)&refid[i], REFID, 4);
+ }
+}
+
+
+/*
+ * gpstm_start - open the device and initialize data for processing
+ */
+static int
+gpstm_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct gpstm_unit *gpstm;
+ register int i;
+ int fd232;
+ char dev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_start: unit %d invalid", unit);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_start: unit %d in use", unit);
+ return 0;
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(dev, GPSTM232, unit);
+ fd232 = open(dev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "gpstm_start: open of %s: %m", dev);
+ return 0;
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TCGETA): %m", dev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TCSETA): %m", dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The GPSTMCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The GPSTMPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+ ttyp = &ttyb;
+
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcgetattr(%s): %m", dev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcsetattr(%s): %m", dev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcflush(%s): %m", dev);
+ goto screwed;
+ }
+#if defined(STREAM)
+#if defined(GPSTMCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, I_PUSH, clk): %m", dev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, CLK_SETSTR): %m", dev);
+#endif /* GPSTMCLK */
+#if defined(GPSTMPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, I_PUSH, ppsclock): %m", dev);
+ else
+ fdpps = fd232;
+#endif /* GPSTMPPS */
+#endif /* STREAM */
+ }
+#endif /* HAVE_TERMIOS */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The GPSTMCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(GPSTMCLK)
+ int ldisc = CLKLDISC;
+#endif /* GPSTMCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCGETP): %m", dev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(GPSTMCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* GPSTMCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCSETP): %m", dev);
+ goto screwed;
+ }
+#if defined(GPSTMCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCSETD): %m", dev);
+ goto screwed;
+ }
+#endif /* GPSTMCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (gpstm_units[unit] != 0) {
+ gpstm = gpstm_units[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && gpstm_units[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ gpstm = gpstm_units[i];
+ gpstm_units[i] = 0;
+ } else {
+ gpstm = (struct gpstm_unit *)
+ emalloc(sizeof(struct gpstm_unit));
+ }
+ }
+ memset((char *)gpstm, 0, sizeof(struct gpstm_unit));
+ gpstm_units[unit] = gpstm;
+
+ /*
+ * Set up the structures
+ */
+ gpstm->peer = peer;
+ gpstm->unit = (u_char)unit;
+ gpstm->timestarted = current_time;
+
+ gpstm->io.clock_recv = gpstm_receive;
+ gpstm->io.srcclock = (caddr_t)gpstm;
+ gpstm->io.datalen = 0;
+ gpstm->io.fd = fd232;
+ if (!io_addclock(&gpstm->io)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = PRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ gpstm_initstate(unit);
+ return 1;
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return 0;
+}
+
+/*
+ * gpstm_shutdown - shut down a clock
+ */
+static void
+gpstm_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_shutdown: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ gpstm = gpstm_units[unit];
+ io_closeclock(&gpstm->io);
+ unitinuse[unit] = 0;
+}
+
+
+/*
+ * gpstm_rep_event - note the occurance of an event
+ */
+static void
+gpstm_rep_event(gpstm, code)
+ struct gpstm_unit *gpstm;
+ int code;
+{
+ struct peer *peer;
+
+ peer = gpstm->peer;
+ if (gpstm->status != (u_char)code) {
+ gpstm->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ gpstm->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "clock %s event %x\n", ntoa(&peer->srcadr), code);
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_rep_event(gpstm%d, code %d)\n",
+ gpstm->unit, code);
+ }
+#endif
+ }
+ if (code == CEVNT_BADREPLY)
+ gpstm_initstate(gpstm->unit);
+}
+
+
+/*
+ * gpstm_receive - receive data from the serial interface on a clock
+ */
+static void
+gpstm_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int i;
+ register struct gpstm_unit *gpstm;
+ register u_char *dpt;
+ register char *cp;
+ register u_char *dpend;
+ l_fp tstmp;
+ u_fp dispersion;
+
+ /*
+ * Get the clock this applies to and a pointers to the data
+ */
+ gpstm = (struct gpstm_unit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+
+ /*
+ * Edit timecode to remove control chars
+ */
+ dpend = dpt + rbufp->recv_length;
+ cp = gpstm->lastcode;
+ while (dpt < dpend) {
+ if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
+#ifdef GPSTMCLK
+ else if (*cp == '\r') {
+ if (dpend - dpt < 8) {
+ /* short timestamp */
+ return;
+ }
+ if (!buftvtots(dpt,&gpstm->lastrec)) {
+ /* screwy timestamp */
+ return;
+ }
+ dpt += 8;
+ }
+#endif
+ }
+ *cp = '\0';
+ gpstm->lencode = cp - gpstm->lastcode;
+ if (gpstm->lencode == 0)
+ return;
+#ifndef GPSTMCLK
+ gpstm->lastrec = rbufp->recv_time;
+#endif /* GPSTMCLK */
+#if !defined(GPSTMCLK) && !defined(GPSTMPPS) && defined(TIOCMODT)
+ do {
+ auto struct timeval cur, now;
+ register long usec;
+
+ if (ioctl(gpstm->io.fd, TIOCMODT, &cur) < 0) {
+ syslog(LOG_ERR, "TIOCMODT: %m");
+#ifdef DEBUG
+ if (debug) perror("TIOCMODT");
+ break;
+#endif
+ }
+ if (cur.tv_sec == 0) {
+ /* no timestamps yet */
+ if (debug) printf("MODT tv_sec == 0\n");
+ break;
+ }
+
+ gettimeofday(&now, NULL);
+ usec = 1000000 * (now.tv_sec - cur.tv_sec)
+ + (now.tv_usec - cur.tv_usec);
+#ifdef DEBUG
+ if (debug) printf("lastmodem: delay=%d us\n", usec);
+#endif
+ if (usec < 0 || usec > 10000) {
+ /* time warp or stale timestamp */
+ break;
+ }
+ if (!buftvtots((char *)&cur, &gpstm->lastrec)) {
+ /* screwy timestamp */
+ break;
+ }
+ } while (0);
+#endif /*TIOCMODT*/
+
+#ifdef DEBUG
+ if (debug)
+ printf("gpstm: timecode %d %s\n",
+ gpstm->lencode, gpstm->lastcode);
+#endif
+
+ cp = gpstm->lastcode;
+ gpstm->leap = 0;
+ if ((cp[0] == 'F' && isdigit(cp[1]) && isdigit(cp[2]))
+ || (cp[0] == ' ' && cp[1] == 'T' && cp[2] == 'R')) {
+ enum gpstm_event event;
+
+ syslog(LOG_NOTICE, "gpstm%d: \"%s\"", gpstm->unit, cp);
+ if (cp[1] == '5' && cp[2] == '0')
+ event = e_F50;
+ else if (cp[1] == '5' && cp[2] == '1')
+ event = e_F51;
+ else if (!strncmp(" TRUETIME Mk III", cp, 16))
+ event = e_F18;
+ else {
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+ gpstm_doevent(gpstm->unit, event);
+ return;
+ } else if (gpstm->lencode == 13) {
+ /*
+ * Check timecode format 0
+ */
+ if (!isdigit(cp[0]) /* day of year */
+ || !isdigit(cp[1])
+ || !isdigit(cp[2])
+ || cp[3] != ':' /* : separator */
+ || !isdigit(cp[4]) /* hours */
+ || !isdigit(cp[5])
+ || cp[6] != ':' /* : separator */
+ || !isdigit(cp[7]) /* minutes */
+ || !isdigit(cp[8])
+ || cp[9] != ':' /* : separator */
+ || !isdigit(cp[10]) /* seconds */
+ || !isdigit(cp[11]))
+ {
+ gpstm->badformat++;
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Convert format 0 and check values
+ */
+ gpstm->year = 0; /* fake */
+ gpstm->day = cp[0] - '0';
+ gpstm->day = MULBY10(gpstm->day) + cp[1] - '0';
+ gpstm->day = MULBY10(gpstm->day) + cp[2] - '0';
+ gpstm->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
+ gpstm->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
+ gpstm->second = MULBY10(cp[10] - '0') + cp[11] - '0';
+ gpstm->msec = 0;
+
+ if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*')
+ gpstm->leap = LEAP_NOTINSYNC;
+ else
+ gpstm->lasttime = current_time;
+
+ if (gpstm->day < 1 || gpstm->day > 366) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADDATE);
+ return;
+ }
+ if (gpstm->hour > 23 || gpstm->minute > 59
+ || gpstm->second > 59) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ gpstm_doevent(gpstm->unit, e_TS);
+ } else {
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!gpstm->polled)
+ return;
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present.
+ *
+ * this code does not yet know how to do the years
+ */
+ tstmp = gpstm->lastrec;
+ if (!clocktime(gpstm->day, gpstm->hour, gpstm->minute,
+ gpstm->second, GMT, tstmp.l_ui,
+ &gpstm->yearstart, &gpstm->lastref.l_ui))
+ {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(gpstm->msec, gpstm->lastref.l_uf);
+
+ i = ((int)(gpstm->coderecv)) % NCODES;
+ gpstm->offset[i] = gpstm->lastref;
+ L_SUB(&gpstm->offset[i], &tstmp);
+ if (gpstm->coderecv == 0)
+ for (i = 1; i < NCODES; i++)
+ gpstm->offset[i] = gpstm->offset[0];
+
+ gpstm->coderecv++;
+
+ /*
+ * Process the median filter, and pass the
+ * offset and dispersion along. We use lastrec as both the
+ * reference time and receive time in order to avoid being cute,
+ * like setting the reference time later than the receive time,
+ * which may cause a paranoid protocol module to chuck out the
+ * data.
+ */
+ if (!gpstm_process(gpstm, &tstmp, &dispersion)) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(gpstm->peer, &tstmp, GMT, dispersion,
+ &gpstm->lastrec, &gpstm->lastrec, gpstm->leap);
+
+ /*
+ * We have succedded in answering the poll. Turn off the flag
+ */
+ gpstm->polled = 0;
+}
+
+/*
+ * gpstm_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+gpstm_send(gpstm, cmd)
+ struct gpstm_unit *gpstm;
+ char *cmd;
+{
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_send(gpstm%d): %s\n", gpstm->unit, cmd);
+ }
+#endif
+ if (!readonlyclockflag[gpstm->unit]) {
+ register int len = strlen(cmd);
+
+ if (write(gpstm->io.fd, cmd, len) != len) {
+ syslog(LOG_ERR, "gpstm_send: unit %d: %m",
+ gpstm->unit);
+ gpstm_rep_event(gpstm, CEVNT_FAULT);
+ }
+ }
+}
+
+/*
+ * state machine for initializing the clock
+ */
+
+static void
+gpstm_doevent(unit, event)
+ int unit;
+ enum gpstm_event event;
+{
+ struct gpstm_unit *gpstm = gpstm_units[unit];
+
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_doevent(gpstm%d, %d)\n", unit, (int)event);
+ }
+#endif
+ if (event == e_TS && State[unit] != F51 && State[unit] != F08) {
+ gpstm_send(gpstm, "\03\r");
+ }
+
+ switch (event) {
+ case e_Init:
+ gpstm_send(gpstm, "F18\r");
+ State[unit] = Start;
+ break;
+ case e_F18:
+ gpstm_send(gpstm, "F50\r");
+ State[unit] = F18;
+ break;
+ case e_F50:
+ gpstm_send(gpstm, "F51\r");
+ State[unit] = F50;
+ break;
+ case e_F51:
+ gpstm_send(gpstm, "F08\r");
+ State[unit] = F51;
+ break;
+ case e_TS:
+ /* nothing to send - we like this mode */
+ State[unit] = F08;
+ break;
+ }
+}
+
+static void
+gpstm_initstate(unit)
+ int unit;
+ {
+ State[unit] = Base; /* just in case */
+ gpstm_doevent(unit, e_Init);
+}
+
+/*
+ * gpstm_process - process a pile of samples from the clock
+ */
+static char
+gpstm_process(gpstm, offset, dispersion)
+ struct gpstm_unit *gpstm;
+ l_fp *offset;
+ u_fp *dispersion;
+{
+ register int i, j;
+ register U_LONG tmp_ui, tmp_uf;
+ int not_median1 = -1; /* XXX correct? */
+ int not_median2 = -1; /* XXX correct? */
+ int median;
+ u_fp disp_tmp, disp_tmp2;
+
+ /*
+ * This code implements a three-stage median filter. First, we
+ * check if the samples are within 125 ms of each other. If not,
+ * dump the sample set. We take the median of the three offsets
+ * and use that as the sample offset. We take the maximum
+ * difference and use that as the sample dispersion. There
+ * probably is not much to be gained by a longer filter, since
+ * the clock filter in ntp_proto should do its thing.
+ */
+ disp_tmp2 = 0;
+ for (i = 0; i < NCODES-1; i++) {
+ for (j = i+1; j < NCODES; j++) {
+ tmp_ui = gpstm->offset[i].l_ui;
+ tmp_uf = gpstm->offset[i].l_uf;
+ M_SUB(tmp_ui, tmp_uf, gpstm->offset[j].l_ui,
+ gpstm->offset[j].l_uf);
+ if (M_ISNEG(tmp_ui, tmp_uf)) {
+ M_NEG(tmp_ui, tmp_uf);
+ }
+ if (tmp_ui != 0 || tmp_uf > CODEDIFF) {
+ return 0;
+ }
+ disp_tmp = MFPTOFP(0, tmp_uf);
+ if (disp_tmp > disp_tmp2) {
+ disp_tmp2 = disp_tmp;
+ not_median1 = i;
+ not_median2 = j;
+ }
+ }
+ }
+
+ /*
+ * It seems as if all are within 125 ms of each other.
+ * Now to determine the median of the three. Whlie the
+ * 125 ms check was going on, we also subtly catch the
+ * dispersion and set-up for a very easy median calculation.
+ * The largest difference between any two samples constitutes
+ * the dispersion. The sample not involve in the dispersion is
+ * the median sample. EASY!
+ */
+ if (gpstm->lasttime == 0 || disp_tmp2 > MAXDISPERSE)
+ disp_tmp2 = MAXDISPERSE;
+ if (not_median1 == 0) {
+ if (not_median2 == 1)
+ median = 2;
+ else
+ median = 1;
+ } else {
+ median = 0;
+ }
+ *offset = gpstm->offset[median];
+ *dispersion = disp_tmp2;
+ return 1;
+}
+
+/*
+ * gpstm_poll - called by the transmit procedure
+ */
+static void
+gpstm_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct gpstm_unit *gpstm;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_poll: unit %d not in use", unit);
+ return;
+ }
+ gpstm = gpstm_units[unit];
+ if ((current_time - gpstm->lasttime) > 150) {
+ gpstm->noreply++;
+ gpstm_rep_event(gpstm_units[unit], CEVNT_TIMEOUT);
+ gpstm_initstate(gpstm->unit);
+ }
+
+ /*
+ * polled every 64 seconds. Ask our receiver to hand in a timestamp.
+ */
+ gpstm->polled = 1;
+ gpstm->polls++;
+}
+
+/*
+ * gpstm_control - set fudge factors, return statistics
+ */
+static void
+gpstm_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor1[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ fudgefactor2[unit] = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ readonlyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = gpstm_units[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_GPSTM_TRUETIME;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG1;
+ out->clockdesc = DESCRIPTION;
+ out->fudgetime1 = fudgefactor1[unit];
+ out->fudgetime2 = fudgefactor2[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->flags = readonlyclockflag[unit];
+ if (unitinuse[unit]) {
+ gpstm = gpstm_units[unit];
+ out->lencode = gpstm->lencode;
+ out->lastcode = gpstm->lastcode;
+ out->timereset = current_time - gpstm->timestarted;
+ out->polls = gpstm->polls;
+ out->noresponse = gpstm->noreply;
+ out->badformat = gpstm->badformat;
+ out->baddata = gpstm->baddata;
+ out->lastevent = gpstm->lastevent;
+ out->currentstatus = gpstm->status;
+ }
+ }
+}
+
+/*
+ * gpstm_buginfo - return clock dependent debugging info
+ */
+static void
+gpstm_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ gpstm = gpstm_units[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (gpstm->lasttime != 0)
+ bug->values[0] = current_time - gpstm->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)gpstm->reason;
+ bug->values[2] = (U_LONG)gpstm->year;
+ bug->values[3] = (U_LONG)gpstm->day;
+ bug->values[4] = (U_LONG)gpstm->hour;
+ bug->values[5] = (U_LONG)gpstm->minute;
+ bug->values[6] = (U_LONG)gpstm->second;
+ bug->values[7] = (U_LONG)gpstm->msec;
+ bug->values[8] = gpstm->noreply;
+ bug->values[9] = gpstm->yearstart;
+ bug->values[10] = gpstm->quality;
+ bug->stimes = 0x1c;
+ bug->times[0] = gpstm->lastref;
+ bug->times[1] = gpstm->lastrec;
+ bug->times[2] = gpstm->offset[0];
+ bug->times[3] = gpstm->offset[1];
+ bug->times[4] = gpstm->offset[2];
+}
+
+#endif /*GPSTM et al*/
diff --git a/usr.sbin/xntpd/xntpd/refclock_heath.c b/usr.sbin/xntpd/xntpd/refclock_heath.c
new file mode 100644
index 0000000..6f2899d
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_heath.c
@@ -0,0 +1,393 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000 Most Accurate Clock
+ */
+#if defined(REFCLOCK) && defined(HEATH)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Heath GC-1000 Most Accurate Clock, with
+ * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ * robust than other supported receivers. Its claimed accuracy is 100 ms
+ * when actually synchronized to the broadcast signal, but this doesn't
+ * happen even most of the time, due to propagation conditions, ambient
+ * noise sources, etc. When not synchronized, the accuracy is at the
+ * whim of the internal clock oscillator, which can wander into the
+ * sunset without warning. Since the indicated precision is 100 ms,
+ * expect a host synchronized only to this thing to wander to and fro,
+ * occasionally being rudely stepped when the offset exceeds the default
+ * CLOCK_MAX of 128 ms.
+ *
+ * The internal DIPswitches should be set to operate at 1200 baud in
+ * MANUAL mode and the current year. The external DIPswitches should be
+ * set to GMT and 24-hour format, or to the host local time zone (with
+ * DST) and 12-hour format. It is very important that the year be
+ * set correctly in the DIPswitches. Otherwise, the day of year will be
+ * incorrect after 28 April of a normal or leap year. In 12-hour mode
+ * with DST selected the clock will be incorrect by an hour for an
+ * indeterminate amount of time between 0000Z and 0200 on the day DST
+ * changes.
+ *
+ * In MANUAL mode the clock responds to a rising edge of the request to
+ * send (RTS) modem control line by sending the timecode. Therefore, it
+ * is necessary that the operating system implement the TIOCMBIC and
+ * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
+ * restrictions require the use of a POSIX-compatible programming
+ * interface, although other interfaces may work as well.
+ *
+ * A simple hardware modification to the clock can be made which
+ * prevents the clock hearing the request to send (RTS) if the HI SPEC
+ * lamp is out. Route the HISPEC signal to the tone decoder board pin
+ * 19, from the display, pin 19. Isolate pin 19 of the decoder board
+ * first, but maintain connection with pin 10. Also isolate pin 38 of
+ * the CPU on the tone board, and use half an added 7400 to gate the
+ * original signal to pin 38 with that from pin 19.
+ *
+ * The clock message consists of 23 ASCII printing characters in the
+ * following format:
+ *
+ * hh:mm:ss.f AM dd/mm/yr<cr>
+ *
+ * hh:mm:ss.f = hours, minutes, seconds
+ * f = deciseconds ('?' when out of spec)
+ * AM/PM/bb = blank in 24-hour mode
+ * dd/mm/yr = day, month, year
+ *
+ * The alarm condition is indicated by '?', rather than a digit, at f.
+ * Note that 0?:??:??.? is displayed before synchronization is first
+ * established and hh:mm:ss.? once synchronization is established and
+ * then lost again for about a day.
+ *
+ * Fudge Factors
+ *
+ * A fudge time1 value of .04 s appears to center the clock offset
+ * residuals. The fudge time2 parameter is the local time offset east of
+ * Greenwich, which depends on DST. Sorry about that, but the clock
+ * gives no hint on what the DIPswitches say.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/heath%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 baud) */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENHEATH 23 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct heathunit {
+ int pollcnt; /* poll message counter */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int heath_start P((int, struct peer *));
+static void heath_shutdown P((int, struct peer *));
+static void heath_receive P((struct recvbuf *));
+static void heath_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_heath = {
+ heath_start, /* start up driver */
+ heath_shutdown, /* shut down driver */
+ heath_poll, /* transmit poll message */
+ noentry, /* not used (old heath_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old heath_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct heathunit *)
+ emalloc(sizeof(struct heathunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct heathunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = heath_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char dsec, a[5];
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * We get a buffer and timestamp for each <cr>; however, we use
+ * the timestamp captured at the RTS modem control line toggle
+ * on the assumption that's what the radio bases the timecode
+ * on. Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp->lastrec = up->tstamp;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("heath: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENHEATH) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ */
+ if (sscanf(pp->lastcode, "%2d:%2d:%2d.%c%5c%2d/%2d/%2d",
+ &pp->hour, &pp->minute, &pp->second, &dsec, a, &month, &day,
+ &pp->year) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * If AM or PM is received, assume the clock is displaying local
+ * time. First, convert to 24-hour format, then add the local
+ * time correction (in hours east of Greenwich) from
+ * fudgetime2.
+ */
+ switch (a[1]) {
+ case 'P':
+ if (pp->hour < 12)
+ pp->hour += 12;
+ break;
+
+ case 'A':
+ if (pp->hour == 12)
+ pp->hour -= 12;
+ break;
+ }
+ i = (int)pp->hour - (int)pp->fudgetime2.l_ui;
+ if (i < 0)
+ i += 24;
+ pp->hour = i % 24;
+
+ /*
+ * We determine the day of the year from the DIPswitches. This
+ * should be fixed, since somebody might forget to set them.
+ * Someday this hazard will be fixed by a fiendish scheme that
+ * looks at the timecode and year the radio shows, then computes
+ * the residue of the seconds mod the seconds in a leap cycle.
+ * If in the third year of that cycle and the third and later
+ * months of that year, add one to the day. Then, correct the
+ * timecode accordingly. Icky pooh. This bit of nonsense could
+ * be avoided if the engineers had been required to write a
+ * device driver before finalizing the timecode format.
+ *
+ * Yes, I know this code incorrectly thinks that 2100 is a leap
+ * year; but, the latest year that can be set by the DIPswitches
+ * is 1997 anyay. Life is short.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ /*
+ * Determine synchronization and last update
+ */
+ if (!isdigit(dsec)) {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ pp->msec = (dsec - '0') * 100;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * heath_poll - called by the transmit procedure
+ */
+static void
+heath_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ int bits = TIOCM_RTS;
+
+ /*
+ * At each poll we check for timeout and toggle the RTS modem
+ * control line, then take a timestamp. Presumably, this is the
+ * event the radio captures to generate the timecode.
+ */
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+
+ /*
+ * We toggle the RTS modem control lead to kick a timecode loose
+ * from the radio. This code works only for POSIX and SYSV
+ * interfaces. With bsd you are on your own. We take a timestamp
+ * between the up and down edges to lengthen the pulse, which
+ * should be about 50 usec on a Sun IPC. With hotshot CPUs, the
+ * pulse might get too short. Later.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+ gettstamp(&up->tstamp);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_irig.c b/usr.sbin/xntpd/xntpd/refclock_irig.c
new file mode 100644
index 0000000..bc93fd7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_irig.c
@@ -0,0 +1,259 @@
+/*
+ * refclock_irig - clock driver for the IRIG audio decoder
+ */
+#if defined(REFCLOCK) && defined(IRIG) && defined(sun)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/ioccom.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include <sys/bsd_audioirig.h>
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the IRIG audio decoder. This clever gadget uses
+ * a modified BSD audio driver for the Sun SPARCstation which provides
+ * a timestamp, raw binary timecode, status byte and decoded ASCII
+ * timecode. The data are represented in the structure in the
+ * sys/bsd_audioirig.h header file:
+ *
+ * struct irig_time {
+ * struct timeval stamp; timestamp
+ * u_char bits[13]; 100 IRIG data bits
+ * u_char status; status byte
+ * char time[14]; time string (null terminated)
+ *
+ * where stamp represents a timestamp at the zero crossing of the index
+ * marker at the second's epoch, bits is a 13-octet, zero-padded binary-
+ * coded string representing code elements 1 through 100 in the IRIG-B
+ * code format, and status is a status bute, The decoded timestamp is a
+ * 13-octet, null-terminated ASCII string "ddd hh:mm:ss*", where ddd is
+ * the day of year, hh:mm:ss the time of day and * is a status
+ * indicator, with " " indicating valid time and "?" indicating
+ * something wrong.
+ *
+ * The timestamp is in Unix timeval format, consisting of two 32-bit
+ * words, the first of which is the seconds since 1970 and the second is
+ * the fraction of the second in microseconds. The status byte is zero
+ * if (a) the input signal is within amplitude tolerances, (b) the raw
+ * binary timecode contains only valid code elements, (c) 11 position
+ * identifiers have been found at the expected element positions, (d)
+ * the clock status byte contained in the timecode is valid, and (e) a
+ * time determination has been made since the last read() system call.
+ *
+ * The 100 elements of the IRIG-B timecode are numbered from 0 through
+ * 99. Position identifiers occur at elements 0, 9, 19 and every ten
+ * thereafter to 99. The control function (CF) elements begin at element
+ * 50 (CF 1) and extend to element 78 (CF 27). The straight-binary-
+ * seconds (SBS) field, which encodes the seconds of the UTC day, begins
+ * at element 80 (CF 28) and extends to element 97 (CF 44). The encoding
+ * of elements 50 (CF 1) through 78 (CF 27) is device dependent. This
+ * driver presently does not use the CF elements.
+ *
+ * Where feasible, the interface should be operated with signature
+ * control, so that, if the IRIG signal is lost or malformed, the
+ * interface produces an unmodulated signal, rather than possibly random
+ * digits. The driver will declare "unsynchronized" in this case.
+ *
+ * Spectracom Netclock/2 WWVB Synchronized Clock
+ *
+ * Element CF Function
+ * -------------------------------------
+ * 55 6 time sync status
+ * 60-63 10-13 bcd year units
+ * 65-68 15-18 bcd year tens
+ *
+ */
+
+/*
+ * IRIG interface definitions
+ */
+#define DEVICE "/dev/irig%d" /* device name and unit */
+#define PRECISION (-13) /* precision assumed (100 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "IRIG Audio Decoder" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define IRIG_FORMAT 1 /* IRIG timestamp format */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Function prototypes
+ */
+static int irig_start P((int, struct peer *));
+static void irig_shutdown P((int, struct peer *));
+static void irig_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * irig_start - open the device and initialize data for processing
+ */
+static int
+irig_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+ char device[20];
+ int fd;
+ int format = IRIG_FORMAT;
+
+ /*
+ * Open audio device and set format
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "irig_start: open of %s: %m", device);
+ return (0);
+ }
+ if (ioctl(fd, AUDIO_IRIG_OPEN, 0) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_OPEN %m");
+ close(fd);
+ return (0);
+ }
+ if (ioctl(fd, AUDIO_IRIG_SETFORMAT, (char *)&format) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_SETFORMAT %m",
+ DEVICE);
+ close(fd);
+ return (0);
+ }
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * irig_shutdown - shut down the clock
+ */
+static void
+irig_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ io_closeclock(&pp->io);
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ */
+static void
+irig_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+
+ struct refclockproc *pp;
+ struct irig_time buf;
+ char *cp, *dp;
+ u_char *dpt;
+ int i;
+
+ pp = peer->procptr;
+ if (read(pp->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->polls++;
+
+#ifdef DEBUG
+ if (debug) {
+ dpt = (u_char *)&buf;
+ printf("irig: ");
+ for (i = 0; i < sizeof(buf); i++)
+ printf("%02x", *dpt++);
+ printf("\n");
+ }
+#endif
+
+ buf.stamp.tv_sec += JAN_1970;
+ TVTOTS(&buf.stamp, &pp->lastrec);
+ cp = buf.time;
+ dp = pp->lastcode;
+ for (i = 0; i < sizeof(buf.time); i++)
+ *dp++ = *cp++;
+ *--dp = '\0';
+ pp->lencode = dp - pp->lastcode;
+
+#ifdef DEBUG
+ if (debug)
+ printf("irig: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->lastcode);
+#endif
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+
+ /*
+ * Get IRIG time and convert to timestamp format
+ */
+ if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d",
+ &pp->day, &pp->hour, &pp->minute, &pp->second) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if (pp->lastcode[12] != ' ') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_leitch.c b/usr.sbin/xntpd/xntpd/refclock_leitch.c
new file mode 100644
index 0000000..b6b002f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_leitch.c
@@ -0,0 +1,710 @@
+/*
+ * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
+ */
+#if defined(REFCLOCK) && (defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#ifdef STREAM
+#include <stropts.h>
+#if defined(LEITCHCLK)
+#include <sys/clkdefs.h>
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+
+#if defined (LEITCHPPS)
+#include <sys/ppsclock.h>
+#endif /* LEITCHPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Driver for Leitch CSD-5300 Master Clock System
+ *
+ * COMMANDS:
+ * DATE: D <CR>
+ * TIME: T <CR>
+ * STATUS: S <CR>
+ * LOOP: L <CR>
+ *
+ * FORMAT:
+ * DATE: YYMMDD<CR>
+ * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
+ * second bondaried on the stop bit of the <CR>
+ * second boundaries at '/' above.
+ * STATUS: G (good), D (diag fail), T (time not provided) or
+ * P (last phone update failed)
+ */
+#define MAXUNITS 1 /* max number of LEITCH units */
+#define LEITCHREFID "ATOM" /* reference id */
+#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
+#define LEITCH232 "/dev/leitch%d" /* name of radio device */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define leitch_send(A,M) \
+ if (debug) fprintf(stderr,"write leitch %s\n",M); \
+ if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ if (debug) \
+ fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
+ else \
+ syslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+
+#define STATE_IDLE 0
+#define STATE_DATE 1
+#define STATE_TIME1 2
+#define STATE_TIME2 3
+#define STATE_TIME3 4
+
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * LEITCH unit control structure
+ */
+struct leitchunit {
+ struct peer *peer;
+ struct event leitchtimer;
+ struct refclockio leitchio;
+ u_char unit;
+ short year;
+ short yearday;
+ short month;
+ short day;
+ short hour;
+ short second;
+ short minute;
+ short state;
+ u_short fudge1;
+ l_fp reftime1;
+ l_fp reftime2;
+ l_fp reftime3;
+ l_fp codetime1;
+ l_fp codetime2;
+ l_fp codetime3;
+ u_long yearstart;
+};
+
+/*
+ * Function prototypes
+ */
+static void leitch_init P((void));
+static int leitch_start P((int, struct peer *));
+static void leitch_shutdown P((int, struct peer *));
+static void leitch_poll P((int, struct peer *));
+static void leitch_control P((int, struct refclockstat *, struct refclockstat *));
+#define leitch_buginfo noentry
+static void leitch_receive P((struct recvbuf *));
+static void leitch_process P((struct leitchunit *));
+static void leitch_timeout P((struct peer *));
+static int leitch_get_date P((struct recvbuf *, struct leitchunit *));
+static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int));
+static int dysize P((int));
+
+static struct leitchunit leitchunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_leitch = {
+ leitch_start, leitch_shutdown, leitch_poll,
+ leitch_control, leitch_init, leitch_buginfo, NOFLAGS
+};
+
+/*
+ * leitch_init - initialize internal leitch driver data
+ */
+static void
+leitch_init()
+{
+ int i;
+
+ memset((char*)leitchunits, 0, sizeof(leitchunits));
+ memset((char*)unitinuse, 0, sizeof(unitinuse));
+ for (i = 0; i < MAXUNITS; i++)
+ memcpy((char *)&refid[i], LEITCHREFID, 4);
+}
+
+/*
+ * leitch_shutdown - shut down a LEITCH clock
+ */
+static void
+leitch_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_shutdown()\n");
+#endif
+}
+
+/*
+ * leitch_poll - called by the transmit procedure
+ */
+static void
+leitch_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct leitchunit *leitch;
+
+ /* start the state machine rolling */
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_poll()\n");
+#endif
+ if (unit > MAXUNITS) {
+ /* XXXX syslog it */
+ return;
+ }
+
+ leitch = &leitchunits[unit];
+
+ if (leitch->state != STATE_IDLE) {
+ /* reset and wait for next poll */
+ /* XXXX syslog it */
+ leitch->state = STATE_IDLE;
+ } else {
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ }
+}
+
+static void
+leitch_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "leitch_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in) {
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = (&leitchunits[unit])->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_ATOM_LEITCH;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->lastcode = "";
+ out->clockdesc = LEITCH_DESCRIPTION;
+ }
+}
+
+/*
+ * leitch_start - open the LEITCH devices and initialize data for processing
+ */
+static int
+leitch_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct leitchunit *leitch;
+ int fd232;
+ char leitchdev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "leitch_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port.
+ */
+ (void) sprintf(leitchdev, LEITCH232, unit);
+ fd232 = open(leitchdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR,
+ "leitch_start: open of %s: %m", leitchdev);
+ return (0);
+ }
+
+ leitch = &leitchunits[unit];
+ memset((char*)leitch, 0, sizeof(*leitch));
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The LEITCHPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcgetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcsetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcflush(%s): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
+#endif /* LEITCHCLK */
+#if defined(LEITCHPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, ppsclock): %m", leitchdev);
+ else
+ fdpps = fd232;
+#endif /* LEITCHPPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(LEITCHCLK)
+ int ldisc = CLKLDISC;
+#endif /* LEITCHCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(LEITCHCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* LEITCHCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
+ goto screwed;
+ }
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
+ goto screwed;
+ }
+#endif /* LEITCHCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Set up the structures
+ */
+ leitch->peer = peer;
+ leitch->unit = unit;
+ leitch->state = STATE_IDLE;
+ leitch->fudge1 = 15; /* 15ms */
+
+ leitch->leitchio.clock_recv = leitch_receive;
+ leitch->leitchio.srcclock = (caddr_t) leitch;
+ leitch->leitchio.datalen = 0;
+ leitch->leitchio.fd = fd232;
+ if (!io_addclock(&leitch->leitchio)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success. Note that root delay and root dispersion are
+ * always zero for this clock.
+ */
+ peer->precision = 0;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return(1);
+
+ /*
+ * Something broke; abandon ship.
+ */
+screwed:
+ close(fd232);
+ return(0);
+}
+
+/*
+ * leitch_receive - receive data from the serial interface on a leitch
+ * clock
+ */
+static void
+leitch_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_recieve(%*.*s)\n",
+ rbufp->recv_length, rbufp->recv_length,
+ rbufp->recv_buffer);
+#endif
+ if (rbufp->recv_length != 7)
+ return; /* The date is return with a trailing newline,
+ discard it. */
+
+ switch (leitch->state) {
+ case STATE_IDLE: /* unexpected, discard and resync */
+ return;
+ case STATE_DATE:
+ if (!leitch_get_date(rbufp,leitch)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch_send(leitch,"T\r");
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n",leitch->yearday);
+#endif
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ if (!leitch_get_time(rbufp,leitch,1)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime1.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime1.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
+ leitch->codetime1 = rbufp->recv_time;
+ leitch->state = STATE_TIME2;
+ break;
+ case STATE_TIME2:
+ if (!leitch_get_time(rbufp,leitch,2)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime2.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime2.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
+ leitch->codetime2 = rbufp->recv_time;
+ leitch->state = STATE_TIME3;
+ break;
+ case STATE_TIME3:
+ if (!leitch_get_time(rbufp,leitch,3)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime3.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime3.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
+ leitch->codetime3 = rbufp->recv_time;
+ leitch_process(leitch);
+ leitch->state = STATE_IDLE;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "leitech_receive: invalid state %d unit %d",
+ leitch->state, leitch->unit);
+ }
+}
+
+/*
+ * leitch_process - process a pile of samples from the clock
+ *
+ * This routine uses a three-stage median filter to calculate offset and
+ * dispersion. reduce jitter. The dispersion is calculated as the span
+ * of the filter (max - min), unless the quality character (format 2) is
+ * non-blank, in which case the dispersion is calculated on the basis of
+ * the inherent tolerance of the internal radio oscillator, which is
+ * +-2e-5 according to the radio specifications.
+ */
+static void
+leitch_process(leitch)
+ struct leitchunit *leitch;
+{
+ l_fp off;
+ s_fp delay;
+ l_fp codetime;
+ l_fp tmp_fp;
+ int isinsync = 1;
+ u_fp dispersion = 10;
+
+ delay = 20;
+
+ codetime = leitch->codetime3;
+
+ off = leitch->reftime1;
+ L_SUB(&off,&leitch->codetime1);
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime1.l_ui, leitch->codetime1.l_uf,
+ leitch->reftime1.l_ui, leitch->reftime1.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ tmp_fp = leitch->reftime2;
+ L_SUB(&tmp_fp,&leitch->codetime2);
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime2.l_ui, leitch->codetime2.l_uf,
+ leitch->reftime2.l_ui, leitch->reftime2.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ tmp_fp = leitch->reftime3;
+ L_SUB(&tmp_fp,&leitch->codetime3);
+
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime3.l_ui, leitch->codetime3.l_uf,
+ leitch->reftime3.l_ui, leitch->reftime3.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ refclock_receive(leitch->peer, &off, 0, dispersion, &codetime,
+ &codetime, isinsync);
+}
+
+/*
+ * leitch_timeout
+ */
+static void
+leitch_timeout(fp)
+ struct peer *fp;
+{
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_timeout()\n");
+#endif
+
+#ifdef NOTYET
+ { struct leitchunit *leitch = (struct leitchunit *)fp;
+
+ switch(leitch->state) {
+ case STATE_IDLE:
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ break;
+ case STATE_DATE:
+ leitch_send(leitch,"T\r");
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ case STATE_TIME2:
+ case STATE_TIME3:
+ default:
+ break;
+ }
+
+ leitch->leitchtimer.event_time += 30;
+ TIMER_ENQUEUE(timerqueue, &leitch->leitchtimer);
+ }
+#endif /* NOTYET */
+}
+
+/*
+ * dysize
+ */
+static int
+dysize(year)
+int year;
+{
+ if (year%4) { /* not a potential leap year */
+ return (365);
+ } else {
+ if (year % 100) { /* is a leap year */
+ return (366);
+ } else {
+ if (year % 400) {
+ return (365);
+ } else {
+ return (366);
+ }
+ }
+ }
+}
+
+static int
+leitch_get_date(rbufp,leitch)
+ struct recvbuf *rbufp;
+ struct leitchunit *leitch;
+{
+ int i;
+
+ if (rbufp->recv_length < 6)
+ return(0);
+#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
+ leitch->year = ATOB(0)*10 + ATOB(1);
+ leitch->month = ATOB(2)*10 + ATOB(3);
+ leitch->day = ATOB(4)*10 + ATOB(5);
+
+ /* sanity checks */
+ if (leitch->month > 12)
+ return(0);
+ if (leitch->day > days_in_month[leitch->month-1])
+ return(0);
+
+ /* calculate yearday */
+ i = 0;
+ leitch->yearday = leitch->day;
+
+ while ( i < (leitch->month-1) )
+ leitch->yearday += days_in_month[i++];
+
+ if ((dysize((leitch->year>90?1900:2000)+leitch->year)==365) &&
+ leitch->month > 2)
+ leitch->yearday--;
+
+ return(1);
+}
+
+/*
+ * leitch_get_time
+ */
+static int
+leitch_get_time(rbufp,leitch,which)
+ struct recvbuf *rbufp;
+ struct leitchunit *leitch;
+ int which;
+{
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+ leitch->hour = ATOB(0)*10 +ATOB(1);
+ leitch->minute = ATOB(2)*10 +ATOB(3);
+ leitch->second = ATOB(4)*10 +ATOB(5);
+
+ if ((leitch->hour > 23) || (leitch->minute > 60) ||
+ (leitch->second > 60))
+ return(0);
+ return(1);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_local.c b/usr.sbin/xntpd/xntpd/refclock_local.c
new file mode 100644
index 0000000..552e712
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_local.c
@@ -0,0 +1,170 @@
+/*
+ * refclock_local - local pseudo-clock driver
+ */
+#if defined(REFCLOCK) && defined(LOCAL_CLOCK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This is a hack to allow a machine to use its own system clock as a
+ * reference clock, i.e., to free-run using no outside clock discipline
+ * source. This is useful if you want to use NTP in an isolated
+ * environment with no radio clock or NIST modem available. Pick a
+ * machine that you figure has a good clock oscillator and configure it
+ * with this driver. Set the clock using the best means available, like
+ * eyeball-and-wristwatch. Then, point all the other machines at this
+ * one or use broadcast (not multicast) mode to distribute time.
+ *
+ * Another application for this driver is if you want to use a
+ * particular server's clock as the clock of last resort when all other
+ * normal synchronization sources have gone away. This is especially
+ * useful if that server has an ovenized oscillator. For this you would
+ * configure this driver at a higher stratum (say 3 or 4) to prevent the
+ * server's stratum from falling below that.
+ *
+ * A third application for this driver is when an external discipline
+ * source is available, such as the NIST "lockclock" program, which
+ * synchronizes the local clock via a telephone modem and the NIST
+ * Automated Computer Time Service (ACTS), or the Digital Time
+ * Synchronization Service (DTSS), which runs on DCE machines. In this
+ * case the stratum should be set at zero, indicating a bona fide
+ * stratum-1 source. Exercise some caution with this, since there is no
+ * easy way to telegraph via NTP that something might be wrong in the
+ * discipline source itself. In the case of DTSS, the local clock can
+ * have a rather large jitter, depending on the interval between
+ * corrections and the intrinsic frequency error of the clock
+ * oscillator. In extreme cases, this can cause clients to exceed the
+ * 128-ms slew window and drop off the NTP subnet.
+ *
+ * In the default mode the behavior of the clock selection algorithm is
+ * modified when this driver is in use. The algorithm is designed so
+ * that this driver will never be selected unless no other discipline
+ * source is available. This can be overriden with the prefer keyword of
+ * the server configuration command, in which case only this driver will
+ * be selected for synchronization and all other discipline sources will
+ * be ignored. This behavior is intended for use when an external
+ * discipline source controls the system clock.
+ *
+ * Fudge Factors
+ *
+ * The stratum for this driver LCLSTRATUM is set at 3 by default, but
+ * can be changed by the fudge command and/or the xntpdc utility. The
+ * reference ID is "LCL" by default, but can be changed using the same
+ * mechanisms. *NEVER* configure this driver to operate at a stratum
+ * which might possibly disrupt a client with access to a bona fide
+ * primary server, unless athe local clock oscillator is reliably
+ * disciplined by another source. *NEVER NEVER* configure a server which
+ * might devolve to an undisciplined local clock to use multicast mode.
+ *
+ * This driver provides a mechanism to trim the local clock in both time
+ * and frequency, as well as a way to manipulate the leap bits. The
+ * fudge time1 parameter adjusts the time, in seconds, and the fudge
+ * time2 parameter adjusts the frequency, in ppm. Both parameters are
+ * additive; that is, they add increments in time or frequency to the
+ * present values. The fudge flag1 and fudge flag2 bits set the
+ * corresponding leap bits; for example, setting flag1 causes a leap
+ * second to be added at the end of the UTC day. These bits are not
+ * reset automatically when the leap takes place; they must be turned
+ * off manually after the leap event.
+ */
+
+/*
+ * Local interface definitions
+ */
+#define PRECISION (-7) /* about 10 ms precision */
+#define REFID "LCL\0" /* reference ID */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+
+#define STRATUM 3 /* default stratum */
+#define DISPERSION (FP_SECOND / 100) /* default dispersion (10 ms) */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_proto
+ */
+extern s_char sys_precision;
+
+/*
+ * Function prototypes
+ */
+static int local_start P((int, struct peer *));
+static void local_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_local = {
+ local_start, /* start up driver */
+ noentry, /* shut down driver (not used) */
+ local_poll, /* transmit poll message */
+ noentry, /* not used (old lcl_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old lcl_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * local_start - start up the clock
+ */
+static int
+local_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = sys_precision;
+ pp->clockdesc = DESCRIPTION;
+ peer->stratum = STRATUM;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ */
+static void
+local_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ pp->polls++;
+ pp->lasttime = current_time;
+
+ /*
+ * Ramble through the usual filtering and grooming code, which
+ * is essentially a no-op and included mostly for pretty
+ * billboards. We fudge flags as the leap indicators and allow a
+ * one-time adjustment in time using fudge time1 (s) and
+ * frequency using fudge time 2 (ppm).
+ */
+ pp->dispersion = DISPERSION;
+ gettstamp(&pp->lastrec);
+ refclock_receive(peer, &pp->fudgetime1, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->sloppyclockflag);
+ adj_frequency(LFPTOFP(&pp->fudgetime2));
+ L_CLR(&pp->fudgetime1);
+ L_CLR(&pp->fudgetime2);
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/refclock_moto.c b/usr.sbin/xntpd/xntpd/refclock_moto.c
new file mode 100644
index 0000000..2e888bc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_moto.c
@@ -0,0 +1,2 @@
+#if defined(REFCLOCK) && defined(NMEA)
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_msfees.c b/usr.sbin/xntpd/xntpd/refclock_msfees.c
new file mode 100644
index 0000000..17e4235
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_msfees.c
@@ -0,0 +1,1557 @@
+/* refclock_ees - clock driver for the EES M201 receiver */
+
+#if defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM)
+
+/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
+ * were removed as the code was overly hairy, they weren't in use
+ * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
+ */
+
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+#include <termios.h>
+#include <stropts.h>
+#include <sys/ppsclock.h>
+#include "ntp_stdlib.h"
+
+ /*
+ fudgefactor = fudgetime1;
+ os_delay = fudgetime2;
+ offset_fudge = os_delay + fudgefactor + inherent_delay;
+ stratumtouse = fudgeval1 & 0xf
+ debug = fudgeval2;
+ sloppyclockflag = flags & CLK_FLAG1;
+ 1 log smoothing summary when processing sample
+ 4 dump the buffer from the clock
+ 8 EIOGETKD the last n uS time stamps
+ if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
+ ees->dump_vals = flags & CLK_FLAG3;
+ ees->usealldata = flags & CLK_FLAG4;
+
+
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10]= ees->yearstart, 0;
+ */
+
+/* This should support the use of an EES M201 receiver with RS232
+ * output (modified to transmit time once per second).
+ *
+ * For the format of the message sent by the clock, see the EESM_
+ * definitions below.
+ *
+ * It appears to run free for an integral number of minutes, until the error
+ * reaches 4mS, at which point it steps at second = 01.
+ * It appears that sometimes it steps 4mS (say at 7 min interval),
+ * then the next minute it decides that it was an error, so steps back.
+ * On the next minute it steps forward again :-(
+ * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
+ * or 9.5uS/S then 3990.5uS at a 7min re-sync,
+ * at which point it may loose the "00" second time stamp.
+ * I assume that the most accurate time is just AFTER the re-sync.
+ * Hence remember the last cycle interval,
+ *
+ * Can run in any one of:
+ *
+ * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
+ * (sun) *in the interupt code*, so as to avoid problems with
+ * the STREAMS scheduling.
+ *
+ * It appears that it goes 16.5 uS slow each second, then every 4 mins it
+ * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
+ */
+
+/* Definitions */
+#ifndef MAXUNITS
+#define MAXUNITS 4 /* maximum number of EES units permitted */
+#endif
+
+#ifndef EES232
+#define EES232 "/dev/ees%d" /* Device to open to read the data */
+#endif
+
+/* Other constant stuff */
+#ifndef EESPRECISION
+#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
+#endif
+#ifndef EESREFID
+#define EESREFID "MSF\0" /* String to identify the clock */
+#endif
+#ifndef EESHSREFID
+#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
+#endif
+
+/* Description of clock */
+#define EESDESCRIPTION "EES M201 MSF Receiver"
+
+/* Speed we run the clock port at. If this is changed the UARTDELAY
+ * value should be recomputed to suit.
+ */
+#ifndef SPEED232
+#define SPEED232 B9600 /* 9600 baud */
+#endif
+
+/* What is the inherent delay for this mode of working, i.e. when is the
+ * data time stamped.
+ */
+#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
+#define BITS_TO_L_FP(bits, baud) \
+ (((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
+#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
+#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
+
+#ifndef STREAM_PP1
+#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
+#endif
+#ifndef STREAM_PP2
+#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
+#endif
+
+/* Offsets of the bytes of the serial line code. The clock gives
+ * local time with a GMT/BST indication. The EESM_ definitions
+ * give offsets into ees->lastcode.
+ */
+#define EESM_CSEC 0 /* centiseconds - always zero in our clock */
+#define EESM_SEC 1 /* seconds in BCD */
+#define EESM_MIN 2 /* minutes in BCD */
+#define EESM_HOUR 3 /* hours in BCD */
+#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
+#define EESM_DAY 5 /* day of month in BCD */
+#define EESM_MON 6 /* month in BCD */
+#define EESM_YEAR 7 /* year MOD 100 in BCD */
+#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
+#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
+#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
+ /* followed by a frame alignment byte (0xff) /
+ / which is not put into the lastcode buffer*/
+
+/* Length of the serial time code, in characters. The first length
+ * is less the frame alignment byte.
+ */
+#define LENEESPRT (EESM_MSFOK+1)
+#define LENEESCODE (LENEESPRT+1)
+
+/* Code state. */
+#define EESCS_WAIT 0 /* waiting for start of timecode */
+#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
+
+/* Default fudge factor and character to receive */
+#define DEFFUDGETIME 0 /* Default user supplied fudge factor */
+#ifndef DEFOSTIME
+#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
+#endif
+#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
+
+/* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
+ * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
+ * as the estimated offset, otherwise average the remainder.
+ */
+#define FULLSHIFT 6 /* NCODES root 2 */
+#define NCODES (1<< FULLSHIFT) /* 64 */
+#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
+
+/* Towards the high ( Why ?) end of half */
+#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
+
+/* Leap hold time. After a leap second the clock will no longer be
+ * reliable until it resynchronizes. Hope 40 minutes is enough. */
+#define EESLEAPHOLD (40 * 60)
+
+#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
+#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
+#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
+#define EES_STEP_NOTES 50 /* Only do a limited number */
+#define MAX_STEP 16 /* Max number of steps to remember */
+
+/* debug is a bit mask of debugging that is wanted */
+#define DB_SYSLOG_SMPLI 0x0001
+#define DB_SYSLOG_SMPLE 0x0002
+#define DB_SYSLOG_SMTHI 0x0004
+#define DB_SYSLOG_NSMTHE 0x0008
+#define DB_SYSLOG_NSMTHI 0x0010
+#define DB_SYSLOG_SMTHE 0x0020
+#define DB_PRINT_EV 0x0040
+#define DB_PRINT_CDT 0x0080
+#define DB_PRINT_CDTC 0x0100
+#define DB_SYSLOG_KEEPD 0x0800
+#define DB_SYSLOG_KEEPE 0x1000
+#define DB_LOG_DELTAS 0x2000
+#define DB_PRINT_DELTAS 0x4000
+#define DB_LOG_AWAITMORE 0x8000
+#define DB_LOG_SAMPLES 0x10000
+#define DB_NO_PPS 0x20000
+#define DB_INC_PPS 0x40000
+#define DB_DUMP_DELTAS 0x80000
+
+struct eesunit { /* EES unit control structure. */
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp reftime; /* reference time */
+ l_fp lastsampletime; /* time as in txt from last EES msg */
+ l_fp arrvtime; /* Time at which pkt arrived */
+ l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */
+ l_fp offset; /* chosen offset (for clkbug) */
+ l_fp lowoffset; /* lowest sample offset (for clkbug) */
+ l_fp highoffset; /* highest " " (for clkbug) */
+ char lastcode[LENEESCODE+6]; /* last time code we received */
+ u_long lasttime; /* last time clock heard from */
+ u_long clocklastgood; /* last time good radio seen */
+ u_char lencode; /* length of code in buffer */
+ u_char nsamples; /* number of samples we've collected */
+ u_char codestate; /* state of 232 code reception */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ char tz; /* timezone from clock */
+ u_char ttytype; /* method used */
+ u_char dump_vals; /* Should clock values be dumped */
+ u_char usealldata; /* Use ALL samples */
+ u_short day; /* day of year from last code */
+ u_long yearstart; /* start of current year */
+ u_long leaphold; /* time of leap hold expiry */
+ u_long badformat; /* number of bad format codes */
+ u_long baddata; /* number of invalid time codes */
+ u_long timestarted; /* time we started this */
+ long last_pps_no; /* The serial # of the last PPS */
+ char fix_pending; /* Is a "sync to time" pending ? */
+ /* Fine tuning - compensate for 4 mS ramping .... */
+ l_fp last_l; /* last time stamp */
+ u_char last_steps[MAX_STEP]; /* Most recent n steps */
+ int best_av_step; /* Best guess at average step */
+ char best_av_step_count; /* # of steps over used above */
+ char this_step; /* Current pos in buffer */
+ int last_step_late; /* How late the last step was (0-59) */
+ long jump_fsecs; /* # of fractions of a sec last jump */
+ u_long last_step; /* time of last step */
+ int last_step_secs; /* Number of seconds in last step */
+ int using_ramp; /* 1 -> noemal, -1 -> over stepped */
+};
+#define last_sec last_l.l_ui
+#define last_sfsec last_l.l_f
+#define this_uisec ((ees->arrvtime).l_ui)
+#define this_sfsec ((ees->arrvtime).l_f)
+#define msec(x) ((x) / (1<<22))
+#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
+#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
+
+/* Bitmask for what methods to try to use -- currently only PPS enabled */
+#define T_CBREAK 1
+#define T_PPS 8
+/* macros to test above */
+#define is_cbreak(x) ((x)->ttytype & T_CBREAK)
+#define is_pps(x) ((x)->ttytype & T_PPS)
+#define is_any(x) ((x)->ttytype)
+
+#define CODEREASON 20 /* reason codes */
+
+/* Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back. */
+static struct eesunit *eesunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/* Keep the fudge factors separately so they can be set even
+ * when no clock is configured. */
+static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */
+static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */
+static l_fp os_delay[MAXUNITS]; /* fudgetime2 */
+static l_fp offset_fudge[MAXUNITS]; /* Sum of above */
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+static int deltas[60];
+
+static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
+static l_fp onesec; /* = { 1, 0 }; */
+
+/* Imported from the timer module */
+extern u_long current_time;
+
+extern s_char sys_precision;
+
+#ifdef DEBUG
+static int debug;
+#endif
+
+#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
+#define DUMP_BUF_SIZE 10112
+#endif
+
+/* ees_reset - reset the count back to zero */
+#define ees_reset(ees) (ees)->nsamples = 0; \
+ (ees)->codestate = EESCS_WAIT
+
+/* ees_event - record and report an event */
+#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
+ ees_report_event((ees), (evcode))
+
+/* Find the precision of the system clock by reading it */
+#define USECS 1000000
+#define MINSTEP 5 /* some systems increment uS on each call */
+#define MAXLOOPS (USECS/9)
+
+static void dump_buf(coffs, from, to, text)
+l_fp *coffs;
+int from;
+int to;
+char *text;
+{
+ char buff[DUMP_BUF_SIZE + 80];
+ int i;
+ register char *ptr = buff;
+ sprintf(ptr, text);
+ for (i=from; i<to; i++)
+ { while (*ptr) ptr++;
+ if ((ptr-buff) > DUMP_BUF_SIZE) syslog(LOG_DEBUG, "D: %s", ptr=buff);
+ sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
+ }
+ syslog(LOG_DEBUG, "D: %s", buff);
+}
+
+/* msfees_init - initialize internal ees driver data */
+static void msfees_init()
+{
+ register int i;
+ /* Just zero the data arrays */
+ memset((char *)eesunits, 0, sizeof eesunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ acceptable_slop.l_ui = 0;
+ acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
+
+ onesec.l_ui = 1;
+ onesec.l_uf = 0;
+
+ /* Initialize fudge factors to default. */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ os_delay[i].l_ui = 0;
+ os_delay[i].l_uf = DEFOSTIME;
+ inherent_delay[i].l_ui = 0;
+ inherent_delay[i].l_uf = DEFINHTIME;
+ offset_fudge[i] = os_delay[i];
+ L_ADD(&offset_fudge[i], &fudgefactor[i]);
+ L_ADD(&offset_fudge[i], &inherent_delay[i]);
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+
+/* msfees_start - open the EES devices and initialize data for processing */
+static int msfees_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct eesunit *ees;
+ register int i;
+ int fd232 = -1;
+ char eesdev[20];
+ struct termios ttyb, *ttyp;
+ static void ees_receive();
+ extern int io_addclock();
+ extern void io_closeclock();
+ extern char *emalloc();
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "ees clock: unit number %d in use", unit);
+ return 0;
+ }
+
+ /* Unit okay, attempt to open the devices. We do them both at
+ * once to make sure we can */
+ (void) sprintf(eesdev, EES232, unit);
+
+ fd232 = open(eesdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
+ return 0;
+ }
+
+#ifdef TIOCEXCL
+ /* Set for exclusive use */
+ if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
+ goto screwed;
+ }
+#endif
+
+ /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ /* Set port characteristics. If we don't have a STREAMS module or
+ * a clock line discipline, cooked mode is just usable, even though it
+ * strips the top bit. The only EES byte which uses the top
+ * bit is the year, and we don't use that anyway. If we do
+ * have the line discipline, we choose raw mode, and the
+ * line discipline code will block up the messages.
+ */
+
+ /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_oflag = 0;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ inherent_delay[unit].l_uf = INH_DELAY_PPS;
+
+ /* offset fudge (how *late* the timestamp is) = fudge + os delays */
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+
+ /* Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc() one.
+ */
+ if (eesunits[unit] != 0) /* The one we want is okay */
+ ees = eesunits[unit];
+ else {
+ /* Look for an unused, but allocated struct */
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && eesunits[i] != 0)
+ break;
+ }
+
+ if (i < MAXUNITS) { /* Reclaim this one */
+ ees = eesunits[i];
+ eesunits[i] = 0;
+ } /* no spare -- make a new one */
+ else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
+ }
+ memset((char *)ees, 0, sizeof(struct eesunit));
+ eesunits[unit] = ees;
+
+ /* Set up the structures */
+ ees->peer = peer;
+ ees->unit = (u_char)unit;
+ ees->timestarted= current_time;
+ ees->ttytype = 0;
+ ees->io.clock_recv= ees_receive;
+ ees->io.srcclock= (caddr_t)ees;
+ ees->io.datalen = 0;
+ ees->io.fd = fd232;
+
+ /* Okay. Push one of the two (linked into the kernel, or dynamically
+ * loaded) STREAMS module, and give it to the I/O code to start
+ * receiving stuff.
+ */
+
+ {
+ int rc1;
+ /* Pop any existing onews first ... */
+ while (ioctl(fd232, I_POP, 0 ) >= 0) ;
+
+ /* Now try pushing either of the possible modules */
+ if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
+ ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
+ syslog(LOG_ERR,
+ "ees clock: Push of `%s' and `%s' to %s failed %m",
+ STREAM_PP1, STREAM_PP2, eesdev);
+ goto screwed;
+ }
+ else {
+ syslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
+ (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
+ ees->ttytype |= T_PPS;
+ }
+ }
+
+ /* Add the clock */
+ if (!io_addclock(&ees->io)) {
+ /* Oh shit. Just close and return. */
+ syslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
+ goto screwed;
+ }
+
+
+ /* All done. Initialize a few random peer variables, then
+ * return success. */
+ peer->precision = sys_precision;
+ peer->stratum = stratumtouse[unit];
+ peer->rootdelay = 0; /* ++++ */
+ peer->rootdispersion = 0; /* ++++ */
+ if (stratumtouse[unit] <= 1) {
+ memcpy((char *)&pp->refid, EESREFID, 4);
+ if (unit > 0 && unit < 10)
+ ((char *)&pp->refid)[3] = '0' + unit;
+ } else {
+ peer->refid = htonl(EESHSREFID);
+ }
+ unitinuse[unit] = 1;
+ pp->unitptr = (caddr_t) &eesunits[unit];
+ pp->clockdesc = EESDESCRIPTION;
+ pp->nstages = MAXSTAGE;
+ syslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
+ return (1);
+
+screwed:
+ if (fd232 != -1)
+ (void) close(fd232);
+ return (0);
+}
+
+
+/* msfees_shutdown - shut down a EES clock */
+static void msfees_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct eesunit *ees;
+ extern void io_closeclock();
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
+ unit, MAXUNITS);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
+ return;
+ }
+
+ /* Tell the I/O module to turn us off. We're history. */
+ ees = eesunits[unit];
+ io_closeclock(&ees->io);
+ unitinuse[unit] = 0;
+}
+
+
+/* ees_report_event - note the occurance of an event */
+static void ees_report_event(ees, code)
+ struct eesunit *ees;
+ int code;
+{
+ if (ees->status != (u_char)code) {
+ ees->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ ees->lastevent = (u_char)code;
+ /* Should report event to trap handler in here.
+ * Soon...
+ */
+ }
+}
+
+
+/* ees_receive - receive data from the serial interface on an EES clock */
+static void ees_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int n_sample;
+ register int day;
+ register struct eesunit *ees;
+ register u_char *dpt; /* Data PoinTeR: move along ... */
+ register u_char *dpend; /* Points just *after* last data char */
+ register char *cp;
+ l_fp tmp;
+ static void ees_process();
+ int call_pps_sample = 0;
+ l_fp pps_arrvstamp;
+ int sincelast;
+ int pps_step = 0;
+ int suspect_4ms_step = 0;
+ struct ppsclockev ppsclockev;
+ long *ptr = (long *) &ppsclockev;
+ extern errno;
+ int rc;
+
+ /* Get the clock this applies to and a pointer to the data */
+ ees = (struct eesunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ if ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
+ printf("[%d] ", rbufp->recv_length);
+
+ /* Check out our state and process appropriately */
+ switch (ees->codestate) {
+ case EESCS_WAIT:
+ /* Set an initial guess at the timestamp as the recv time.
+ * If just running in CBREAK mode, we can't improve this.
+ * If we have the CLOCK Line Discipline, PPSCD, or sime such,
+ * then we will do better later ....
+ */
+ ees->arrvtime = rbufp->recv_time;
+ ees->codestate = EESCS_GOTSOME;
+ ees->lencode = 0;
+ /*FALLSTHROUGH*/
+
+ case EESCS_GOTSOME:
+ cp = &(ees->lastcode[ees->lencode]);
+
+ /* Gobble the bytes until the final (possibly stripped) 0xff */
+ while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
+ *cp++ = (char)*dpt++;
+ ees->lencode++;
+ /* Oh dear -- too many bytes .. */
+ if (ees->lencode > LENEESPRT) {
+ syslog(LOG_INFO,
+"I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
+ ees->lencode, dpend - dpt, LENEESPRT,
+#define D(x) (ees->lastcode[x])
+ D(0), D(1), D(2), D(3), D(4), D(5), D(6),
+ D(7), D(8), D(9), D(10), D(11), D(12));
+#undef D
+ ees->badformat++;
+ ees->reason = CODEREASON + 1;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+ }
+ /* Gave up because it was end of the buffer, rather than ff */
+ if (dpt == dpend) {
+ /* Incomplete. Wait for more. */
+ if (debug & DB_LOG_AWAITMORE) syslog(LOG_INFO,
+ "I: ees clock %d: %d == %d: await more",
+ ees->unit, dpt, dpend);
+ return;
+ }
+
+ /* This shouldn't happen ... ! */
+ if ((*dpt & 0x7f) != 0x7f) {
+ syslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
+ ees->badformat++;
+ ees->reason = CODEREASON + 2;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Skip the 0xff */
+ dpt++;
+
+ /* Finally, got a complete buffer. Mainline code will
+ * continue on. */
+ cp = ees->lastcode;
+ break;
+
+ default:
+ syslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
+ ees->unit, ees->codestate);
+ ees->reason = CODEREASON + 5;
+ ees_event(ees, CEVNT_FAULT);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Boy! After all that crap, the lastcode buffer now contains
+ * something we hope will be a valid time code. Do length
+ * checks and sanity checks on constant data.
+ */
+ ees->codestate = EESCS_WAIT;
+ ees->lasttime = current_time;
+ if (ees->lencode != LENEESPRT) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 6;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ cp = ees->lastcode;
+
+ /* Check that centisecond is zero */
+ if (cp[EESM_CSEC] != 0) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 7;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Check flag formats */
+ if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 8;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 9;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 10;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* So far, so good. Compute day, hours, minutes, seconds,
+ * time zone. Do range checks on these.
+ */
+
+#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
+#define istrue(x) ((x)?1:0)
+
+ ees->second = bcdunpack(cp[EESM_SEC]); /* second */
+ ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
+ ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
+
+ day = bcdunpack(cp[EESM_DAY]); /* day of month */
+
+ switch (bcdunpack(cp[EESM_MON])) { /* month */
+
+ /* Add in lengths of all previous months. Add one more
+ if it is a leap year and after February.
+ */
+ case 12: day += NOV; /*FALLSTHROUGH*/
+ case 11: day += OCT; /*FALLSTHROUGH*/
+ case 10: day += SEP; /*FALLSTHROUGH*/
+ case 9: day += AUG; /*FALLSTHROUGH*/
+ case 8: day += JUL; /*FALLSTHROUGH*/
+ case 7: day += JUN; /*FALLSTHROUGH*/
+ case 6: day += MAY; /*FALLSTHROUGH*/
+ case 5: day += APR; /*FALLSTHROUGH*/
+ case 4: day += MAR; /*FALLSTHROUGH*/
+ case 3: day += FEB;
+ if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
+ case 2: day += JAN; /*FALLSTHROUGH*/
+ case 1: break;
+ default: ees->baddata++;
+ ees->reason = CODEREASON + 11;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ ees->day = day;
+
+ /* Get timezone. The clocktime routine wants the number
+ * of hours to add to the delivered time to get UT.
+ * Currently -1 if BST flag set, 0 otherwise. This
+ * is the place to tweak things if double summer time
+ * ever happens.
+ */
+ ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
+
+ if (ees->day > 366 || ees->day < 1 ||
+ ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 12;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ n_sample = ees->nsamples;
+
+ /* Now, compute the reference time value: text -> tmp.l_ui */
+ if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
+ ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
+ &tmp.l_ui)) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 13;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+ tmp.l_uf = 0;
+
+ /* DON'T use ees->arrvtime -- it may be < reftime */
+ ees->lastsampletime = tmp;
+
+ /* If we are synchronised to the radio, update the reference time.
+ * Also keep a note of when clock was last good.
+ */
+ if (istrue(cp[EESM_MSFOK])) {
+ ees->reftime = tmp;
+ ees->clocklastgood = current_time;
+ }
+
+
+ /* Compute the offset. For the fractional part of the
+ * offset we use the expected delay for the message.
+ */
+ ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
+ ees->codeoffsets[n_sample].l_uf = 0;
+
+ /* Number of seconds since the last step */
+ sincelast = this_uisec - ees->last_step;
+
+ memset((char *) &ppsclockev, 0, sizeof ppsclockev);
+
+ rc = ioctl(ees->io.fd, CIOGETEV, (char *) &ppsclockev);
+ if (debug & DB_PRINT_EV) fprintf(stderr,
+ "[%x] CIOGETEV u%d %d (%lx %d) gave %d (%d): %08lx %08lx %ld\n",
+ DB_PRINT_EV, ees->unit, ees->io.fd, CIOGETEV, is_pps(ees),
+ rc, errno, ptr[0], ptr[1], ptr[2]);
+
+ /* If we managed to get the time of arrival, process the info */
+ if (rc >= 0) {
+ int conv = -1;
+ pps_step = ppsclockev.serial - ees->last_pps_no;
+
+ /* Possible that PPS triggered, but text message didn't */
+ if (pps_step == 2) syslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
+ if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
+ if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
+
+ /* allow for single loss of PPS only */
+ if (pps_step != 1 && pps_step != 2)
+ fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
+ ppsclockev.serial, ees->last_pps_no, pps_step);
+ else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
+ fprintf(stderr, "buftvtots failed\n");
+ else { /* if ((ABS(time difference) - 0.25) < 0)
+ * then believe it ...
+ */
+ l_fp diff;
+ diff = pps_arrvstamp;
+ conv = 0;
+ L_SUB(&diff, &ees->arrvtime);
+if (debug & DB_PRINT_CDT) printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
+ DB_PRINT_CDT, ees->arrvtime.l_ui, ees->arrvtime.l_uf,
+ pps_arrvstamp.l_ui, pps_arrvstamp.l_uf,
+ diff.l_ui, diff.l_uf,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ ees->arrvtime = pps_arrvstamp;
+ conv++;
+ call_pps_sample++;
+ }
+ /* Some loss of some signals around sec = 1 */
+ else if (ees->second == 1) {
+ diff = pps_arrvstamp;
+ L_ADD(&diff, &onesec);
+ L_SUB(&diff, &ees->arrvtime);
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+syslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
+ pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
+ pps_arrvstamp.l_uf,
+ ees->arrvtime.l_uf,
+ diff.l_ui, diff.l_uf,
+ ppsclockev.tv.tv_usec,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ suspect_4ms_step |= 2;
+ ees->arrvtime = pps_arrvstamp;
+ L_ADD(&ees->arrvtime, &onesec);
+ conv++;
+ call_pps_sample++;
+ }
+ }
+ }
+ ees->last_pps_no = ppsclockev.serial;
+ if (debug & DB_PRINT_CDTC) printf(
+ "[%x] %08lx %08lx %d u%d (%d %d)\n",
+ DB_PRINT_CDTC, pps_arrvstamp.l_ui,
+ pps_arrvstamp.l_uf, conv, ees->unit,
+ call_pps_sample, pps_step);
+ }
+
+ /* See if there has been a 4ms jump at a minute boundry */
+ { l_fp delta;
+#define delta_isec delta.l_ui
+#define delta_ssec delta.l_i
+#define delta_sfsec delta.l_f
+ long delta_f_abs;
+
+ delta.l_i = ees->arrvtime.l_i;
+ delta.l_f = ees->arrvtime.l_f;
+
+ L_SUB(&delta, &ees->last_l);
+ delta_f_abs = delta_sfsec;
+ if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
+
+ /* Dump the deltas each minute */
+ if (debug & DB_DUMP_DELTAS)
+ { if (/*0 <= ees->second && */
+ ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
+ /* Dump on second 1, as second 0 sometimes missed */
+ if (ees->second == 1) {
+ char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
+ char *ptr=text;
+ int i;
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
+ sprintf(ptr, " %d.%04d",
+ msec(deltas[i]), subms(deltas[i]));
+ while (*ptr) ptr++;
+ }
+ syslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
+ msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
+ text+1);
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
+ }
+ }
+
+ /* Lets see if we have a 4 mS step at a minute boundaary */
+ if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
+ (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
+ (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
+ (sincelast < 0 || sincelast > 122)
+ ) { /* 4ms jump at min boundry */
+ int old_sincelast;
+ int count=0;
+ int sum = 0;
+ /* Yes -- so compute the ramp time */
+ if (ees->last_step == 0) sincelast = 0;
+ old_sincelast = sincelast;
+
+ /* First time in, just set "ees->last_step" */
+ if(ees->last_step) {
+ int other_step = 0;
+ int third_step = 0;
+ int this_step = (sincelast + (60 /2)) / 60;
+ int p_step = ees->this_step;
+ int p;
+ ees->last_steps[p_step] = this_step;
+ p= p_step;
+ p_step++;
+ if (p_step >= LAST_STEPS) p_step = 0;
+ ees->this_step = p_step;
+ /* Find the "average" interval */
+ while (p != p_step) {
+ int this = ees->last_steps[p];
+ if (this == 0) break;
+ if (this != this_step) {
+ if (other_step == 0 && (
+ this== (this_step +2) ||
+ this== (this_step -2) ||
+ this== (this_step +1) ||
+ this== (this_step -1)))
+ other_step = this;
+ if (other_step != this) {
+ int delta = (this_step - other_step);
+ if (delta < 0) delta = - delta;
+ if (third_step == 0 && (
+ (delta == 1) ? (
+ this == (other_step +1) ||
+ this == (other_step -1) ||
+ this == (this_step +1) ||
+ this == (this_step -1))
+ :
+ (
+ this == (this_step + other_step)/2
+ )
+ )) third_step = this;
+ if (third_step != this) break;
+ }
+ }
+ sum += this;
+ p--;
+ if (p < 0) p += LAST_STEPS;
+ count++;
+ }
+syslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
+ if (count != 0) sum = ((sum * 60) + (count /2)) / count;
+#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
+syslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+#undef SV
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ if (sincelast > 170)
+ ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
+ else ees->last_step_late = 30;
+ if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
+ if (ees->last_step_late < 0) ees->last_step_late = 0;
+ if (ees->last_step_late >= 60) ees->last_step_late = 59;
+ sincelast = 0;
+ }
+ else { /* First time in -- just save info */
+ ees->last_step_late = 30;
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ sum = 4 * 60;
+ }
+ ees->last_step = this_uisec;
+printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+syslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+ if (sum) ees->last_step_secs = sum;
+ }
+ /* OK, so not a 4ms step at a minute boundry */
+ else {
+ if (suspect_4ms_step) syslog(LOG_ERR,
+ "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
+ ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
+ msec(EES_STEP_F - EES_STEP_F_GRACE),
+ subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(delta_f_abs),
+ subms(delta_f_abs),
+ msec(EES_STEP_F + EES_STEP_F_GRACE),
+ subms(EES_STEP_F + EES_STEP_F_GRACE),
+ ees->second,
+ sincelast);
+ if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
+ static ees_step_notes = EES_STEP_NOTES;
+ if (ees_step_notes > 0) {
+ ees_step_notes--;
+printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
+syslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
+ }
+ }
+ }
+ }
+ ees->last_l = ees->arrvtime;
+
+ /* IF we have found that it's ramping
+ * && it's within twice the expected ramp period
+ * && there is a non zero step size (avoid /0 !)
+ * THEN we twiddle things
+ */
+ if (ees->using_ramp &&
+ sincelast < (ees->last_step_secs)*2 &&
+ ees->last_step_secs)
+ { long sec_of_ramp = sincelast + ees->last_step_late;
+ long fsecs;
+ l_fp inc;
+
+ /* Ramp time may vary, so may ramp for longer than last time */
+ if (sec_of_ramp > (ees->last_step_secs + 120))
+ sec_of_ramp = ees->last_step_secs;
+
+ /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
+ fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
+
+ if (debug & DB_LOG_DELTAS) syslog(LOG_ERR,
+ "[%x] MSF%d: %3d/%03d -> d=%11d (%d|%d)",
+ DB_LOG_DELTAS,
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+
+ /* Must sign extend the result */
+ inc.l_i = (fsecs < 0) ? -1 : 0;
+ inc.l_f = fsecs;
+ if (debug & DB_INC_PPS)
+ { L_SUB(&pps_arrvstamp, &inc);
+ L_SUB(&ees->arrvtime, &inc);
+ }
+ else
+ { L_ADD(&pps_arrvstamp, &inc);
+ L_ADD(&ees->arrvtime, &inc);
+ }
+ }
+ else {
+ if (debug & DB_LOG_DELTAS) syslog(LOG_ERR,
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ }
+
+ L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
+ L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
+
+ if (call_pps_sample && !(debug & DB_NO_PPS)) {
+ /* Sigh -- it expects its args negated */
+ L_NEG(&pps_arrvstamp);
+ (void) pps_sample(&pps_arrvstamp);
+ }
+
+ /* Subtract off the local clock time stamp */
+ L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
+ if (debug & DB_LOG_SAMPLES) syslog(LOG_ERR,
+ "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
+ ees->unit, DB_LOG_DELTAS, n_sample,
+ ees->codeoffsets[n_sample].l_f,
+ ees->codeoffsets[n_sample].l_f / 4295,
+ pps_arrvstamp.l_f,
+ pps_arrvstamp.l_f /4295,
+ (debug & DB_NO_PPS) ? " [no PPS]" : "");
+
+ if (ees->nsamples++ == NCODES-1) ees_process(ees);
+
+ /* Done! */
+}
+
+
+static void set_x(fp_offset)
+l_fp *fp_offset;
+{
+ step_systime_real(fp_offset);
+}
+
+
+/* offcompare - auxiliary comparison routine for offset sort */
+
+static int
+offcompare(a, b)
+l_fp *a, *b;
+{
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+
+
+/* ees_process - process a pile of samples from the clock */
+static void ees_process(ees)
+ struct eesunit *ees;
+{
+ static last_samples = -1;
+ register int i, j;
+ register int noff;
+ register l_fp *coffs = ees->codeoffsets;
+ l_fp offset, tmp;
+ u_fp dispersion; /* ++++ */
+ int lostsync, isinsync;
+ int samples = ees->nsamples;
+ int samplelog = 0; /* keep "gcc -Wall" happy ! */
+ int samplereduce = (samples + 1) / 2;
+
+ /* Reset things to zero so we don't have to worry later */
+ ees_reset(ees);
+
+ if (sloppyclockflag[ees->unit]) {
+ samplelog = (samples < 2) ? 0 :
+ (samples < 5) ? 1 :
+ (samples < 9) ? 2 :
+ (samples < 17) ? 3 :
+ (samples < 33) ? 4 : 5;
+ samplereduce = (1 << samplelog);
+ }
+
+ if (samples != last_samples &&
+ ((samples != (last_samples-1)) || samples < 3)) {
+ syslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
+ samples, last_samples, samplereduce);
+ last_samples = samples;
+ }
+ if (samples < 1) return;
+
+ /* If requested, dump the raw data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:");
+
+ /* Sort the offsets, trim off the extremes, then choose one. */
+ qsort((char *) coffs, samples, sizeof(l_fp), offcompare);
+
+ noff = samples;
+ i = 0;
+ while ((noff - i) > samplereduce) {
+ /* Trim off the sample which is further away
+ * from the median. We work this out by doubling
+ * the median, subtracting off the end samples, and
+ * looking at the sign of the answer, using the
+ * identity (c-b)-(b-a) == 2*b-a-c
+ */
+ tmp = coffs[(noff + i)/2];
+ L_ADD(&tmp, &tmp);
+ L_SUB(&tmp, &coffs[i]);
+ L_SUB(&tmp, &coffs[noff-1]);
+ if (L_ISNEG(&tmp)) noff--; else i++;
+ }
+
+ /* If requested, dump the reduce data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
+
+ /* What we do next depends on the setting of the sloppy clock flag.
+ * If it is on, average the remainder to derive our estimate.
+ * Otherwise, just pick a representative value from the remaining stuff
+ */
+ if (sloppyclockflag[ees->unit]) {
+ offset.l_ui = offset.l_uf = 0;
+ for (j = i; j < noff; j++)
+ L_ADD(&offset, &coffs[j]);
+ for (j = samplelog; j > 0; j--)
+ L_RSHIFTU(&offset);
+ }
+ else offset = coffs[i+BESTSAMPLE];
+
+ /* Compute the dispersion as the difference between the
+ * lowest and highest offsets that remain in the
+ * consideration list.
+ *
+ * It looks like MOST clocks have MOD (max error), so halve it !
+ */
+ tmp = coffs[noff-1];
+ L_SUB(&tmp, &coffs[i]);
+#define FRACT_SEC(n) ((1 << 30) / (n/2))
+ dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
+ if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) syslog(
+ (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Offset=%06d (%d), disp=%06d%s [%d], %d %d=%d %d:%d %d=%d %d",
+ debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
+ offset.l_f / 4295, offset.l_f,
+ (dispersion * 1526) / 100,
+ (sloppyclockflag[ees->unit]) ? " by averaging" : "",
+ FRACT_SEC(10) / 4295,
+ (coffs[0].l_f) / 4295,
+ i,
+ (coffs[i].l_f) / 4295,
+ (coffs[samples/2].l_f) / 4295,
+ (coffs[i+BESTSAMPLE].l_f) / 4295,
+ noff-1,
+ (coffs[noff-1].l_f) / 4295,
+ (coffs[samples-1].l_f) / 4295);
+
+ /* Are we playing silly wotsits ?
+ * If we are using all data, see if there is a "small" delta,
+ * and if so, blurr this with 3/4 of the delta from the last value
+ */
+ if (ees->usealldata && ees->offset.l_uf) {
+ long diff = (long) (ees->offset.l_uf - offset.l_uf);
+
+ /* is the delta small enough ? */
+ if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
+ int samd = (64 * 4) / samples;
+ long new;
+ if (samd < 2) samd = 2;
+ new = offset.l_uf + ((diff * (samd -1)) / samd);
+
+ /* Sign change -> need to fix up int part */
+ if ((new & (1 << 31)) !=
+ (((long) offset.l_uf) & ( 1 << 31)))
+ { syslog(LOG_INFO, "I: %x != %x (%x %x), so add %d",
+ new & (1 << 31),
+ ((long) offset.l_uf) & ( 1 << 31),
+ new, (long) offset.l_uf,
+ (new < 0) ? -1 : 1);
+ offset.l_ui += (new < 0) ? -1 : 1;
+ }
+ dispersion /= 4;
+ if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) syslog(
+ (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Smooth data: %d -> %d, dispersion now %d",
+ debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
+ ((long) offset.l_uf) / 4295, new / 4295,
+ (dispersion * 1526) / 100);
+ offset.l_uf = (unsigned long) new;
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "[%x] No smooth as delta not %d < %d < %d",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ - FRACT_SEC(100), diff, FRACT_SEC(100));
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
+ offset.l_f, ees->offset.l_f - offset.l_f);
+
+ /* Collect offset info for debugging info */
+ ees->offset = offset;
+ ees->lowoffset = coffs[i];
+ ees->highoffset = coffs[noff-1];
+
+ /* Determine synchronization status. Can be unsync'd either
+ * by a report from the clock or by a leap hold.
+ *
+ * Loss of the radio signal for a short time does not cause
+ * us to go unsynchronised, since the receiver keeps quite
+ * good time on its own. The spec says 20ms in 4 hours; the
+ * observed drift in our clock (Cambridge) is about a second
+ * a day, but even that keeps us within the inherent tolerance
+ * of the clock for about 15 minutes. Observation shows that
+ * the typical "short" outage is 3 minutes, so to allow us
+ * to ride out those, we will give it 5 minutes.
+ */
+ lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
+ isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
+
+ /* Done. Use time of last good, synchronised code as the
+ * reference time, and lastsampletime as the receive time.
+ */
+ if (ees->fix_pending) {
+ syslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
+ ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
+ ees->fix_pending = 0;
+ set_x(&offset);
+ L_CLR(&offset);
+ }
+ refclock_receive(ees->peer,
+ &offset,
+ 0, /* delay */
+ dispersion,
+ &ees->reftime,
+ &ees->lastsampletime, /* receive time */
+ (isinsync) ? 0 : LEAP_NOTINSYNC);
+ ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
+}
+
+/* msfees_poll - called by the transmit procedure */
+static void msfees_poll(unit, peer)
+ int unit;
+ char *peer;
+{
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
+ unit);
+ return;
+ }
+
+ ees_process(eesunits[unit]);
+
+ if ((current_time - eesunits[unit]->lasttime) > 150)
+ ees_event(eesunits[unit], CEVNT_FAULT);
+}
+
+/* msfees_leap - called when a leap second occurs */
+static void msfees_leap()
+{
+ register int i;
+
+ /* This routine should be entered a few seconds after
+ * midnight UTC when a leap second occurs. To ensure we
+ * don't believe foolish time from the clock(s) we set a
+ * 40 minute hold on them. It shouldn't take anywhere
+ * near this amount of time to adjust if the clock is getTING
+ * data, but doing anything else is complicated.
+ */
+ for (i = 0; i < MAXUNITS; i++) if (unitinuse[i])
+ eesunits[i]->leaphold = current_time + EESLEAPHOLD;
+}
+
+/* msfees_control - set fudge factors, return statistics */
+static void msfees_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct eesunit *ees = eesunits[unit];
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ os_delay[unit] = in->fudgetime2;
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+ if (in->haveflags & CLK_HAVEVAL1) {
+ stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
+ if (unitinuse[unit]) {
+ /* Should actually reselect clock, but
+ * will wait for the next timecode
+ */
+ struct peer *peer = ees->peer;
+ struct refclockproc *pp = peer->procptr;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1) {
+ memmove((char *)&pp->refid,
+ EESREFID, 4);
+ if (unit>0 && unit<10)
+ ((char *)&pp->refid)[3] =
+ '0' + unit;
+ }
+ else peer->refid = htonl(EESHSREFID);
+ }
+ }
+ if (in->haveflags & CLK_HAVEVAL2) {
+ printf("Debug: %x -> %lx\n", debug, in->fudgeval2);
+ syslog(LOG_ERR, "MSF%d: debug %x -> %x",
+ unit, debug, in->fudgeval2);
+ debug = in->fudgeval2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ sloppyclockflag[unit] = in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ ees->fix_pending++;
+ /* if (in->flags & CLK_FLAG2 && unitinuse[unit])
+ ees->leaphold = 0; */
+ }
+ if (in->haveflags & CLK_HAVEFLAG3 && unitinuse[unit]) {
+ printf("dump_vals: %x -> %x\n", ees->dump_vals, in->flags & CLK_FLAG3);
+ ees->dump_vals = in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4 && unitinuse[unit]) {
+ ees->usealldata = in->flags & CLK_FLAG4;
+ }
+ }
+
+ if (out != 0) {
+ struct peer *peer = ees->peer;
+ struct refclockproc *pp = peer->procptr;
+ out->type = REFCLK_MSF_EES;
+ out->haveflags
+ = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1|CLK_HAVEFLAG3|CLK_HAVEFLAG4;
+ out->clockdesc = pp->clockdesc;
+ out->fudgetime1 = fudgefactor[unit];
+ out->fudgetime2 = os_delay[unit];
+ out->fudgeval1 = (long)stratumtouse[unit];
+ /*out->fudgeval2= debug*/;
+ memmove((char *)&out->fudgeval2, (char *)&pp->refid, 4);
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ out->flags |= ees->dump_vals | ees->usealldata;
+ out->lencode = ees->lencode;
+ out->lastcode = ees->lastcode;
+ out->timereset = current_time - ees->timestarted;
+ out->polls = 0; /* we don't poll */
+ out->noresponse = 0; /* ditto */
+ out->badformat = ees->badformat;
+ out->baddata = ees->baddata;
+ out->lastevent = ees->lastevent;
+ out->currentstatus = ees->status;
+ } else {
+ out->lencode = 0;
+ out->lastcode = "";
+ out->polls = out->noresponse = 0;
+ out->badformat = out->baddata = 0;
+ out->timereset = 0;
+ out->currentstatus = out->lastevent = CEVNT_NOMINAL;
+ }
+ }
+}
+
+
+/* msfees_buginfo - return clock dependent debugging info */
+static void msfees_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct eesunit *ees;
+
+ bug->nvalues = bug->ntimes = 0;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ ees = eesunits[unit];
+
+ bug->nvalues = 16;
+ bug->svalues = 0x0800;
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->ntimes = 11;
+ bug->stimes = 0x3f8;
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10].l_ui = ees->yearstart;
+ bug->times[10].l_uf = 0;
+}
+
+struct refclock refclock_msfees = {
+ msfees_start, msfees_shutdown, msfees_poll,
+ msfees_control, msfees_init, msfees_buginfo, NOFLAGS
+};
+#endif /* defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) */
diff --git a/usr.sbin/xntpd/xntpd/refclock_mx4200.c b/usr.sbin/xntpd/xntpd/refclock_mx4200.c
new file mode 100644
index 0000000..577193d
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_mx4200.c
@@ -0,0 +1,1343 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. 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 BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(REFCLOCK) && defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS))
+
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#) /src/master/xntp-930612/xntpd/refclock_mx4200.c,v 1.5 1993/06/18 21:19:54 jbj Exp (LBL) ";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_unixtime.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(MX4200CLK)
+#include <sys/clkdefs.h>
+#endif /* MX4200CLK */
+#endif /* STREAM */
+
+#include <sys/ppsclock.h>
+
+#include "mx4200.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Magnavox Model MX4200 GPS Receiver.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 2 /* max number of mx4200 units */
+#define MX4200232 "/dev/gps%d"
+#define SPEED232 B4800 /* baud */
+
+/*
+ * The number of raw samples which we acquire to derive a single estimate.
+ */
+#define NSTAMPS 64
+
+/*
+ * Radio interface parameters
+ */
+#define MX4200PRECISION (-18) /* precision assumed (about 4 us) */
+#define MX4200REFID "GPS" /* reference id */
+#define MX4200DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+/* Leap stuff */
+extern U_LONG leap_hoursfromleap;
+extern U_LONG leap_happened;
+static int leap_debug;
+
+/*
+ * mx4200_reset - reset the count back to zero
+ */
+#define mx4200_reset(mx4200) \
+ do { \
+ (mx4200)->nsamples = 0; \
+ } while (0)
+
+/*
+ * mx4200_event - record and report an event
+ */
+#define mx4200_event(mx4200, evcode) \
+ do { \
+ if ((mx4200)->status != (u_char)(evcode)) \
+ mx4200_report_event((mx4200), (evcode)); \
+ } while (0)
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * MX4200 unit control structure.
+ */
+struct mx4200unit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ U_LONG gpssamples[NSTAMPS]; /* the GPS time samples */
+ l_fp unixsamples[NSTAMPS]; /* the UNIX time samples */
+
+
+ l_fp lastsampletime; /* time of last estimate */
+ u_int lastserial; /* last pps serial number */
+#ifdef notdef
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+#endif
+ char lastcode[RX_BUFF_SIZE]; /* last timecode received */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char nsamples; /* number of samples we've collected */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char lencode; /* length of last timecode */
+ u_char year; /* year of eternity */
+ u_short monthday; /* day of month */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ /*
+ * Status tallies
+ */
+#ifdef notdef
+ U_LONG polls; /* polls sent */
+ U_LONG noresponse; /* number of nonresponses */
+#endif
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * We demand that consecutive PPS samples are more than 0.995 seconds
+ * and less than 1.005 seconds apart.
+ */
+#define PPSLODIFF_UI 0 /* 0.900 as an l_fp */
+#define PPSLODIFF_UF 0xe6666610
+
+#define PPSHIDIFF_UI 1 /* 1.100 as an l_fp */
+#define PPSHIDIFF_UF 0x19999990
+
+/*
+ * reason codes
+ */
+#define PPSREASON 20
+#define CODEREASON 40
+#define PROCREASON 60
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct mx4200unit *mx4200units[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+static const char pmvxg[] = "PMVXG";
+
+/*
+ * Function prototypes
+ */
+static void mx4200_init P((void));
+static int mx4200_start P((int, struct peer *));
+static void mx4200_shutdown P((int, struct peer *));
+static void mx4200_receive P((struct recvbuf *));
+static void mx4200_process P((struct mx4200unit *));
+static void mx4200_report_event P((struct mx4200unit *, int));
+static void mx4200_poll P((int, struct peer *));
+static void mx4200_control P((int, struct refclockstat *, struct refclockstat *));
+static void mx4200_buginfo P((int, struct refclockbug *));
+
+static char * mx4200_parse P((char *, struct calendar *, int *, int *));
+static int mx4200_needconf P((char *));
+static void mx4200_config P((struct mx4200unit *));
+static void mx4200_send P((int, const char *, ...));
+static int mx4200_cmpl_fp P((void *, void *));
+static u_char cksum P((char *, u_int));
+
+#ifdef DEBUG
+static void opendfile P((int));
+static void checkdfile P((void));
+#endif /* DEBUG */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_mx4200 = {
+ mx4200_start, mx4200_shutdown, mx4200_poll,
+ mx4200_control, mx4200_init, mx4200_buginfo, NOFLAGS
+};
+
+/*
+ * mx4200_init - initialize internal mx4200 driver data
+ */
+static void
+mx4200_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)mx4200units, 0, sizeof mx4200units);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ memcpy((char *)&refid[i], MX4200REFID, 4);
+ }
+}
+
+#ifdef DEBUG
+static char dfile[] = "/var/tmp/MX4200.debug";
+static FILE *df = NULL;
+
+static void
+opendfile(create)
+ int create;
+{
+ if (!create && access(dfile, F_OK) < 0) {
+ syslog(LOG_ERR, "mx4200: open %s: %m", dfile);
+ return;
+ }
+ df = fopen(dfile, "a");
+ if (df == NULL)
+ syslog(LOG_ERR, "mx4200: open %s: %m", dfile);
+ else if (setvbuf(df, NULL, _IOLBF, 0) < 0)
+ syslog(LOG_ERR, "mx4200: setvbuf %s: %m", dfile);
+}
+
+static void
+checkdfile()
+{
+
+ if (df == NULL)
+ return;
+
+ if (access(dfile, F_OK) < 0) {
+ fclose(df);
+ opendfile(1);
+ }
+}
+
+#endif
+
+
+/*
+ * mx4200_start - open the MX4200 devices and initialize data for processing
+ */
+static int
+mx4200_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+ register int i;
+ int fd232;
+ char mx4200dev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_start: unit %d invalid", unit);
+ return (0);
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(mx4200dev, MX4200232, unit);
+ fd232 = open(mx4200dev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR,
+ "mx4200_start: open of %s: %m", mx4200dev);
+ return (0);
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TCGETA): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TCSETA): %m", mx4200dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The MX4200CLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The MX4200PPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcgetattr(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcsetattr(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcflush(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(MX4200CLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, I_PUSH, clk): %m", mx4200dev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, CLK_SETSTR): %m", mx4200dev);
+#endif /* MX4200CLK */
+#if defined(MX4200PPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, I_PUSH, ppsclock): %m", mx4200dev);
+ else
+ fdpps = fd232;
+#endif /* MX4200PPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The MX4200CLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(MX4200CLK)
+ int ldisc = CLKLDISC;
+#endif /* MX4200CLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCGETP): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(MX4200CLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* MX4200CLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCSETP): %m", mx4200dev);
+ goto screwed;
+ }
+#if defined(MX4200CLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCSETD): %m",mx4200dev);
+ goto screwed;
+ }
+#endif /* MX4200CLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (mx4200units[unit] != 0) {
+ mx4200 = mx4200units[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && mx4200units[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ mx4200 = mx4200units[i];
+ mx4200units[i] = 0;
+ } else {
+ mx4200 = (struct mx4200unit *)
+ emalloc(sizeof(struct mx4200unit));
+ }
+ }
+
+ memset((char *)mx4200, 0, sizeof(struct mx4200unit));
+ mx4200units[unit] = mx4200;
+
+ /*
+ * Set up the structures
+ */
+ mx4200->peer = peer;
+ mx4200->unit = (u_char)unit;
+ mx4200->timestarted = current_time;
+
+ mx4200->io.clock_recv = mx4200_receive;
+ mx4200->io.srcclock = (caddr_t)mx4200;
+ mx4200->io.datalen = 0;
+ mx4200->io.fd = fd232;
+ if (!io_addclock(&mx4200->io))
+ goto screwed;
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = MX4200PRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+
+ /* Insure the receiver is properly configured */
+ mx4200_config(mx4200);
+
+#ifdef DEBUG
+ opendfile(0);
+#endif
+ return (1);
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return (0);
+}
+
+/*
+ * mx4200_shutdown - shut down a MX4200 clock
+ */
+static void
+mx4200_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_shutdown: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ mx4200 = mx4200units[unit];
+ io_closeclock(&mx4200->io);
+ unitinuse[unit] = 0;
+}
+
+static void
+mx4200_config(mx4200)
+ register struct mx4200unit *mx4200;
+{
+ register int fd = mx4200->io.fd;
+
+syslog(LOG_DEBUG, "mx4200_config");
+
+ /* Zero the output list (do it twice to flush possible junk) */
+ mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1);
+ mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1);
+
+ /* Switch to 2d mode */
+ mx4200_send(fd, "%s,%03d,%d,,%.1f,%.1f,,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 2, /* 2d mode */
+ 0.1, /* hor accel fact as per Steve */
+ 0.1, /* ver accel fact as per Steve */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve */
+ 'U', /* time output mode */
+ 0); /* local time offset from gmt */
+
+ /* Configure time recovery */
+ mx4200_send(fd, "%s,%03d,%c,%c,%c,%d,%d,%d,",
+ pmvxg, PMVXG_S_TRECOVCONF,
+#ifdef notdef
+ 'K', /* known position */
+ 'D', /* dynamic position */
+#else
+ 'S', /* static position */
+#endif
+ 'U', /* steer clock to gps time */
+ 'A', /* always output time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output to control port */
+}
+
+/*
+ * mx4200_report_event - note the occurrence of an event
+ */
+static void
+mx4200_report_event(mx4200, code)
+ struct mx4200unit *mx4200;
+ int code;
+{
+ struct peer *peer;
+
+ peer = mx4200->peer;
+ if (mx4200->status != (u_char)code) {
+ mx4200->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ mx4200->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "mx4200 clock %s event %x", ntoa(&peer->srcadr), code);
+ }
+}
+
+/*
+ * mx4200_poll - mx4200 watchdog routine
+ */
+static void
+mx4200_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_poll: unit %d not used", unit);
+ return;
+ }
+
+ mx4200 = mx4200units[unit];
+ if ((current_time - mx4200->lasttime) > 150) {
+ mx4200_event(mx4200, CEVNT_FAULT);
+
+ /* Request a status message which should trigger a reconfig */
+ mx4200_send(mx4200->io.fd, "%s,%03d", "CDGPQ", PMVXG_D_STATUS);
+ syslog(LOG_DEBUG, "mx4200_poll: request status");
+ }
+}
+
+static const char char2hex[] = "0123456789ABCDEF";
+
+/*
+ * mx4200_receive - receive gps data
+ */
+static void
+mx4200_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct mx4200unit *mx4200;
+ register char *dpt, *cp;
+ register U_LONG tmp_ui;
+ register U_LONG tmp_uf;
+ register U_LONG gpstime;
+ struct ppsclockev ev;
+ register struct calendar *jt;
+ struct calendar sjt;
+ register int n;
+ int valid, leapsec;
+ register u_char ck;
+
+ mx4200 = (struct mx4200unit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("mx4200_receive: nsamples = %d\n", mx4200->nsamples);
+#endif
+
+ /* Record the time of this event */
+ mx4200->lasttime = current_time;
+
+ /* Get the pps value */
+ if (ioctl(mx4200->io.fd, CIOGETEV, (char *)&ev) < 0) {
+ /* XXX Actually, if this fails, we're pretty much screwed */
+#ifdef DEBUG
+ if (debug) {
+ fprintf(stderr, "mx4200_receive: ");
+ perror("CIOGETEV");
+ }
+#endif
+ mx4200->reason = PPSREASON + 1;
+ mx4200_event(mx4200, CEVNT_FAULT);
+ mx4200_reset(mx4200);
+ return;
+ }
+ tmp_ui = ev.tv.tv_sec + (U_LONG)JAN_1970;
+ TVUTOTSF(ev.tv.tv_usec, tmp_uf);
+
+ /* Get buffer and length; sock away last timecode */
+ n = rbufp->recv_length;
+ dpt = rbufp->recv_buffer;
+ if (n <= 1)
+ return;
+ mx4200->lencode = n;
+ memmove(mx4200->lastcode, dpt, n);
+
+ /*
+ * We expect to see something like:
+ *
+ * $PMVXG,830,T,1992,07,09,04:18:34,U,S,-02154,00019,000000,00*1D\n
+ *
+ * Reject if any important landmarks are missing.
+ */
+ cp = dpt + n - 4;
+ if (cp < dpt || *dpt != '$' || cp[0] != '*' || cp[3] != '\n') {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad format\n");
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 2;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Check checksum */
+ ck = cksum(&dpt[1], n - 5);
+ if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad checksum\n");
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 3;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Truncate checksum (and the buffer for that matter) */
+ *cp = '\0';
+
+ /* Leap second debugging stuff */
+ if ((leap_hoursfromleap && !leap_happened) || leap_debug > 0) {
+ /* generate reports for awhile after leap */
+ if (leap_hoursfromleap && !leap_happened)
+ leap_debug = 3600;
+ else
+ --leap_debug;
+ syslog(LOG_INFO, "mx4200 leap: %s \"%s\"",
+ umfptoa(tmp_ui, tmp_uf, 6), dpt);
+ }
+
+ /* Parse time recovery message */
+ jt = &sjt;
+ if ((cp = mx4200_parse(dpt, jt, &valid, &leapsec)) != NULL) {
+ /* Configure the receiver if necessary */
+ if (mx4200_needconf(dpt))
+ mx4200_config(mx4200);
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: mx4200_parse: %s\n", cp);
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 5;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Setup leap second indicator */
+ if (leapsec == 0)
+ mx4200->leap = LEAP_NOWARNING;
+ else if (leapsec == 1)
+ mx4200->leap = LEAP_ADDSECOND;
+ else if (leapsec == -1)
+ mx4200->leap = LEAP_DELSECOND;
+ else
+ mx4200->leap = LEAP_NOTINSYNC; /* shouldn't happen */
+
+ /* Check parsed time (allow for possible leap seconds) */
+ if (jt->second >= 61 || jt->minute >= 60 || jt->hour >= 24) {
+#ifdef DEBUG
+ if (debug) {
+ printf("mx4200_receive: bad time %d:%02d:%02d",
+ jt->hour, jt->minute, jt->second);
+ if (leapsec != 0)
+ printf(" (leap %+d)", leapsec);
+ putchar('\n');
+ }
+#endif
+ mx4200->baddata++;
+ mx4200->reason = PPSREASON + 6;
+ mx4200_event(mx4200, CEVNT_BADTIME);
+ mx4200_reset(mx4200);
+ /* Eat the next pulse which the clock claims will be bad */
+ mx4200->nsamples = -1;
+ return;
+ }
+
+ /* Check parsed date */
+ if (jt->monthday > 31 || jt->month > 12 || jt->year < 1900) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad date (%d/%d/%d)\n",
+ jt->monthday, jt->month, jt->year);
+#endif
+ mx4200->baddata++;
+ mx4200->reason = PPSREASON + 7;
+ mx4200_event(mx4200, CEVNT_BADDATE);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Convert to ntp time */
+ gpstime = caltontp(jt);
+
+ /* The gps message describes the *next* pulse; pretend it's this one */
+ --gpstime;
+
+ /* Debugging */
+#ifdef DEBUG
+ checkdfile();
+ if (df != NULL) {
+ l_fp t;
+
+ t.l_ui = gpstime;
+ t.l_uf = 0;
+ M_SUB(t.l_ui, t.l_uf, tmp_ui, tmp_uf);
+ fprintf(df, "%s\t%s",
+ umfptoa(tmp_ui, tmp_uf, 6), mfptoa(t.l_ui, t.l_uf, 6));
+ if (debug > 3)
+ fprintf(df, "\t(gps: %lu)", (u_long)gpstime);
+ if (leapsec != 0)
+ fprintf(df, "\t(leap sec %+d)", leapsec);
+ if (!valid)
+ fprintf(df, "\t(pulse not valid)");
+ fputc('\n', df);
+ }
+#endif
+
+ /* Check pps serial number against last one */
+ if (mx4200->lastserial + 1 != ev.serial && mx4200->lastserial != 0) {
+#ifdef DEBUG
+ if (debug) {
+ if (ev.serial == mx4200->lastserial)
+ printf("mx4200_receive: no new pps event\n");
+ else
+ printf("mx4200_receive: missed %d pps events\n",
+ ev.serial - mx4200->lastserial - 1);
+ }
+#endif
+ mx4200->reason = PPSREASON + 8;
+ mx4200_event(mx4200, CEVNT_FAULT);
+ mx4200_reset(mx4200);
+ /* fall through and this one collect as first sample */
+ }
+ mx4200->lastserial = ev.serial;
+
+/*
+ * XXX
+ * Since this message is for the next pulse, it's really the next pulse
+ * that the clock might be telling us will be invalid.
+ */
+ /* Toss if not designated "valid" by the gps */
+ if (!valid) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: pps not valid\n");
+#endif
+ mx4200->reason = PPSREASON + 9;
+ mx4200_event(mx4200, CEVNT_BADTIME);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Copy time into mx4200unit struct */
+ /* XXX (why?) */
+ mx4200->year = jt->year;
+ mx4200->monthday = jt->monthday;
+ mx4200->hour = jt->hour;
+ mx4200->minute = jt->minute;
+ mx4200->second = jt->second;
+
+ /* Sock away the GPS and UNIX timesamples */
+ n = mx4200->nsamples++;
+ if (n < 0)
+ return; /* oops, this pulse is bad */
+ mx4200->gpssamples[n] = gpstime;
+ mx4200->unixsamples[n].l_ui = mx4200->lastsampletime.l_ui = tmp_ui;
+ mx4200->unixsamples[n].l_uf = mx4200->lastsampletime.l_uf = tmp_uf;
+ if (mx4200->nsamples >= NSTAMPS) {
+ /*
+ * Here we've managed to complete an entire NSTAMPS
+ * second cycle without major mishap. Process what has
+ * been received.
+ */
+ mx4200_process(mx4200);
+ mx4200_reset(mx4200);
+ }
+}
+
+/* Compare two l_fp's, used with qsort() */
+static int
+mx4200_cmpl_fp(p1, p2)
+ register void *p1, *p2;
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+/*
+ * mx4200_process - process a pile of samples from the clock
+ */
+static void
+mx4200_process(mx4200)
+ struct mx4200unit *mx4200;
+{
+ register int i, n;
+ register l_fp *fp, *op;
+ register U_LONG *lp;
+ l_fp off[NSTAMPS];
+ register U_LONG tmp_ui, tmp_uf;
+ register U_LONG date_ui, date_uf;
+ u_fp dispersion;
+
+ /* Compute offsets from the raw data. */
+ fp = mx4200->unixsamples;
+ op = off;
+ lp = mx4200->gpssamples;
+ for (i = 0; i < NSTAMPS; ++i, ++lp, ++op, ++fp) {
+ op->l_ui = *lp;
+ op->l_uf = 0;
+ L_SUB(op, fp);
+ }
+
+ /* Sort offsets into ascending order. */
+ qsort((char *)off, NSTAMPS, sizeof(l_fp), mx4200_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median until 8 samples left
+ */
+ i = 0;
+ n = NSTAMPS;
+ while ((n - i) > 8) {
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ date_ui = off[(n+i)/2].l_ui;
+ date_uf = off[(n+i)/2].l_uf;
+ M_SUB(tmp_ui, tmp_uf, date_ui, date_uf);
+ M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf);
+ if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) {
+ /*
+ * reject low end
+ */
+ i++;
+ } else {
+ /*
+ * reject high end
+ */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets.
+ */
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ dispersion = MFPTOFP(tmp_ui, tmp_uf);
+
+ /*
+ * Now compute the offset estimate. If the sloppy clock
+ * flag is set, average the remainder, otherwise pick the
+ * median.
+ */
+ if (sloppyclockflag[mx4200->unit]) {
+ tmp_ui = tmp_uf = 0;
+ while (i < n) {
+ M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ i++;
+ }
+ M_RSHIFT(tmp_ui, tmp_uf);
+ M_RSHIFT(tmp_ui, tmp_uf);
+ M_RSHIFT(tmp_ui, tmp_uf);
+ i = 0;
+ off[0].l_ui = tmp_ui;
+ off[0].l_uf = tmp_uf;
+ } else {
+ i = (n + i) / 2;
+ }
+
+ /*
+ * Add the default MX4200 QT delay into this.
+ */
+#ifdef notdef
+ L_ADDUF(&off[i], MX4200QTFUDGE);
+#endif
+
+ /*
+ * Done. Use lastref as the reference time and lastrec
+ * as the receive time. ** note this can result in tossing
+ * out the peer in the protocol module if lastref > lastrec,
+ * so last rec is used for both values - dlm ***
+ */
+ refclock_receive(mx4200->peer, &off[i],
+ (s_fp)0, /* delay */
+ dispersion,
+ &mx4200->unixsamples[NSTAMPS-1], /* reftime */
+ &mx4200->unixsamples[NSTAMPS-1], /* rectime */
+ mx4200->leap);
+
+ mx4200_event(mx4200, CEVNT_NOMINAL);
+}
+
+/*
+ * mx4200_control - set fudge factors, return statistics
+ */
+static void
+mx4200_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ sloppyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = mx4200units[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_GPS_MX4200;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 |
+ CLK_HAVEFLAG1;
+ out->clockdesc = MX4200DESCRIPTION;
+ out->fudgetime1 = fudgefactor[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];;
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ mx4200 = mx4200units[unit];
+ out->lencode = mx4200->lencode;
+ out->lastcode = mx4200->lastcode;
+ out->lastevent = mx4200->lastevent;
+ out->currentstatus = mx4200->status;
+
+ out->polls = 0; /* mx4200->polls; */
+ out->noresponse = 0; /* mx4200->noresponse; */
+ out->badformat = mx4200->badformat;
+ out->baddata = mx4200->baddata;
+ out->timereset = current_time - mx4200->timestarted;
+ }
+ }
+}
+
+/*
+ * mx4200_buginfo - return clock dependent debugging info
+ */
+static void
+mx4200_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ mx4200 = mx4200units[unit];
+
+ memset((char *)bug, 0, sizeof(*bug));
+ bug->nvalues = 10;
+ bug->ntimes = 2;
+ if (mx4200->lasttime != 0)
+ bug->values[0] = current_time - mx4200->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)mx4200->reason;
+ bug->values[2] = (U_LONG)mx4200->year;
+ bug->values[3] = (U_LONG)mx4200->monthday;
+ bug->values[4] = (U_LONG)mx4200->hour;
+ bug->values[5] = (U_LONG)mx4200->minute;
+ bug->values[6] = (U_LONG)mx4200->second;
+#ifdef notdef
+ bug->values[7] = mx4200->msec;
+ bug->values[8] = mx4200->noreply;
+ bug->values[9] = mx4200->yearstart;
+#endif
+ bug->stimes = 0x1c;
+#ifdef notdef
+ bug->times[0] = mx4200->lastref;
+ bug->times[1] = mx4200->lastrec;
+#endif
+}
+
+/*
+ * Returns true if the this is a status message. We use this as
+ * an indication that the receiver needs to be initialized.
+ */
+static int
+mx4200_needconf(buf)
+ char *buf;
+{
+ register LONG v;
+ char *cp;
+
+ cp = buf;
+
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Record type */
+ v = strtol(cp, &cp, 10);
+ if (v != PMVXG_D_STATUS)
+ return (0);
+ /*
+ * XXX
+ * Since we configure the receiver to not give us status
+ * messages and since the receiver outputs status messages by
+ * default after being reset to factory defaults when sent the
+ * "$PMVXG,018,C\r\n" message, any status message we get
+ * indicates the reciever needs to be initialized; thus, it is
+ * not necessary to decode the status message.
+ */
+#ifdef notdef
+ ++cp;
+
+ /* Receiver status */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Number of satellites which should be visible */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Number of satellites being tracked */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Time since last NAV */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Initialization status */
+ v = strtol(cp, &cp, 10);
+ if (v == 0)
+#endif
+ return (1);
+}
+
+/* Parse a mx4200 time recovery message. Returns a string if error */
+static char *
+mx4200_parse(buf, jt, validp, leapsecp)
+ register char *buf;
+ register struct calendar *jt;
+ register int *validp, *leapsecp;
+{
+ register LONG v;
+ char *cp;
+
+ cp = buf;
+ memset((char *)jt, 0, sizeof(*jt));
+
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no rec-type");
+ ++cp;
+
+ /* Record type */
+ v = strtol(cp, &cp, 10);
+ if (v != PMVXG_D_TRECOVOUT)
+ return ("wrong rec-type");
+
+ /* Pulse valid indicator */
+ if (*cp++ != ',')
+ return ("no pulse-valid");
+ if (*cp == 'T')
+ *validp = 1;
+ else if (*cp == 'F')
+ *validp = 0;
+ else
+ return ("bad pulse-valid");
+ ++cp;
+
+ /* Year */
+ if (*cp++ != ',')
+ return ("no year");
+ jt->year = strtol(cp, &cp, 10);
+
+ /* Month of year */
+ if (*cp++ != ',')
+ return ("no month");
+ jt->month = strtol(cp, &cp, 10);
+
+ /* Day of month */
+ if (*cp++ != ',')
+ return ("no month day");
+ jt->monthday = strtol(cp, &cp, 10);
+
+ /* Hour */
+ if (*cp++ != ',')
+ return ("no hour");
+ jt->hour = strtol(cp, &cp, 10);
+
+ /* Minute */
+ if (*cp++ != ':')
+ return ("no minute");
+ jt->minute = strtol(cp, &cp, 10);
+
+ /* Second */
+ if (*cp++ != ':')
+ return ("no second");
+ jt->second = strtol(cp, &cp, 10);
+
+ /* Time indicator */
+ if (*cp++ != ',' || *cp++ == '\0')
+ return ("no time indicator");
+
+ /* Time recovery mode */
+ if (*cp++ != ',' || *cp++ == '\0')
+ return ("no time mode");
+
+ /* Oscillator offset */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no osc off");
+ ++cp;
+
+ /* Time mark error */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no time mark err");
+ ++cp;
+
+ /* User time bias */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no user bias");
+ ++cp;
+
+ /* Leap second flag */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no leap");
+ ++cp;
+ *leapsecp = strtol(cp, &cp, 10);
+
+ return (NULL);
+}
+
+/* Calculate the checksum */
+static u_char
+cksum(cp, n)
+ register char *cp;
+ register u_int n;
+{
+ register u_char ck;
+
+ for (ck = 0; n-- > 0; ++cp)
+ ck ^= *cp;
+ return (ck);
+}
+
+static void
+#if __STDC__
+mx4200_send(register int fd, const char *fmt, ...)
+#else
+mx4200_send(fd, fmt, va_alist)
+ register int fd;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ register char *cp;
+ register int n, m;
+ va_list ap;
+ char buf[1024];
+ u_char ck;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ cp = buf;
+ *cp++ = '$';
+#ifdef notdef
+ /* BSD is rational */
+ n = vsnprintf(cp, sizeof(buf) - 1, fmt, ap);
+#else
+ /* SunOS sucks */
+ (void)vsprintf(cp, fmt, ap);
+ n = strlen(cp);
+#endif
+ ck = cksum(cp, n);
+ cp += n;
+ ++n;
+#ifdef notdef
+ /* BSD is rational */
+ n += snprintf(cp, sizeof(buf) - n - 5, "*%02X\r\n", ck);
+#else
+ /* SunOS sucks */
+ sprintf(cp, "*%02X\r\n", ck);
+ n += strlen(cp);
+#endif
+
+ m = write(fd, buf, n);
+ if (m < 0)
+ syslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
+ else if (m != n)
+ syslog(LOG_ERR, "mx4200_send: write: %d != %d (%s)", m, n, buf);
+ va_end(ap);
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_nmea.c b/usr.sbin/xntpd/xntpd/refclock_nmea.c
new file mode 100644
index 0000000..f8ae4bc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_nmea.c
@@ -0,0 +1,391 @@
+/*
+ * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
+ * Michael Petry Jun 20, 1994
+ * based on refclock_heath.c
+ */
+#if defined(REFCLOCK) && defined(NMEA)
+
+#define DEBUG 1
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NMEA GPS Receiver with
+ *
+ * Protype was refclock_trak.c, Thanks a lot.
+ *
+ * The reciever used spits out the NMEA sentences for boat navigation.
+ * And you thought it was an information superhighway. Try a raging river
+ * filled with rapids and whirlpools that rip away your data and warp time.
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENNMEA 75 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct nmeaunit {
+ int pollcnt; /* poll message counter */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int nmea_start P((int, struct peer *));
+static void nmea_shutdown P((int, struct peer *));
+static void nmea_receive P((struct recvbuf *));
+static void nmea_poll P((int, struct peer *));
+static void gps_send P((int, char *, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_nmea = {
+ nmea_start, /* start up driver */
+ nmea_shutdown, /* shut down driver */
+ nmea_poll, /* transmit poll message */
+ noentry, /* handle control */
+ noentry, /* initialize driver */
+ noentry, /* buginfo */
+ NOFLAGS /* not used */
+};
+
+/*
+ * nmea_start - open the GPS devices and initialize data for processing
+ */
+static int
+nmea_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct nmeaunit *)
+ emalloc(sizeof(struct nmeaunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct nmeaunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = nmea_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000\n", peer);
+ return (1);
+}
+
+/*
+ * nmea_shutdown - shut down a GPS clock
+ */
+static void
+nmea_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * nmea_receive - receive data from the serial interface
+ */
+static void
+nmea_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char *cp, *dp;
+ int cmdtype;
+ char *field_parse();
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * There is a case that a <CR><LF> gives back a "blank" line
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * We get a buffer and timestamp for each <cr>; however, we use
+ * the timestamp of "now" since this may be a broadcast instead
+ * of a poll. This needs to be checked empeerically
+ */
+ gettstamp(&up->tstamp); /* HACK */
+ pp->lastrec = up->tstamp;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We check the timecode format and decode its contents. The
+ * we only care about a few of them. The most important being
+ * the $GPRMC format
+ * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
+ */
+#define GPRMC 0
+#define GPXXX 1
+ cp = pp->lastcode;
+ pp->leap = 0;
+ cmdtype=0;
+ if(strncmp(cp,"$GPRMC",6)==0) {
+ cmdtype=GPRMC;
+ }
+ else if(strncmp(cp,"$GPXXX",6)==0) {
+ cmdtype=GPXXX;
+ }
+ else
+ return;
+
+ switch( cmdtype ) {
+ case GPRMC:
+ /*
+ * Check time code format of NMEA
+ */
+
+ dp = field_parse(cp,1);
+ if( !isdigit(dp[0]) ||
+ !isdigit(dp[1]) ||
+ !isdigit(dp[2]) ||
+ !isdigit(dp[3]) ||
+ !isdigit(dp[4]) ||
+ !isdigit(dp[5])
+ ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ dp = field_parse(cp,9);
+ /*
+ * Convert date and check values.
+ */
+ day = dp[0] - '0';
+ day = (day * 10) + dp[1] - '0';
+ month = dp[2] - '0';
+ month = (month * 10) + dp[3] - '0';
+ pp->year = dp[4] - '0';
+ pp->year = (pp->year * 10) + dp[5] - '0';
+
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ dp = field_parse(cp,1);
+ /*
+ * Convert time and check values.
+ */
+ pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
+ pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
+ pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
+ pp->msec = 0;
+
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Test for synchronization Check for quality byte. (soon)
+ */
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+/*
+ * nmea_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+nmea_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+
+ /*
+ * usually nmea_receive can get a timestamp every second
+ */
+
+ gps_send(pp->io.fd,"$PMOTG,RMC,0\n", peer);
+}
+
+/*
+ *
+ * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
+ * as gps_send(fd,"rqts,u\r", peer);
+ *
+ * We don't currently send any data, but would like to send
+ * RTCM SC104 messages for differential positioning. It should
+ * also give us better time. Without a PPS output, we're
+ * Just fooling ourselves because of the serial code paths
+ *
+ */
+static void
+gps_send(fd, cmd, peer)
+ int fd;
+ char *cmd;
+ struct peer *peer;
+{
+
+ if (write(fd, cmd, strlen(cmd)) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+char *
+field_parse(cp, fn)
+ char *cp;
+ int fn;
+{
+ char *tp;
+ int i = fn;
+
+ for (tp = cp; *tp != '\0'; tp++) {
+ if (*tp == ',')
+ i--;
+ if (i == 0)
+ break;
+ }
+ return (++tp);
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_omega.c
new file mode 100644
index 0000000..1dc31ff
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_omega.c
@@ -0,0 +1,999 @@
+/*
+ * refclock_omega - clock driver for the Kinemetrics Truetime OM-DC OMEGA
+ * receiver.
+ *
+ * Version 1.0 11-Dec-92 Steve Clift (clift@ml.csiro.au)
+ * Initial version, mostly lifted from refclock_goes.c.
+ *
+ * 1.1 03-May-93 Steve Clift
+ * Tarted up the sample filtering mechanism to give improved
+ * one-off measurements. Improved measurement dispersion code
+ * to account for accumulated drift when the clock loses lock.
+ *
+ */
+
+#if defined(REFCLOCK) && (defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(OMEGACLK)
+#include <sys/clkdefs.h>
+#endif /* OMEGACLK */
+#endif /* STREAM */
+
+#if defined (OMEGAPPS)
+#include <sys/ppsclock.h>
+#endif /* OMEGAPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime OM-DC OMEGA Receiver
+ *
+ * Most of this code is copied from refclock_goes.c with thanks.
+ *
+ * the time code looks like follows; Send the clock a R or C and once per
+ * second a timestamp will appear that looks like this:
+ * ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ * C - Carriage return
+ * L - Line feed
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* max number of OMEGA units */
+#define OMEGA232 "/dev/omega%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define OMEGADESCRIPTION "Kinemetrics OM-DC OMEGA Receiver" /* who we are */
+#define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */
+#define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */
+#define OMEGAREFID "VLF\0" /* reference id */
+#define LENOMEGA 13 /* length of standard response */
+#define GMT 0 /* hour offset from Greenwich */
+#define NSTAMPS 9 /* samples collected when polled */
+#define NSKEEP 5 /* samples to keep after discards */
+
+/*
+ * The OM-DC puts out the start bit of the <CR> on the second, but
+ * we see the result after the <LF> is received, about 2ms later at
+ * 9600 baud. Use this as the default fudge time, and let the user
+ * fiddle it to account for driver latency etc.
+ */
+#define DEFFUDGETIME 0x00830000 /* default fudge time (~2ms) */
+
+/*
+ * Clock drift errors as u_fp values.
+ */
+#define U_FP5000MS (5*FP_SECOND) /* 5 seconds */
+#define U_FP500MS (FP_SECOND/2) /* 500 msec */
+#define U_FP50MS (FP_SECOND/20) /* 50 msec */
+#define U_FP5MS (FP_SECOND/200) /* 5 msec */
+
+/*
+ * Station codes
+ */
+#define STATION_NONE 0
+#define STATION_NORWAY 1
+#define STATION_LIBERIA 2
+#define STATION_HAWAII 3
+#define STATION_N_DAKOTA 4
+#define STATION_LA_REUNION 5
+#define STATION_ARGENTINA 6
+#define STATION_AUSTRALIA 7
+#define STATION_JAPAN 8
+
+/*
+ * Hack to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * OMEGA unit control structure
+ */
+struct omegaunit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+ l_fp offset[NSTAMPS]; /* recent sample offsets */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short station; /* which station we're locked to */
+ u_short polled; /* Hand in a time sample? */
+ U_LONG coderecv; /* timecodes received */
+ u_char lencode; /* length of last timecode */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last failure */
+ u_char year; /* year of eternity */
+ u_short day; /* day of year */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ u_short msec; /* millisecond of second */
+ u_char quality; /* quality char from last timecode */
+ u_long yearstart; /* start of current year */
+ /*
+ * Status tallies
+ */
+ U_LONG polls; /* polls sent */
+ U_LONG noreply; /* no replies to polls */
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct omegaunit *omegaunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor1[MAXUNITS];
+static l_fp fudgefactor2[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char readonlyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+/*
+ * Function prototypes
+ */
+static void omega_init P((void));
+static int omega_start P((int, struct peer *));
+static void omega_shutdown P((int, struct peer *));
+static void omega_report_event P((struct omegaunit *, int));
+static void omega_receive P((struct recvbuf *));
+static char omega_process P((struct omegaunit *, l_fp *, u_fp *));
+static void omega_poll P((int, struct peer *));
+static void omega_control P((int, struct refclockstat *, struct refclockstat *));
+static void omega_buginfo P((int, struct refclockbug *));
+static void omega_send P((struct omegaunit *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_omega = {
+ omega_start, omega_shutdown, omega_poll,
+ omega_control, omega_init, omega_buginfo, NOFLAGS
+};
+
+/*
+ * omega_init - initialize internal omega driver data
+ */
+static void
+omega_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)omegaunits, 0, sizeof omegaunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor1[i].l_ui = 0;
+ fudgefactor1[i].l_uf = DEFFUDGETIME;
+ fudgefactor2[i].l_ui = 0;
+ fudgefactor2[i].l_uf = 0;
+ stratumtouse[i] = 0;
+ readonlyclockflag[i] = 0;
+ memcpy((char *)&refid[i], OMEGAREFID, 4);
+ }
+}
+
+
+/*
+ * omega_start - open the OMEGA devices and initialize data for processing
+ */
+static int
+omega_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct omegaunit *omega;
+ register int i;
+ int fd232;
+ char omegadev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,"omega_start: unit %d invalid", unit);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_start: unit %d in use", unit);
+ return 0;
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(omegadev, OMEGA232, unit);
+ fd232 = open(omegadev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "omega_start: open of %s: %m", omegadev);
+ return 0;
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TCGETA): %m", omegadev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TCSETA): %m", omegadev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The OMEGAPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcgetattr(%s): %m", omegadev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcsetattr(%s): %m", omegadev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcflush(%s): %m", omegadev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(OMEGACLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, clk): %m", omegadev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, CLK_SETSTR): %m", omegadev);
+#endif /* OMEGACLK */
+#if defined(OMEGAPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, ppsclock): %m", omegadev);
+ else
+ fdpps = fd232;
+#endif /* OMEGAPPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(OMEGACLK)
+ int ldisc = CLKLDISC;
+#endif /* OMEGACLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCGETP): %m", omegadev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(OMEGACLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* OMEGACLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETP): %m", omegadev);
+ goto screwed;
+ }
+#if defined(OMEGACLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETD): %m",omegadev);
+ goto screwed;
+ }
+#endif /* OMEGACLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (omegaunits[unit] != 0) {
+ omega = omegaunits[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && omegaunits[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ omega = omegaunits[i];
+ omegaunits[i] = 0;
+ } else {
+ omega = (struct omegaunit *)
+ emalloc(sizeof(struct omegaunit));
+ }
+ }
+ memset((char *)omega, 0, sizeof(struct omegaunit));
+ omegaunits[unit] = omega;
+
+ /*
+ * Set up the structures
+ */
+ omega->peer = peer;
+ omega->unit = (u_char)unit;
+ omega->timestarted = current_time;
+ omega->station = STATION_NONE;
+
+ omega->io.clock_recv = omega_receive;
+ omega->io.srcclock = (caddr_t)omega;
+ omega->io.datalen = 0;
+ omega->io.fd = fd232;
+ if (!io_addclock(&omega->io)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = OMEGAPRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return 1;
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return 0;
+}
+
+
+/*
+ * omega_shutdown - shut down a OMEGA clock
+ */
+static void
+omega_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ omega = omegaunits[unit];
+ io_closeclock(&omega->io);
+ unitinuse[unit] = 0;
+}
+
+
+/*
+ * omega_report_event - note the occurance of an event
+ */
+static void
+omega_report_event(omega, code)
+ struct omegaunit *omega;
+ int code;
+{
+ struct peer *peer;
+
+ peer = omega->peer;
+ if (omega->status != (u_char)code) {
+ omega->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ omega->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "omega clock %s event %x\n", ntoa(&peer->srcadr), code);
+ }
+}
+
+
+/*
+ * omega_receive - receive data from the serial interface on a
+ * Kinemetrics OM-DC OMEGA clock.
+ */
+static void
+omega_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int i;
+ register struct omegaunit *omega;
+ register u_char *dpt;
+ register char *cp, *cpend;
+ register u_char *dpend;
+ l_fp tstmp;
+ u_fp dispersion, drift;
+
+ /*
+ * Get the clock this applies to and a pointers to the data
+ */
+ omega = (struct omegaunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+
+#ifndef PEDANTIC
+ /*
+ * The OM-DC outputs a timecode every second, but we only want
+ * a set of NSTAMPS timecodes when polled (every 64 seconds).
+ * Setting PEDANTIC causes a sanity check on every timecode.
+ */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * Edit timecode to remove control chars
+ */
+ dpend = dpt + rbufp->recv_length;
+ cp = omega->lastcode;
+ cpend = omega->lastcode + BMAX - 1;
+ while (dpt < dpend && cp < cpend) {
+ if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
+#ifdef OMEGACLK
+ else if (*cp == '\r') {
+ if (dpend - dpt < 8) {
+ /* short timestamp */
+ return;
+ }
+ if (!buftvtots(dpt,&omega->lastrec)) {
+ /* screwy timestamp */
+ return;
+ }
+ dpt += 8;
+ }
+#endif
+ }
+ *cp = '\0';
+ omega->lencode = cp - omega->lastcode;
+
+ if (omega->lencode == 0)
+ return;
+ else if (omega->lencode != LENOMEGA) {
+ omega->badformat++;
+ /* Sometimes get a lot of these, filling the log with noise */
+ /* omega_report_event(omega, CEVNT_BADREPLY); */
+ return;
+ }
+
+#ifndef OMEGACLK
+ omega->lastrec = rbufp->recv_time;
+#endif
+
+#ifdef DEBUG
+ if (debug)
+ printf("omega: timecode %d %s\n",
+ omega->lencode, omega->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format
+ * and decode its contents.
+ */
+ cp = omega->lastcode;
+ omega->leap = 0;
+ /*
+ * Check timecode format.
+ */
+ if (!isdigit(cp[0]) || /* day of year */
+ !isdigit(cp[1]) ||
+ !isdigit(cp[2]) ||
+ cp[3] != ':' || /* <sp> */
+ !isdigit(cp[4]) || /* hours */
+ !isdigit(cp[5]) ||
+ cp[6] != ':' || /* : separator */
+ !isdigit(cp[7]) || /* minutes */
+ !isdigit(cp[8]) ||
+ cp[9] != ':' || /* : separator */
+ !isdigit(cp[10]) || /* seconds */
+ !isdigit(cp[11])) {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Convert and check values.
+ */
+ omega->year = 0; /* fake */
+ omega->day = cp[0] - '0';
+ omega->day = MULBY10(omega->day) + cp[1] - '0';
+ omega->day = MULBY10(omega->day) + cp[2] - '0';
+ omega->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
+ omega->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
+ omega->second = MULBY10(cp[10] - '0') + cp[11] - '0';
+ omega->msec = 0;
+
+ if (omega->day < 1 || omega->day > 366) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADDATE);
+ return;
+ }
+ if (omega->hour > 23 || omega->minute > 59 || omega->second > 59) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Check quality/station-id flag. The OM-DC should normally stay
+ * permanently locked to a station, and its time error should be less
+ * than 1 msec. If it loses lock for any reason, it makes a worst
+ * case drift estimate based on the internally stored stability figure
+ * for its reference oscillator. The stability figure can be adjusted
+ * by the user based on experience. The default value is 1E05, which
+ * is pretty bad - 2E07 is about right for the unit I have.
+ *
+ * The following is arbitrary, change it if you're offended:
+ * For errors less than 50 msec, just clear the station indicator.
+ * For errors greater than 50 msec, flag loss of sync and report a
+ * propagation problem. If the error is greater than 500 msec,
+ * something is dreadfully wrong - report a clock fault.
+ *
+ * In each case, we set a drift estimate which is used below as an
+ * estimate of measurement accuracy.
+ */
+ omega->quality = cp[12];
+ if (cp[12] == '>' || cp[12] == '?') {
+ /* Error 500 to 5000 msec */
+ omega_report_event(omega, CEVNT_FAULT);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP5000MS;
+ } else if (cp[12] == '#') {
+ /* Error 50 to 500 msec */
+ omega_report_event(omega, CEVNT_PROP);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP500MS;
+ } else if (cp[12] == '*') {
+ /* Error 5 to 50 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP50MS;
+ } else if (cp[12] == '.') {
+ /* Error 1 to 5 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP5MS;
+ } else if ('A' <= cp[12] && cp[12] <= 'H') {
+ /* Error less than 1 msec */
+ omega->lasttime = current_time;
+ omega->station = cp[12] - 'A' + 1;
+ drift = 0;
+ } else {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+#ifdef PEDANTIC
+ /* If we haven't been polled, bail out. */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present.
+ *
+ * this code does not yet know how to do the years
+ */
+ tstmp = omega->lastrec;
+ if (!clocktime(omega->day, omega->hour, omega->minute,
+ omega->second, GMT, tstmp.l_ui,
+ &omega->yearstart, &omega->lastref.l_ui)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(omega->msec, omega->lastref.l_uf);
+
+ /*
+ * Adjust the read value by fudgefactor1 to correct RS232 delays.
+ */
+ L_ADD(&omega->lastref, &fudgefactor1[omega->unit]);
+
+ /* Carousel of NSTAMPS offsets. */
+ i = omega->coderecv % NSTAMPS;
+ omega->offset[i] = omega->lastref;
+ L_SUB(&omega->offset[i], &tstmp);
+ omega->coderecv++;
+
+ /* If we don't yet have a full set, return. */
+ if (omega->coderecv < NSTAMPS)
+ return;
+
+ /*
+ * Filter the samples, add the fudge factor and pass the
+ * offset and dispersion along. We use lastrec as both the
+ * reference time and receive time in order to avoid being cute,
+ * like setting the reference time later than the receive time,
+ * which may cause a paranoid protocol module to chuck out the
+ * data. If the sample filter chokes because of excessive
+ * dispersion or whatever, get a new sample (omega->coderecv
+ * is still >= NSTAMPS) and try again.
+ */
+ if (!omega_process(omega, &tstmp, &dispersion)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Add accumulated clock drift to the dispersion to get
+ * a (hopefully) meaningful measurement accuracy estimate.
+ */
+ dispersion += drift;
+ refclock_receive(omega->peer, &tstmp, GMT, dispersion,
+ &omega->lastrec, &omega->lastrec, omega->leap);
+
+ /*
+ * We have succeeded in answering the poll. If the clock
+ * is locked, we're nominal.
+ */
+ omega->polled = 0;
+ omega->coderecv = 0;
+ if (omega->leap != LEAP_NOTINSYNC)
+ omega_report_event(omega, CEVNT_NOMINAL);
+}
+
+
+/*
+ * omega_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+omega_send(omega,cmd)
+ struct omegaunit *omega;
+ char *cmd;
+{
+ if (!readonlyclockflag[omega->unit]) {
+ /*
+ * Send a command to the clock.
+ */
+ if (write(omega->io.fd, cmd, 1) != 1) {
+ syslog(LOG_ERR, "omega_send: unit %d: %m", omega->unit);
+ omega_report_event(omega, CEVNT_FAULT);
+ }
+ }
+}
+
+
+/*
+ * Compare two l_fp's, used with qsort()
+ */
+static int
+omega_cmpl_fp(p1, p2)
+ register void *p1, *p2;
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+
+/*
+ * omega_process - process a pile of samples from the clock
+ */
+static char
+omega_process(omega, offset, dispersion)
+ struct omegaunit *omega;
+ l_fp *offset;
+ u_fp *dispersion;
+{
+ register int i, n;
+ register U_LONG med_ui, med_uf, tmp_ui, tmp_uf;
+ l_fp off[NSTAMPS];
+ u_fp disp;
+
+ /* Copy in offsets and sort into ascending order */
+ for (i = 0; i < NSTAMPS; i++)
+ off[i] = omega->offset[i];
+ qsort((char *)off, NSTAMPS, sizeof(l_fp), omega_cmpl_fp);
+ /*
+ * Reject the furthest from the median until NSKEEP samples remain
+ */
+ i = 0;
+ n = NSTAMPS;
+ while ((n - i) > NSKEEP) {
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ med_ui = off[(n+i)/2].l_ui;
+ med_uf = off[(n+i)/2].l_uf;
+ M_SUB(tmp_ui, tmp_uf, med_ui, med_uf);
+ M_SUB(med_ui, med_uf, off[i].l_ui, off[i].l_uf);
+ if (M_ISHIS(med_ui, med_uf, tmp_ui, tmp_uf)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. If this is greater than
+ * the allowed sample set dispersion, bail out. Otherwise,
+ * return the median offset and the dispersion.
+ */
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ disp = MFPTOFP(tmp_ui, tmp_uf);
+ if (disp > OMEGAMAXDISPERSE)
+ return 0;
+ *offset = off[(n+1)/2];
+ *dispersion = disp;
+ return 1;
+}
+
+
+/*
+ * omega_poll - called by the transmit procedure
+ */
+static void
+omega_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct omegaunit *omega;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_poll: unit %d not in use", unit);
+ return;
+ }
+ omega = omegaunits[unit];
+ if ((current_time - omega->lasttime) > 150) {
+ omega->noreply++;
+ omega_report_event(omegaunits[unit], CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask OMEGA_RECEIVE to hand in a timestamp.
+ */
+ omega->polled = 1;
+ omega->polls++;
+ /*
+ * Ensure the clock is running in the correct mode - on-second
+ * timestamps.
+ */
+ omega_send(omega,"C");
+}
+
+
+/*
+ * omega_control - set fudge factors, return statistics
+ */
+static void
+omega_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor1[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ fudgefactor2[unit] = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ readonlyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = omegaunits[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ out->type = REFCLK_OMEGA_TRUETIME;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2| CLK_HAVEFLAG1;
+ out->clockdesc = OMEGADESCRIPTION;
+ out->fudgetime1 = fudgefactor1[unit];
+ out->fudgetime2 = fudgefactor2[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->flags = readonlyclockflag[unit];
+ if (unitinuse[unit]) {
+ omega = omegaunits[unit];
+ out->flags |= omega->station << 1;
+ out->lencode = omega->lencode;
+ out->lastcode = omega->lastcode;
+ out->timereset = current_time - omega->timestarted;
+ out->polls = omega->polls;
+ out->noresponse = omega->noreply;
+ out->badformat = omega->badformat;
+ out->baddata = omega->baddata;
+ out->lastevent = omega->lastevent;
+ out->currentstatus = omega->status;
+ }
+ }
+}
+
+
+/*
+ * omega_buginfo - return clock dependent debugging info
+ */
+static void
+omega_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ omega = omegaunits[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (omega->lasttime != 0)
+ bug->values[0] = current_time - omega->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)omega->reason;
+ bug->values[2] = (U_LONG)omega->year;
+ bug->values[3] = (U_LONG)omega->day;
+ bug->values[4] = (U_LONG)omega->hour;
+ bug->values[5] = (U_LONG)omega->minute;
+ bug->values[6] = (U_LONG)omega->second;
+ bug->values[7] = (U_LONG)omega->msec;
+ bug->values[8] = omega->noreply;
+ bug->values[9] = omega->yearstart;
+ bug->values[10] = omega->quality;
+ bug->stimes = 0x1c;
+ bug->times[0] = omega->lastref;
+ bug->times[1] = omega->lastrec;
+ bug->times[2] = omega->offset[0];
+ bug->times[3] = omega->offset[1];
+ bug->times[4] = omega->offset[2];
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_parse.c
new file mode 100644
index 0000000..6f7683e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_parse.c
@@ -0,0 +1,3925 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp
+ *
+ * refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp
+ *
+ * generic reference clock driver for receivers
+ *
+ * Added support for the Boeder DCF77 receiver on FreeBSD
+ * by Vincenzo Capuano 1995/04/18.
+ *
+ * make use of a STREAMS module for input processing where
+ * available and configured. Currently the STREAMS module
+ * is only available for Suns running SunOS 4.x and SunOS5.x (new - careful!)
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * Defines:
+ * REFCLOCK && (PARSE||PARSEPPS)
+ * - enable this mess
+ * STREAM - allow for STREAMS modules
+ * ("parse", "ppsclocd", "ppsclock")
+ * PARSEPPS - provide PPS information to loopfilter (for
+ * backward compatibilty only)
+ * PPS - supply loopfilter with PPS samples (if configured)
+ * PPSPPS - notify loopfilter of PPS file descriptor
+ *
+ * FREEBSD_CONRAD - Make very cheap "Conrad DCF77 RS-232" gadget work
+ * with FreeBSD.
+ * BOEDER - Make cheap "Boeder DCF77 RS-232" receiver work
+ * with FreeBSD.
+ * TTY defines:
+ * HAVE_BSD_TTYS - currently unsupported
+ * HAVE_SYSV_TTYS - will use termio.h
+ * HAVE_TERMIOS - will use termios.h
+ * STREAM - will use streams and implies HAVE_TERMIOS
+ */
+
+/*
+ * This driver currently provides the support for
+ * - Meinberg DCF77 receiver DCF77 PZF 535 (TCXO version) (DCF)
+ * - Meinberg DCF77 receiver DCF77 PZF 535 (OCXO version) (DCF)
+ * - Meinberg DCF77 receiver U/A 31 (DCF)
+ * - ELV DCF7000 (DCF)
+ * - Schmid clock (DCF)
+ * - Conrad DCF77 receiver module (DCF)
+ * - Boeder DCF77 receiver (DCF)
+ * - FAU DCF77 NTP receiver (TimeBrick) (DCF)
+ * - Meinberg GPS166 (GPS)
+ * - Trimble SV6 (TSIP and TAIP protocol) (GPS)
+ *
+ */
+
+/*
+ * Meinberg receivers are connected via a 9600 baud serial line
+ *
+ * Receivers that do NOT support:
+ * - leap second indication
+ * DCF U/A 31
+ * DCF PZF535 (stock version)
+ *
+ * so...
+ * - for PZF535 please ask for revision PZFUERL4.6 or higher
+ * (support for leap second and alternate antenna)
+ *
+ * The Meinberg GPS receiver also has a special NTP time stamp
+ * format. The firmware release is Uni-Erlangen. Only this
+ * firmware release is supported by xntp3.
+ *
+ * Meinberg generic receiver setup:
+ * output time code every second
+ * Baud rate 9600 7E2S
+ */
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/errno.h>
+#ifdef FREEBSD_CONRAD
+#include <sys/ioctl.h>
+#endif
+extern int errno;
+
+#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
+/* #error NEED TO DEFINE ONE OF "STREAM" or "HAVE_SYSV_TTYS" */
+NEED TO DEFINE ONE OF "STREAM", "HAVE_SYSV_TTYS" or "HAVE_TERMIOS"
+#endif
+
+#ifdef STREAM
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#ifndef HAVE_TERMIOS
+#define HAVE_TERMIOS
+#endif
+#endif
+
+#ifdef HAVE_TERMIOS
+#include <termios.h>
+#define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+#undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+#include <termio.h>
+#define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifdef HAVE_BSD_TTYS
+/* #error CURRENTLY NO BSD TTY SUPPORT */
+CURRENTLY NO BSD TTY SUPPORT
+#endif
+
+#if !defined(O_RDWR) /* XXX SOLARIS */
+#include <fcntl.h>
+#endif /* !def(O_RDWR) */
+
+#ifdef PPSPPS
+#include <sys/ppsclock.h>
+#endif
+
+#include "ntp_select.h"
+#include "ntp_stdlib.h"
+
+#include "parse.h"
+
+#if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__)
+static char rcsid[]="refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp";
+#endif
+
+/**===========================================================================
+ ** external interface to xntp mechanism
+ **/
+
+static void parse_init P((void));
+static int parse_start P((int, struct peer *));
+static void parse_shutdown P((int, struct peer *));
+static void parse_poll P((int, struct peer *));
+static void parse_control P((int, struct refclockstat *, struct refclockstat *));
+
+#define parse_buginfo noentry
+
+struct refclock refclock_parse = {
+ parse_start,
+ parse_shutdown,
+ parse_poll,
+ parse_control,
+ parse_init,
+ parse_buginfo,
+ NOFLAGS
+};
+
+/*
+ * the unit field selects for one the prototype to be used (lower 4 bits)
+ * and for the other the clock type in case of different but similar
+ * receivers (bits 4-6)
+ * the most significant bit encodes PPS support
+ * when the most significant bit is set the pps telegrams will be used
+ * for controlling the local clock (ntp_loopfilter.c)
+ * receiver specific configration data is kept in the clockinfo field.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
+#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
+
+/**===========================================================================
+ ** function vector for dynamically binding io handling mechanism
+ **/
+
+typedef struct bind
+{
+ char *bd_description; /* name of type of binding */
+ int (*bd_init)(); /* initialize */
+ void (*bd_end)(); /* end */
+ int (*bd_setcs)(); /* set character size */
+ int (*bd_disable)(); /* disable */
+ int (*bd_enable)(); /* enable */
+ int (*bd_getfmt)(); /* get format */
+ int (*bd_setfmt)(); /* setfmt */
+ int (*bd_getstat)(); /* getstat */
+ int (*bd_setstat)(); /* setstat */
+ int (*bd_timecode)(); /* get time code */
+ void (*bd_receive)(); /* receive operation */
+ void (*bd_poll)(); /* poll operation */
+} bind_t;
+
+#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
+#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
+#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
+#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
+#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
+#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
+#define PARSE_GETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_getstat)(_X_, _DCT_)
+#define PARSE_SETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_setstat)(_X_, _DCT_)
+#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
+#define PARSE_POLL(_X_) (*(_X_)->binding->bd_poll)(_X_)
+
+/*
+ * io modes
+ */
+#define PARSE_F_NOPOLLONLY 0x0001 /* always do async io (possible PPS support via PARSE) */
+#define PARSE_F_POLLONLY 0x0002 /* never do async io (no PPS support via PARSE) */
+#define PARSE_F_PPSPPS 0x0004 /* use loopfilter PPS code (CIOGETEV) */
+#define PARSE_F_PPSONSECOND 0x0008 /* PPS pulses are on second */
+
+/**===========================================================================
+ ** refclock instance data
+ **/
+
+struct parseunit
+{
+ /*
+ * XNTP management
+ */
+ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
+ int fd; /* device file descriptor */
+ u_char unit; /* encoded unit/type/PPS */
+
+ /*
+ * XNTP io
+ */
+ struct refclockio io; /* io system structure (used in PPS mode) */
+ bind_t *binding; /* io handling binding */
+
+ /*
+ * parse state
+ */
+ parse_t parseio; /* io handling structure (user level parsing) */
+
+ /*
+ * type specific parameters
+ */
+ struct my_clockinfo *parse_type; /* link to clock description */
+
+ /*
+ * clock specific configuration
+ */
+ l_fp basedelay; /* clock local phase offset */
+ l_fp ppsdelay; /* clock local pps phase offset */
+
+ /*
+ * clock state handling/reporting
+ */
+ u_char flags; /* flags (leap_control) */
+ u_char status; /* current status */
+ u_char lastevent; /* last not NORMAL status */
+ U_LONG lastchange; /* time (xntp) when last state change accured */
+ U_LONG statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
+ struct event stattimer; /* statistics timer */
+ U_LONG polls; /* polls from NTP protocol machine */
+ U_LONG noresponse; /* number of expected but not seen datagrams */
+ U_LONG badformat; /* bad format (failed format conversions) */
+ U_LONG baddata; /* usually bad receive length, bad format */
+
+ u_char pollonly; /* 1 for polling only (no PPS mode) */
+ u_char pollneeddata; /* 1 for receive sample expected in PPS mode */
+ U_LONG laststatus; /* last packet status (error indication) */
+ u_short lastformat; /* last format used */
+ U_LONG lastsync; /* time (xntp) when clock was last seen fully synchronized */
+ U_LONG timestarted; /* time (xntp) when peer clock was instantiated */
+ U_LONG nosynctime; /* time (xntp) when last nosync message was posted */
+ U_LONG lastmissed; /* time (xntp) when poll didn't get data (powerup heuristic) */
+ U_LONG ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
+ parsetime_t time; /* last (parse module) data */
+ void *localdata; /* optional local data */
+};
+
+
+/**===========================================================================
+ ** Clockinfo section all parameter for specific clock types
+ ** includes NTP paramaters, TTY parameters and IO handling parameters
+ **/
+
+static void poll_dpoll P((struct parseunit *));
+static void poll_poll P((struct parseunit *));
+static int poll_init P((struct parseunit *));
+static void poll_end P((struct parseunit *));
+
+typedef struct poll_info
+{
+ U_LONG rate; /* poll rate - once every "rate" seconds - 0 off */
+ char * string; /* string to send for polling */
+ U_LONG count; /* number of charcters in string */
+} poll_info_t;
+
+#define NO_FLAGS 0
+#define NO_POLL (void (*)())0
+#define NO_INIT (int (*)())0
+#define NO_END (void (*)())0
+#define NO_DATA (void *)0
+#define NO_FORMAT ""
+#define NO_PPSDELAY 0
+
+#define DCF_ID "DCF" /* generic DCF */
+#define DCF_A_ID "DCFa" /* AM demodulation */
+#define DCF_P_ID "DCFp" /* psuedo random phase shift */
+#define GPS_ID "GPS" /* GPS receiver */
+
+#define NOCLOCK_ROOTDELAY 0x00000000
+#define NOCLOCK_BASEDELAY 0x00000000
+#define NOCLOCK_DESCRIPTION ((char *)0)
+#define NOCLOCK_MAXUNSYNC 0
+#define NOCLOCK_CFLAG 0
+#define NOCLOCK_IFLAG 0
+#define NOCLOCK_OFLAG 0
+#define NOCLOCK_LFLAG 0
+#define NOCLOCK_ID "TILT"
+#define NOCLOCK_POLL NO_POLL
+#define NOCLOCK_INIT NO_INIT
+#define NOCLOCK_END NO_END
+#define NOCLOCK_DATA NO_DATA
+#define NOCLOCK_FORMAT NO_FORMAT
+#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
+
+#define DCF_TYPE CTL_SST_TS_LF
+#define GPS_TYPE CTL_SST_TS_UHF
+
+/*
+ * receiver specific constants
+ */
+#define MBG_CFLAG19200 (B19200|CS7|PARENB|CREAD|HUPCL)
+#define MBG_CFLAG (B9600|CS7|PARENB|CREAD|HUPCL)
+#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define MBG_OFLAG 0
+#define MBG_LFLAG 0
+/*
+ * Meinberg DCF U/A 31 (AM) receiver
+ */
+#define DCFUA31_ROOTDELAY 0x00000D00 /* 50.78125ms */
+#define DCFUA31_BASEDELAY 0x02C00000 /* 10.7421875ms: 10 ms (+/- 3 ms) */
+#define DCFUA31_DESCRIPTION "Meinberg DCF U/A 31"
+#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
+#define DCFUA31_CFLAG MBG_CFLAG
+#define DCFUA31_IFLAG MBG_IFLAG
+#define DCFUA31_OFLAG MBG_OFLAG
+#define DCFUA31_LFLAG MBG_LFLAG
+
+/*
+ * Meinberg DCF PZF535/TCXO (FM/PZF) receiver
+ */
+#define DCFPZF535_ROOTDELAY 0x00000034 /* 800us */
+#define DCFPZF535_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/TCXO"
+#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
+ * @ 5e-8df/f we have accumulated
+ * at most 2.16 ms (thus we move to
+ * NTP synchronisation */
+#define DCFPZF535_CFLAG MBG_CFLAG
+#define DCFPZF535_IFLAG MBG_IFLAG
+#define DCFPZF535_OFLAG MBG_OFLAG
+#define DCFPZF535_LFLAG MBG_LFLAG
+
+
+/*
+ * Meinberg DCF PZF535/OCXO receiver
+ */
+#define DCFPZF535OCXO_ROOTDELAY 0x00000034 /* 800us (max error * 10) */
+#define DCFPZF535OCXO_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/OCXO"
+#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define DCFPZF535OCXO_CFLAG MBG_CFLAG
+#define DCFPZF535OCXO_IFLAG MBG_IFLAG
+#define DCFPZF535OCXO_OFLAG MBG_OFLAG
+#define DCFPZF535OCXO_LFLAG MBG_LFLAG
+
+/*
+ * Meinberg GPS166 receiver
+ */
+#define GPS166_ROOTDELAY 0x00000000 /* nothing here */
+#define GPS166_BASEDELAY 0x00800000 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define GPS166_DESCRIPTION "Meinberg GPS166 receiver"
+#define GPS166_MAXUNSYNC 0 /* this clock is immediately lost */
+#define GPS166_CFLAG MBG_CFLAG
+#define GPS166_IFLAG MBG_IFLAG
+#define GPS166_OFLAG MBG_OFLAG
+#define GPS166_LFLAG MBG_LFLAG
+#define GPS166_POLL NO_POLL
+#define GPS166_INIT NO_INIT
+#define GPS166_END NO_END
+#define GPS166_DATA NO_DATA
+#define GPS166_ID GPS_ID
+#define GPS166_FORMAT NO_FORMAT
+
+/*
+ * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
+ *
+ * This is really not the hottest clock - but before you have nothing ...
+ */
+#define DCF7000_ROOTDELAY 0x00000364 /* 13 ms */
+#define DCF7000_BASEDELAY 0x67AE0000 /* 405 ms - slow blow */
+#define DCF7000_DESCRIPTION "ELV DCF7000"
+#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
+#define DCF7000_CFLAG (B9600|CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
+#define DCF7000_IFLAG (IGNBRK)
+#define DCF7000_OFLAG 0
+#define DCF7000_LFLAG 0
+
+/*
+ * Schmid DCF Receiver Kit
+ *
+ * When the WSDCF clock is operating optimally we want the primary clock
+ * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
+ * structure is set to 290 ms and we compute delays which are at least
+ * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
+#define WS_POLLCMD "\163"
+#define WS_CMDSIZE 1
+
+static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
+
+#define WSDCF_INIT poll_init
+#define WSDCF_POLL poll_dpoll
+#define WSDCF_END poll_end
+#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
+#define WSDCF_ROOTDELAY 0X00004A3D /* ~ 290ms */
+#define WSDCF_BASEDELAY 0x028F5C29 /* ~ 10ms */
+#define WSDCF_DESCRIPTION "WS/DCF Receiver"
+#define WSDCF_FORMAT "Schmid"
+#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
+#define WSDCF_CFLAG (B1200|CS8|CREAD|CLOCAL)
+#define WSDCF_IFLAG 0
+#define WSDCF_OFLAG 0
+#define WSDCF_LFLAG 0
+
+/*
+ * RAW DCF77 - input of DCF marks via RS232 - many variants
+ */
+#define RAWDCF_FLAGS PARSE_F_NOPOLLONLY
+#define RAWDCF_ROOTDELAY 0x00000364 /* 13 ms */
+#define RAWDCF_FORMAT "RAW DCF77 Timecode"
+#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
+
+#if defined(FREEBSD_CONRAD) || (defined(SYS_FREEBSD) && defined(BOEDER))
+#define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
+#else
+#define RAWDCF_CFLAG (B50|CS8|CREAD|CLOCAL)
+#endif
+#define RAWDCF_IFLAG 0
+#define RAWDCF_OFLAG 0
+#define RAWDCF_LFLAG 0
+
+/*
+ * RAW DCF variants
+ */
+/*
+ * Conrad receiver
+ *
+ * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
+ * (~40DM - roughly $30 ) followed by a level converter for RS232
+ */
+#define CONRAD_BASEDELAY 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud on a Sun */
+#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
+
+/*
+ * Boeder receiver
+ *
+ * simple (cheap) DCF clock - e. g. DCF77 receiver by Boeder
+ * followed by a level converter for RS232
+ */
+#define BOEDER_BASEDELAY 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud */
+#define BOEDER_DESCRIPTION "RAW DCF77 CODE (BOEDER DCF77 receiver)"
+
+/*
+ * TimeBrick receiver
+ */
+#define TIMEBRICK_BASEDELAY 0x35C29000 /* ~210 ms - TimeBrick @ 50 Baud on a Sun */
+#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
+
+/*
+ * Trimble SV6 GPS receivers (TAIP and TSIP protocols)
+ */
+#define ETX 0x03
+#define DLE 0x10
+
+#define TRIM_POLLRATE 0 /* only true direct polling */
+
+#define TRIM_TAIPPOLLCMD ">QTM<"
+#define TRIM_TAIPCMDSIZE 5
+static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
+static int trimbletaip_init P((struct parseunit *));
+
+/* query time & UTC correction data */
+static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
+
+static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
+static int trimbletsip_init P((struct parseunit *));
+
+#define TRIMBLETAIP_CFLAG (B4800|CS8|CREAD)
+#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
+#define TRIMBLETAIP_LFLAG (ICANON|ECHOK)
+#define TRIMBLETSIP_CFLAG (B9600|CS8|CLOCAL|CREAD|PARENB|PARODD)
+#define TRIMBLETSIP_IFLAG (IGNBRK)
+#define TRIMBLETSIP_OFLAG (0)
+#define TRIMBLETSIP_LFLAG (0)
+
+#define TRIMBLETAIP_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND)
+#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS|PARSE_F_NOPOLLONLY)
+
+#define TRIMBLETAIP_POLL poll_dpoll
+#define TRIMBLETSIP_POLL poll_dpoll
+
+#define TRIMBLETAIP_INIT trimbletaip_init
+#define TRIMBLETSIP_INIT trimbletsip_init
+
+#define TRIMBLETAIP_END poll_end
+#define TRIMBLETSIP_END poll_end
+
+#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
+#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
+
+#define TRIMBLETAIP_ID GPS_ID
+#define TRIMBLETSIP_ID GPS_ID
+
+#define TRIMBLETAIP_FORMAT NO_FORMAT
+#define TRIMBLETSIP_FORMAT "Trimble SV6/TSIP"
+
+#define TRIMBLETAIP_ROOTDELAY 0x0
+#define TRIMBLETSIP_ROOTDELAY 0x0
+
+#define TRIMBLETAIP_BASEDELAY 0x0
+#define TRIMBLETSIP_BASEDELAY 0x51EB852 /* 20 ms as a l_uf - avg GPS time message latency */
+
+#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
+#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
+
+#define TRIMBLETAIP_MAXUNSYNC 0
+#define TRIMBLETSIP_MAXUNSYNC 0
+
+#define TRIMBLETAIP_EOL '<'
+
+static struct my_clockinfo
+{
+ U_LONG cl_flags; /* operation flags (io modes) */
+ void (*cl_poll)(); /* active poll routine */
+ int (*cl_init)(); /* active poll init routine */
+ void (*cl_end)(); /* active poll end routine */
+ void *cl_data; /* local data area for "poll" mechanism */
+ u_fp cl_rootdelay; /* rootdelay */
+ U_LONG cl_basedelay; /* current offset - unsigned l_fp fractional part */
+ U_LONG cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional part */
+ char *cl_id; /* ID code (usually "DCF") */
+ char *cl_description; /* device name */
+ char *cl_format; /* fixed format */
+ u_char cl_type; /* clock type (ntp control) */
+ U_LONG cl_maxunsync; /* time to trust oscillator after loosing synch */
+ U_LONG cl_cflag; /* terminal io flags */
+ U_LONG cl_iflag; /* terminal io flags */
+ U_LONG cl_oflag; /* terminal io flags */
+ U_LONG cl_lflag; /* terminal io flags */
+} clockinfo[] =
+{ /* 0. 0.0.128 - base offset for PPS support */
+ { /* 127.127.8.<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFPZF535_ROOTDELAY,
+ DCFPZF535_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_P_ID,
+ DCFPZF535_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535_MAXUNSYNC,
+ DCFPZF535_CFLAG,
+ DCFPZF535_IFLAG,
+ DCFPZF535_OFLAG,
+ DCFPZF535_LFLAG
+ },
+ { /* 127.127.8.4+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFPZF535OCXO_ROOTDELAY,
+ DCFPZF535OCXO_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_P_ID,
+ DCFPZF535OCXO_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535OCXO_MAXUNSYNC,
+ DCFPZF535OCXO_CFLAG,
+ DCFPZF535OCXO_IFLAG,
+ DCFPZF535OCXO_OFLAG,
+ DCFPZF535OCXO_LFLAG
+ },
+ { /* 127.127.8.8+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG
+ },
+ { /* 127.127.8.12+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCF7000_ROOTDELAY,
+ DCF7000_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ DCF7000_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCF7000_MAXUNSYNC,
+ DCF7000_CFLAG,
+ DCF7000_IFLAG,
+ DCF7000_OFLAG,
+ DCF7000_LFLAG
+ },
+ { /* 127.127.8.16+<device> */
+ NO_FLAGS,
+ WSDCF_POLL,
+ WSDCF_INIT,
+ WSDCF_END,
+ WSDCF_DATA,
+ WSDCF_ROOTDELAY,
+ WSDCF_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ WSDCF_DESCRIPTION,
+ WSDCF_FORMAT,
+ DCF_TYPE,
+ WSDCF_MAXUNSYNC,
+ WSDCF_CFLAG,
+ WSDCF_IFLAG,
+ WSDCF_OFLAG,
+ WSDCF_LFLAG
+ },
+ { /* 127.127.8.20+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ CONRAD_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ CONRAD_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ },
+ { /* 127.127.8.24+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ TIMEBRICK_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ TIMEBRICK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ },
+ { /* 127.127.8.28+<device> */
+ NO_FLAGS,
+ GPS166_POLL,
+ GPS166_INIT,
+ GPS166_END,
+ GPS166_DATA,
+ GPS166_ROOTDELAY,
+ GPS166_BASEDELAY,
+ NO_PPSDELAY,
+ GPS166_ID,
+ GPS166_DESCRIPTION,
+ GPS166_FORMAT,
+ GPS_TYPE,
+ GPS166_MAXUNSYNC,
+ GPS166_CFLAG,
+ GPS166_IFLAG,
+ GPS166_OFLAG,
+ GPS166_LFLAG
+ },
+ { /* 127.127.8.32+<device> */
+ TRIMBLETAIP_FLAGS,
+ TRIMBLETAIP_POLL,
+ TRIMBLETAIP_INIT,
+ TRIMBLETAIP_END,
+ TRIMBLETAIP_DATA,
+ TRIMBLETAIP_ROOTDELAY,
+ TRIMBLETAIP_BASEDELAY,
+ NO_PPSDELAY,
+ TRIMBLETAIP_ID,
+ TRIMBLETAIP_DESCRIPTION,
+ TRIMBLETAIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETAIP_MAXUNSYNC,
+ TRIMBLETAIP_CFLAG,
+ TRIMBLETAIP_IFLAG,
+ TRIMBLETAIP_OFLAG,
+ TRIMBLETAIP_LFLAG
+ },
+ { /* 127.127.8.36+<device> */
+ TRIMBLETSIP_FLAGS,
+ TRIMBLETSIP_POLL,
+ TRIMBLETSIP_INIT,
+ TRIMBLETSIP_END,
+ TRIMBLETSIP_DATA,
+ TRIMBLETSIP_ROOTDELAY,
+ TRIMBLETSIP_BASEDELAY,
+ NO_PPSDELAY,
+ TRIMBLETSIP_ID,
+ TRIMBLETSIP_DESCRIPTION,
+ TRIMBLETSIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETSIP_MAXUNSYNC,
+ TRIMBLETSIP_CFLAG,
+ TRIMBLETSIP_IFLAG,
+ TRIMBLETSIP_OFLAG,
+ TRIMBLETSIP_LFLAG
+ },
+ { /* 127.127.8.40+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ BOEDER_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ BOEDER_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ }
+};
+
+static int ncltypes = sizeof(clockinfo) / sizeof(struct my_clockinfo);
+
+#define CL_REALTYPE(x) (((x) >> 2) & 0x1F)
+#define CL_TYPE(x) ((CL_REALTYPE(x) >= ncltypes) ? ~0 : CL_REALTYPE(x))
+#define CL_PPS(x) ((x) & 0x80)
+#define CL_UNIT(x) ((x) & 0x3)
+
+/*
+ * Other constant stuff
+ */
+#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
+
+#define PARSENOSYNCREPEAT (10*60) /* mention uninitialized clocks all 10 minutes */
+#define PARSESTATISTICS (60*60) /* output state statistics every hour */
+
+static struct parseunit *parseunits[MAXUNITS];
+
+extern U_LONG current_time;
+extern s_char sys_precision;
+extern struct event timerqueue[];
+#ifdef PPSPPS
+extern int fdpps;
+#endif
+
+static int notice = 0;
+
+#define PARSE_STATETIME(parse, i) ((parse->status == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
+
+static void parse_event P((struct parseunit *, int));
+static void parse_process P((struct parseunit *, parsetime_t *));
+
+/**===========================================================================
+ ** implementation of i/o handling methods
+ ** (all STREAM, partial STREAM, user level)
+ **/
+
+/*
+ * define possible io handling methods
+ */
+#ifdef STREAM
+static int ppsclock_init P((struct parseunit *));
+static int stream_init P((struct parseunit *));
+static void stream_nop P((struct parseunit *));
+static int stream_enable P((struct parseunit *));
+static int stream_disable P((struct parseunit *));
+static int stream_setcs P((struct parseunit *, parsectl_t *));
+static int stream_getfmt P((struct parseunit *, parsectl_t *));
+static int stream_setfmt P((struct parseunit *, parsectl_t *));
+static int stream_getstat P((struct parseunit *, parsectl_t *));
+static int stream_setstat P((struct parseunit *, parsectl_t *));
+static int stream_timecode P((struct parseunit *, parsectl_t *));
+static void stream_receive P((struct recvbuf *));
+static void stream_poll P((struct parseunit *));
+#endif
+
+static int local_init P((struct parseunit *));
+static void local_end P((struct parseunit *));
+static int local_nop P((struct parseunit *));
+static int local_setcs P((struct parseunit *, parsectl_t *));
+static int local_getfmt P((struct parseunit *, parsectl_t *));
+static int local_setfmt P((struct parseunit *, parsectl_t *));
+static int local_getstat P((struct parseunit *, parsectl_t *));
+static int local_setstat P((struct parseunit *, parsectl_t *));
+static int local_timecode P((struct parseunit *, parsectl_t *));
+static void local_receive P((struct recvbuf *));
+static void local_poll P((struct parseunit *));
+
+static bind_t io_bindings[] =
+{
+#ifdef STREAM
+ {
+ "parse STREAM",
+ stream_init,
+ stream_nop,
+ stream_setcs,
+ stream_disable,
+ stream_enable,
+ stream_getfmt,
+ stream_setfmt,
+ stream_getstat,
+ stream_setstat,
+ stream_timecode,
+ stream_receive,
+ stream_poll
+ },
+ {
+ "ppsclock STREAM",
+ ppsclock_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_getstat,
+ local_setstat,
+ local_timecode,
+ local_receive,
+ local_poll
+ },
+#endif
+ {
+ "normal",
+ local_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_getstat,
+ local_setstat,
+ local_timecode,
+ local_receive,
+ local_poll
+ },
+ {
+ (char *)0,
+ }
+};
+
+#ifdef STREAM
+/*--------------------------------------------------
+ * ppsclock STREAM init
+ */
+static int
+ppsclock_init(parse)
+ struct parseunit *parse;
+{
+ /*
+ * now push the parse streams module
+ * it will ensure exclusive access to the device
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclocd") == -1 &&
+ ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclock") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
+ CL_UNIT(parse->unit));
+ return 0;
+ }
+ if (!local_init(parse))
+ {
+ (void)ioctl(parse->fd, I_POP, (caddr_t)0);
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse STREAM init
+ */
+static int
+stream_init(parse)
+ struct parseunit *parse;
+{
+ /*
+ * now push the parse streams module
+ * to test whether it is there (Oh boy - neat kernel interface)
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ while(ioctl(parse->fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+
+ /*
+ * now push it a second time after we have removed all
+ * module garbage
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+ /*--------------------------------------------------
+ * STREAM setcs
+ */
+static int
+stream_setcs(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETCS;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM nop
+ */
+static void
+stream_nop(parse)
+ struct parseunit *parse;
+{
+}
+
+/*--------------------------------------------------
+ * STREAM enable
+ */
+static int
+stream_enable(parse)
+ struct parseunit *parse;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_ENABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM disable
+ */
+static int
+stream_disable(parse)
+ struct parseunit *parse;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_DISABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getfmt
+ */
+static int
+stream_getfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setfmt
+ */
+static int
+stream_setfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getstat
+ */
+static int
+stream_getstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_getstat: ioctl(fd, I_STR, PARSEIOC_GETSTAT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setstat
+ */
+static int
+stream_setstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setstat: ioctl(fd, I_STR, PARSEIOC_SETSTAT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM timecode
+ */
+static int
+stream_timecode(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_TIMECODE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_process: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CL_UNIT(parse->unit), parse->fd);
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM receive
+ */
+static void
+stream_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
+ parsetime_t parsetime;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: parse_receive: bad size (got %d expected %d)",
+ CL_UNIT(parse->unit), rbufp->recv_length, sizeof(parsetime_t));
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ memmove((caddr_t)&parsetime,
+ (caddr_t)&rbufp->recv_space,
+ sizeof(parsetime_t));
+
+ /*
+ * switch time stamp world - be sure to normalize small usec field
+ * errors.
+ */
+
+#define fix_ts(_X_) \
+ if ((&(_X_))->tv.tv_usec >= 1000000) \
+ { \
+ (&(_X_))->tv.tv_usec -= 1000000; \
+ (&(_X_))->tv.tv_sec += 1; \
+ }
+
+#define cvt_ts(_X_, _Y_) \
+ { \
+ l_fp ts; \
+ \
+ fix_ts((_X_)); \
+ if (!buftvtots((const char *)&(&(_X_))->tv, &ts)) \
+ { \
+ syslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%d.%06d) ", (_Y_), (&(_X_))->tv.tv_sec, (&(_X_))->tv.tv_usec);\
+ return; \
+ } \
+ else \
+ { \
+ (&(_X_))->fp = ts; \
+ } \
+ }
+
+ if (PARSE_TIMECODE(parsetime.parse_state))
+ {
+ cvt_ts(parsetime.parse_time, "parse_time");
+ cvt_ts(parsetime.parse_stime, "parse_stime");
+ }
+
+ if (PARSE_PPS(parsetime.parse_state))
+ cvt_ts(parsetime.parse_ptime, "parse_ptime");
+
+ parse_process(parse, &parsetime);
+}
+
+/*--------------------------------------------------
+ * STREAM poll
+ */
+static void
+stream_poll(parse)
+ struct parseunit *parse;
+{
+ register int fd, i, rtc;
+ fd_set fdmask;
+ struct timeval timeout, starttime, curtime, selecttime;
+ parsetime_t parsetime;
+
+ /*
+ * now we do the following:
+ * - read the first packet from the parse module (OLD !!!)
+ * - read the second packet from the parse module (fresh)
+ * - compute values for xntp
+ */
+
+ FD_ZERO(&fdmask);
+ fd = parse->fd;
+ FD_SET(fd, &fdmask);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; /* 0.5 sec */
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * elapsed real time passed timeout value - consider it timed out
+ */
+ break;
+ }
+
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device", CL_UNIT(parse->unit));
+ }
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ return;
+ }
+
+ while (((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
+ {
+ /* bad packet */
+ if ( i == -1)
+ {
+ if (errno == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+
+ return;
+ }
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000; /* 1.500 sec */
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * elapsed real time passed timeout value - consider it timed out
+ */
+ break;
+ }
+
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device", CL_UNIT(parse->unit));
+ }
+
+ /*
+ * we will return here iff we got a good old sample as this would
+ * be misinterpreted. bad samples are passed on to be logged into the
+ * state statistics
+ */
+ if ((parsetime.parse_status & CVT_MASK) == CVT_OK)
+ {
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+ return;
+ }
+ }
+
+ /*
+ * we get here either by a possible read() (rtc == 1 - while assertion)
+ * or by a timeout or a system call error. when a read() is possible we
+ * get the new data, otherwise we stick with the old
+ */
+ if ((rtc == 1) && ((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
+ {
+ /* bad packet */
+ if ( i== -1)
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+
+ return;
+ }
+
+ /*
+ * process what we got
+ */
+ parse_process(parse, &parsetime);
+}
+#endif
+
+/*--------------------------------------------------
+ * local init
+ */
+static int
+local_init(parse)
+ struct parseunit *parse;
+{
+ return parse_ioinit(&parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local end
+ */
+static void
+local_end(parse)
+ struct parseunit *parse;
+{
+ parse_ioend(&parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local nop
+ */
+static int
+local_nop(parse)
+ struct parseunit *parse;
+{
+ return 1;
+}
+
+/*--------------------------------------------------
+ * local setcs
+ */
+static int
+local_setcs(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setcs(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getfmt
+ */
+static int
+local_getfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_getfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setfmt
+ */
+static int
+local_setfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getstat
+ */
+static int
+local_getstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_getstat(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setstat
+ */
+static int
+local_setstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setstat(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local timecode
+ */
+static int
+local_timecode(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_timecode(tcl, &parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local receive
+ */
+static void
+local_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
+ register int count;
+ register char *s;
+#ifdef FREEBSD_CONRAD
+ struct timeval foo;
+#endif
+
+ /*
+ * eat all characters, parsing then and feeding complete samples
+ */
+ count = rbufp->recv_length;
+ s = rbufp->recv_buffer;
+#ifdef FREEBSD_CONRAD
+ ioctl(parse->fd,TIOCTIMESTAMP,&foo);
+ TVTOTS(&foo, &rbufp->recv_time);
+ rbufp->recv_time.l_uf += TS_ROUNDBIT;
+ rbufp->recv_time.l_ui += JAN_1970;
+ rbufp->recv_time.l_uf &= TS_MASK;
+#endif
+
+ while (count--)
+ {
+ if (parse_ioread(&parse->parseio, *s++, (timestamp_t *)&rbufp->recv_time))
+ {
+ /*
+ * got something good to eat
+ */
+#ifdef PPSPPS
+ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state) &&
+ (parse->flags & PARSE_PPSCLOCK))
+ {
+ l_fp ts;
+ struct ppsclockev ev;
+
+ if (ioctl(parse->fd, CIOGETEV, (caddr_t)&ev) == 0)
+ {
+ if (ev.serial != parse->ppsserial)
+ {
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (!buftvtots((const char *)&ev.tv, &ts))
+ {
+ syslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
+ }
+ else
+ {
+ parse->parseio.parse_dtime.parse_ptime.fp = ts;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+ parse->ppsserial = ev.serial;
+ }
+ }
+#endif
+ parse_process(parse, &parse->parseio.parse_dtime);
+ parse_iodone(&parse->parseio);
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * local poll
+ */
+static void
+local_poll(parse)
+ struct parseunit *parse;
+{
+ register int fd, i, rtc;
+ fd_set fdmask;
+ struct timeval timeout, starttime, curtime, selecttime;
+ static struct timeval null_time = { 0, 0};
+ timestamp_t ts;
+
+ FD_ZERO(&fdmask);
+ fd = parse->fd;
+ FD_SET(fd, &fdmask);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000; /* 1.5 sec */
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ do
+ {
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (!timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device", CL_UNIT(parse->unit));
+ }
+
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ return;
+ }
+
+ /*
+ * at least 1 character is available - gobble everthing up that is available
+ */
+ do
+ {
+ char inbuf[256];
+
+ register char *s = inbuf;
+
+ rtc = i = read(fd, inbuf, sizeof(inbuf));
+
+ get_systime(&ts.fp);
+
+ while (i-- > 0)
+ {
+ if (parse_ioread(&parse->parseio, *s++, &ts))
+ {
+ /*
+ * got something good to eat
+ */
+ parse_process(parse, &parse->parseio.parse_dtime);
+ parse_iodone(&parse->parseio);
+ /*
+ * done if no more characters are available
+ */
+ FD_SET(fd, &fdmask);
+ if ((i == 0) &&
+ (select(fd + 1, &fdmask, 0, 0, &null_time) == 0))
+ return;
+ }
+ }
+ FD_SET(fd, &fdmask);
+ } while ((rtc = select(fd + 1, &fdmask, 0, 0, &null_time)) == 1);
+ FD_SET(fd, &fdmask);
+ } while (1);
+}
+
+/*--------------------------------------------------
+ * init_iobinding - find and initialize lower layers
+ */
+static bind_t *
+init_iobinding(parse)
+ struct parseunit *parse;
+{
+ register bind_t *b = io_bindings;
+
+ while (b->bd_description != (char *)0)
+ {
+ if ((*b->bd_init)(parse))
+ {
+ return b;
+ }
+ b++;
+ }
+ return (bind_t *)0;
+}
+
+/**===========================================================================
+ ** support routines
+ **/
+
+/*--------------------------------------------------
+ * convert a flag field to a string
+ */
+static char *
+parsestate(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADD WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ unsigned LONG bit;
+ char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ register char *s, *t;
+
+ if (buffer[0])
+ strcat(buffer, "; ");
+
+ strcat(buffer, "(");
+
+ t = s = buffer + strlen(buffer);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & state)
+ {
+ if (t != s)
+ {
+ strcpy(t, "; ");
+ t += 2;
+ }
+
+ strcpy(t, sflagstrings[i].name);
+ t += strlen(t);
+ }
+ i++;
+ }
+ strcpy(t, ")");
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *
+parsestatus(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a clock status flag field to a string
+ */
+static char *
+clockstatus(state)
+ unsigned LONG state;
+{
+ static char buffer[20];
+ static struct status
+ {
+ unsigned LONG value;
+ char *name;
+ } flagstrings[] =
+ {
+ { CEVNT_NOMINAL, "NOMINAL" },
+ { CEVNT_TIMEOUT, "NO RESPONSE" },
+ { CEVNT_BADREPLY,"BAD FORMAT" },
+ { CEVNT_FAULT, "FAULT" },
+ { CEVNT_PROP, "PROPAGATION DELAY" },
+ { CEVNT_BADDATE, "ILLEGAL DATE" },
+ { CEVNT_BADTIME, "ILLEGAL TIME" },
+ { ~0 }
+ };
+ int i;
+
+ i = 0;
+ while (flagstrings[i].value != ~0)
+ {
+ if (flagstrings[i].value == state)
+ {
+ return flagstrings[i].name;
+ }
+ i++;
+ }
+
+ sprintf(buffer, "unknown #%d", state);
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * mkascii - make a printable ascii string
+ * assumes (unless defined better) 7-bit ASCII
+ */
+#ifndef isprint
+#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
+#endif
+
+static char *
+mkascii(buffer, blen, src, srclen)
+ register char *buffer;
+ register LONG blen;
+ register char *src;
+ register LONG srclen;
+{
+ register char *b = buffer;
+ register char *endb = (char *)0;
+
+ if (blen < 4)
+ return (char *)0; /* don't bother with mini buffers */
+
+ endb = buffer + blen - 4;
+
+ blen--; /* account for '\0' */
+
+ while (blen && srclen--)
+ {
+ if ((*src != '\\') && isprint(*src))
+ { /* printables are easy... */
+ *buffer++ = *src++;
+ blen--;
+ }
+ else
+ {
+ if (blen < 4)
+ {
+ while (blen--)
+ {
+ *buffer++ = '.';
+ }
+ *buffer = '\0';
+ return b;
+ }
+ else
+ {
+ if (*src == '\\')
+ {
+ strcpy(buffer,"\\\\");
+ buffer += 2;
+ blen -= 2;
+ }
+ else
+ {
+ sprintf(buffer, "\\x%02x", *src++);
+ blen -= 4;
+ buffer += 4;
+ }
+ }
+ }
+ if (srclen && !blen && endb) /* overflow - set last chars to ... */
+ strcpy(endb, "...");
+ }
+
+ *buffer = '\0';
+ return b;
+}
+
+
+/*--------------------------------------------------
+ * l_mktime - make representation of a relative time
+ */
+static char *
+l_mktime(delta)
+ unsigned LONG delta;
+{
+ unsigned LONG tmp, m, s;
+ static char buffer[40];
+
+ buffer[0] = '\0';
+
+ if ((tmp = delta / (60*60*24)) != 0)
+ {
+ sprintf(buffer, "%dd+", tmp);
+ delta -= tmp * 60*60*24;
+ }
+
+ s = delta % 60;
+ delta /= 60;
+ m = delta % 60;
+ delta /= 60;
+
+ sprintf(buffer+strlen(buffer), "%02d:%02d:%02d",
+ delta, m, s);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * parse_statistics - list summary of clock states
+ */
+static void
+parse_statistics(parse)
+ register struct parseunit *parse;
+{
+ register int i;
+
+ syslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
+ CL_UNIT(parse->unit),
+ l_mktime(current_time - parse->timestarted));
+
+ syslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
+ CL_UNIT(parse->unit),
+ clockstatus(parse->status));
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ register unsigned LONG stime;
+ register unsigned LONG percent, div = current_time - parse->timestarted;
+
+ percent = stime = PARSE_STATETIME(parse, i);
+
+ while (((unsigned LONG)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ div /= 10;
+ }
+
+ if (div)
+ percent = (percent * 10000) / div;
+ else
+ percent = 10000;
+
+ if (stime)
+ syslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3d.%02d%%)",
+ CL_UNIT(parse->unit),
+ clockstatus(i),
+ l_mktime(stime),
+ percent / 100, percent % 100);
+ }
+}
+
+/*--------------------------------------------------
+ * cparse_statistics - wrapper for statistics call
+ */
+static void
+cparse_statistics(peer)
+ register struct peer *peer;
+{
+ register struct parseunit *parse = (struct parseunit *)peer;
+
+ parse_statistics(parse);
+ parse->stattimer.event_time = current_time + PARSESTATISTICS;
+ TIMER_ENQUEUE(timerqueue, &parse->stattimer);
+}
+
+/**===========================================================================
+ ** xntp interface routines
+ **/
+
+/*--------------------------------------------------
+ * parse_init - initialize internal parse driver data
+ */
+static void
+parse_init()
+{
+ memset((caddr_t)parseunits, 0, sizeof parseunits);
+}
+
+
+/*--------------------------------------------------
+ * parse_shutdown - shut down a PARSE clock
+ */
+static void
+parse_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct parseunit *parse;
+
+ unit = CL_UNIT(unit);
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit invalid (max %d)",
+ unit,MAXUNITS);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (parse && !parse->peer) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit not in use", unit);
+ return;
+ }
+
+ /*
+ * print statistics a last time and
+ * stop statistics machine
+ */
+ parse_statistics(parse);
+ TIMER_DEQUEUE(&parse->stattimer);
+
+#if PPSPPS
+ {
+ /*
+ * kill possible PPS association
+ */
+ if (fdpps == parse->fd)
+ fdpps = -1;
+ }
+#endif
+
+ if (parse->parse_type->cl_end)
+ {
+ parse->parse_type->cl_end(parse);
+ }
+
+ if (parse->binding)
+ PARSE_END(parse);
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ if (!parse->pollonly)
+ io_closeclock(&parse->io);
+ else
+ (void) close(parse->fd);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
+ CL_UNIT(parse->unit), parse->parse_type->cl_description);
+
+ parse->peer = (struct peer *)0; /* unused now */
+}
+
+/*--------------------------------------------------
+ * parse_start - open the PARSE devices and initialize data for processing
+ */
+static int
+parse_start(sysunit, peer)
+ int sysunit;
+ struct peer *peer;
+{
+ u_int unit;
+ int fd232, i;
+#ifdef HAVE_TERMIOS
+ struct termios tm; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tm; /* NEEDED FOR A LONG TIME ! */
+#endif
+ struct parseunit * parse;
+ char parsedev[sizeof(PARSEDEVICE)+20];
+ parsectl_t tmp_ctl;
+ u_int type;
+
+ type = CL_TYPE(sysunit);
+ unit = CL_UNIT(sysunit);
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit number invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+
+ if ((type == ~0) || (clockinfo[type].cl_description == (char *)0))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
+ unit, CL_REALTYPE(sysunit), ncltypes-1);
+ return 0;
+ }
+
+ if (parseunits[unit] && parseunits[unit]->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit in use", unit);
+ return 0;
+ }
+
+ /*
+ * Unit okay, attempt to open the device.
+ */
+ (void) sprintf(parsedev, PARSEDEVICE, unit);
+
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ fd232 = open(parsedev, O_RDONLY | O_NONBLOCK, 0777);
+#else
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+ fd232 = open(parsedev, O_RDWR|O_NOCTTY, 0777);
+#endif
+ if (fd232 == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
+ return 0;
+ }
+
+ /*
+ * Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc()
+ * one.
+ */
+ if (parseunits[unit])
+ {
+ parse = parseunits[unit]; /* The one we want is okay - and free */
+ }
+ else
+ {
+ for (i = 0; i < MAXUNITS; i++)
+ {
+ if (parseunits[i] && !parseunits[i]->peer)
+ break;
+ }
+ if (i < MAXUNITS)
+ {
+ /*
+ * Reclaim this one
+ */
+ parse = parseunits[i];
+ parseunits[i] = (struct parseunit *)0;
+ }
+ else
+ {
+ parse = (struct parseunit *)
+ emalloc(sizeof(struct parseunit));
+ }
+ }
+
+ memset((char *)parse, 0, sizeof(struct parseunit));
+ parseunits[unit] = parse;
+
+ /*
+ * Set up the structures
+ */
+ parse->unit = (u_char)sysunit;
+ parse->timestarted = current_time;
+ parse->lastchange = current_time;
+ /*
+ * we want to filter input for the sake of
+ * getting an impression on dispersion
+ * also we like to average the median range
+ */
+ parse->flags = PARSE_STAT_FILTER|PARSE_STAT_AVG;
+ parse->pollneeddata = 0;
+ parse->pollonly = 1; /* go for default polling mode */
+ parse->lastformat = ~0; /* assume no format known */
+ parse->status = CEVNT_TIMEOUT; /* expect the worst */
+ parse->laststatus = ~0; /* be sure to mark initial status change */
+ parse->nosynctime = 0; /* assume clock reasonable */
+ parse->lastmissed = 0; /* assume got everything */
+ parse->ppsserial = 0;
+ parse->localdata = (void *)0;
+
+ parse->parse_type = &clockinfo[type];
+
+ parse->basedelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */
+ parse->basedelay.l_uf = parse->parse_type->cl_basedelay;
+
+ parse->ppsdelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */
+ parse->ppsdelay.l_uf = parse->parse_type->cl_ppsdelay;
+
+ peer->rootdelay = parse->parse_type->cl_rootdelay;
+ peer->sstclktype = parse->parse_type->cl_type;
+ peer->precision = sys_precision;
+ peer->stratum = STRATUM_REFCLOCK;
+ if (peer->stratum <= 1)
+ memmove((char *)&peer->refid, parse->parse_type->cl_id, 4);
+ else
+ peer->refid = htonl(PARSEHSREFID);
+
+ parse->fd = fd232;
+
+ parse->peer = peer; /* marks it also as busy */
+
+ parse->binding = init_iobinding(parse);
+
+ if (parse->binding == (bind_t *)0)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.");
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * configure terminal line
+ */
+ if (TTY_GETATTR(fd232, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tm): %m", unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+#ifndef _PC_VDISABLE
+ memset((char *)tm.c_cc, 0, sizeof(tm.c_cc));
+#else
+ int disablec;
+ errno = 0; /* pathconf can deliver -1 without changing errno ! */
+
+ disablec = fpathconf(parse->fd, _PC_VDISABLE);
+ if (disablec == -1 && errno)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CL_UNIT(parse->unit));
+ memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); /* best guess */
+ }
+ else
+ if (disablec != -1)
+ memset((char *)tm.c_cc, disablec, sizeof(tm.c_cc));
+#endif
+
+ tm.c_cflag = clockinfo[type].cl_cflag;
+ tm.c_iflag = clockinfo[type].cl_iflag;
+ tm.c_oflag = clockinfo[type].cl_oflag;
+ tm.c_lflag = clockinfo[type].cl_lflag;
+#if defined(SYS_FREEBSD) && (defined(BOEDER) || defined(FREEBSD_CONRAD))
+ if (cfsetspeed(&tm, B50) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: cfsetspeed(&tm, B50): %m",
+ unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#endif
+ if (TTY_SETATTR(fd232, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tm): %m", unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+
+ /*
+ * as we always(?) get 8 bit chars we want to be
+ * sure, that the upper bits are zero for less
+ * than 8 bit I/O - so we pass that information on.
+ * note that there can be only one bit count format
+ * per file descriptor
+ */
+
+ switch (tm.c_cflag & CSIZE)
+ {
+ case CS5:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
+ break;
+
+ case CS6:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
+ break;
+
+ case CS7:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
+ break;
+
+ case CS8:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
+ break;
+ }
+
+ if (!PARSE_SETCS(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+#ifdef FREEBSD_CONRAD
+ {
+ int i,j;
+ struct timeval tv;
+ ioctl(parse->fd,TIOCTIMESTAMP,&tv);
+ j = TIOCM_RTS;
+ i = ioctl(fd232, TIOCMBIC, &j);
+ if (i < 0) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: lowrts_poll: failed to lower RTS: %m",
+ CL_UNIT(parse->unit));
+ }
+ }
+#endif
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ if (fcntl(fd232, F_SETFL, fcntl(fd232, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: fcntl(%d, F_SETFL, ...): %m",
+ unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+
+ if (ioctl(fd232, TIOCCDTR, 0) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: ioctl(%d, TIOCCDTR, 0): %m",
+ unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#endif
+
+ strcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format);
+ tmp_ctl.parseformat.parse_count = strlen(tmp_ctl.parseformat.parse_buffer);
+
+ if (!PARSE_SETFMT(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+#ifdef TCFLSH
+ /*
+ * get rid of all IO accumulated so far
+ */
+ {
+#ifndef TCIOFLUSH
+#define TCIOFLUSH 2
+#endif
+ int flshcmd = TCIOFLUSH;
+
+ (void) ioctl(parse->fd, TCFLSH, (caddr_t)&flshcmd);
+ }
+#endif
+
+ tmp_ctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS;
+
+ if (!PARSE_SETSTAT(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setstat() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * try to do any special initializations
+ */
+ if (parse->parse_type->cl_init)
+ {
+ if (parse->parse_type->cl_init(parse))
+ {
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+ }
+
+ if (!(parse->parse_type->cl_flags & PARSE_F_POLLONLY) &&
+ (CL_PPS(parse->unit) || (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)))
+ {
+ /*
+ * Insert in async io device list.
+ */
+ parse->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
+ parse->io.srcclock = (caddr_t)parse;
+ parse->io.datalen = 0;
+ parse->io.fd = parse->fd; /* replicated, but what the heck */
+ if (!io_addclock(&parse->io))
+ {
+ if (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CL_UNIT(parse->unit), parsedev);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (switching to polling mode)", CL_UNIT(parse->unit), parsedev);
+ }
+ }
+ else
+ {
+ parse->pollonly = 0; /*
+ * update at receipt of time_stamp - also
+ * supports PPS processing
+ */
+ }
+ }
+
+#ifdef PPSPPS
+ if (parse->pollonly || (parse->parse_type->cl_flags & PARSE_F_PPSPPS))
+ {
+ if (fdpps == -1)
+ {
+ fdpps = parse->fd;
+ if (!PARSE_DISABLE(parse))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_disable() FAILED", CL_UNIT(parse->unit));
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+ else
+ {
+ syslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: loopfilter PPS already active - no PPS via CIOGETEV", CL_UNIT(parse->unit));
+ }
+ }
+#endif
+
+ /*
+ * wind up statistics timer
+ */
+ parse->stattimer.peer = (struct peer *)parse; /* we know better, but what the heck */
+ parse->stattimer.event_handler = cparse_statistics;
+ parse->stattimer.event_time = current_time + PARSESTATISTICS;
+ TIMER_ENQUEUE(timerqueue, &parse->stattimer);
+
+ /*
+ * get out Copyright information once
+ */
+ if (!notice)
+ {
+ syslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1993, Frank Kardel");
+ notice = 1;
+ }
+
+ /*
+ * print out configuration
+ */
+ syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added",
+ CL_UNIT(parse->unit),
+ parse->parse_type->cl_description, parsedev);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d",
+ CL_UNIT(parse->unit),
+ parse->peer->stratum, (parse->pollonly || !CL_PPS(parse->unit)) ? "no " : "",
+ l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: rootdelay %s s, phaseadjust %s s, %s IO handling",
+ CL_UNIT(parse->unit),
+ ufptoa(parse->parse_type->cl_rootdelay, 6),
+ lfptoa(&parse->basedelay, 8),
+ parse->binding->bd_description);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CL_UNIT(parse->unit),
+ !(*parse->parse_type->cl_format) ? "<AUTOMATIC>" : parse->parse_type->cl_format);
+
+#ifdef PPSPPS
+ syslog(LOG_INFO, "PARSE receiver #%d: %sCD PPS support",
+ CL_UNIT(parse->unit),
+ (fdpps == parse->fd) ? "" : "NO ");
+#endif
+
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse_poll - called by the transmit procedure
+ */
+static void
+parse_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct parseunit *parse;
+
+ unit = CL_UNIT(unit);
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit invalid",
+ unit);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (!parse->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit unused",
+ unit);
+ return;
+ }
+
+ if (peer != parse->peer)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: poll: INTERNAL: peer incorrect",
+ unit);
+ return;
+ }
+
+ /*
+ * Update clock stat counters
+ */
+ parse->polls++;
+
+ /*
+ * in PPS mode we just mark that we want the next sample
+ * for the clock filter
+ */
+ if (!parse->pollonly)
+ {
+ if (parse->pollneeddata)
+ {
+ /*
+ * bad news - didn't get a response last time
+ */
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval", CL_UNIT(parse->unit));
+ }
+ parse->pollneeddata = 1;
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+ return;
+ }
+
+ /*
+ * the following code is only executed only when polling is used
+ */
+
+ PARSE_POLL(parse);
+}
+
+/*--------------------------------------------------
+ * parse_leap - called when a leap second occurs
+ */
+
+static void
+parse_leap()
+{
+ /*
+ * PARSE encodes the LEAP correction direction.
+ * For timecodes that do not pass on the leap correction direction
+ * the default PARSEB_LEAPADD must be used. It may then be modified
+ * with a fudge flag (flag2).
+ */
+}
+
+
+/*--------------------------------------------------
+ * parse_control - set fudge factors, return statistics
+ */
+static void
+parse_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct parseunit *parse;
+ parsectl_t tmpctl;
+ unsigned LONG type;
+ static char outstatus[400]; /* status output buffer */
+
+ type = CL_TYPE(unit);
+ unit = CL_UNIT(unit);
+
+ if (out)
+ {
+ out->lencode = 0;
+ out->lastcode = 0;
+ out->polls = out->noresponse = 0;
+ out->badformat = out->baddata = 0;
+ out->timereset = 0;
+ out->currentstatus = out->lastevent = CEVNT_NOMINAL;
+ out->kv_list = (struct ctl_var *)0;
+ }
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (!parse || !parse->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
+ unit);
+ return;
+ }
+
+ if (in)
+ {
+ if (in->haveflags & CLK_HAVETIME1)
+ parse->basedelay = in->fudgetime1;
+
+ if (in->haveflags & CLK_HAVETIME2)
+ {
+ parse->ppsdelay = in->fudgetime2;
+ }
+
+ if (in->haveflags & CLK_HAVEVAL1)
+ {
+ parse->peer->stratum = (u_char)(in->fudgeval1 & 0xf);
+ if (parse->peer->stratum <= 1)
+ memmove((char *)&parse->peer->refid,
+ parse->parse_type->cl_id,
+ 4);
+ else
+ parse->peer->refid = htonl(PARSEHSREFID);
+ }
+
+ /*
+ * NOT USED - yet
+ *
+ if (in->haveflags & CLK_HAVEVAL2)
+ {
+ }
+ */
+ if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parse->flags = (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) |
+ (parse->flags & ~PARSE_STAT_FLAGS);
+ }
+
+ if (in->haveflags & (CLK_HAVEVAL2|CLK_HAVETIME2|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parsectl_t tmpctl;
+ tmpctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS;
+
+ if (!PARSE_SETSTAT(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_setstat() FAILED", unit);
+ }
+ }
+ }
+
+ if (out)
+ {
+ register unsigned LONG sum = 0;
+ register char *t, *tt;
+ register struct tm *tm;
+ register short utcoff;
+ register char sign;
+ register int i;
+ time_t tim;
+
+ outstatus[0] = '\0';
+
+ out->haveflags = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3;
+ out->clockdesc = parse->parse_type->cl_description;
+
+ out->fudgetime1 = parse->basedelay;
+
+ out->fudgetime2 = parse->ppsdelay;
+
+ out->fudgeval1 = (LONG)parse->peer->stratum;
+
+ out->fudgeval2 = 0;
+
+ out->flags = parse->flags & PARSE_STAT_FLAGS;
+
+ out->type = REFCLK_PARSE;
+
+ /*
+ * figure out skew between PPS and RS232 - just for informational
+ * purposes - returned in time2 value
+ */
+ if (PARSE_SYNC(parse->time.parse_state))
+ {
+ if (PARSE_PPS(parse->time.parse_state) && PARSE_TIMECODE(parse->time.parse_state))
+ {
+ l_fp off;
+
+ /*
+ * we have a PPS and RS232 signal - calculate the skew
+ * WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
+ */
+ off = parse->time.parse_stime.fp;
+ L_SUB(&off, &parse->time.parse_ptime.fp); /* true offset */
+ tt = add_var(&out->kv_list, 40, RO);
+ sprintf(tt, "refclock_ppsskew=%s", lfptoms(&off, 6));
+ }
+ }
+
+ if (PARSE_PPS(parse->time.parse_state))
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_ppstime=\"%s\"", prettydate(&parse->time.parse_ptime.fp));
+ }
+
+ /*
+ * all this for just finding out the +-xxxx part (there are always
+ * new and changing fields in the standards 8-().
+ *
+ * but we do it for the human user...
+ */
+ tim = parse->time.parse_time.fp.l_ui - JAN_1970;
+ tm = gmtime(&tim);
+ utcoff = tm->tm_hour * 60 + tm->tm_min;
+ tm = localtime(&tim);
+ utcoff = tm->tm_hour * 60 + tm->tm_min - utcoff + 12 * 60;
+ utcoff += 24 * 60;
+ utcoff %= 24 * 60;
+ utcoff -= 12 * 60;
+ if (utcoff < 0)
+ {
+ utcoff = -utcoff;
+ sign = '-';
+ }
+ else
+ {
+ sign = '+';
+ }
+
+ tt = add_var(&out->kv_list, 128, RO|DEF);
+ sprintf(tt, "refclock_time=\"");
+ tt += strlen(tt);
+
+ if (parse->time.parse_time.fp.l_ui == 0)
+ {
+ strcpy(tt, "<UNDEFINED>\"");
+ }
+ else
+ {
+ strcpy(tt, prettydate(&parse->time.parse_time.fp));
+ t = tt + strlen(tt);
+
+ sprintf(t, " (%c%02d%02d)\"", sign, utcoff / 60, utcoff % 60);
+ }
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 128, RO|DEF);
+ sprintf(tt, "refclock_status=\"");
+ tt += strlen(tt);
+
+ /*
+ * copy PPS flags from last read transaction (informational only)
+ */
+ tmpctl.parsegettc.parse_state |= parse->time.parse_state &
+ (PARSEB_PPS|PARSEB_S_PPS);
+
+ (void) parsestate(tmpctl.parsegettc.parse_state, tt);
+
+ strcat(tt, "\"");
+
+ if (tmpctl.parsegettc.parse_count)
+ mkascii(outstatus+strlen(outstatus), sizeof(outstatus)- strlen(outstatus) - 1,
+ tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1);
+
+ parse->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+
+ tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ syslog (LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_format=\"");
+
+ strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count);
+ strcat(tt,"\"");
+ }
+
+ /*
+ * gather state statistics
+ */
+
+ tt = add_var(&out->kv_list, 200, RO|DEF);
+ strcpy(tt, "refclock_states=\"");
+ tt += strlen(tt);
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ register unsigned LONG stime;
+ register unsigned LONG div = current_time - parse->timestarted;
+ register unsigned LONG percent;
+
+ percent = stime = PARSE_STATETIME(parse, i);
+
+ while (((unsigned LONG)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ div /= 10;
+ }
+
+ if (div)
+ percent = (percent * 10000) / div;
+ else
+ percent = 10000;
+
+ if (stime)
+ {
+ sprintf(tt, "%s%s%s: %s (%d.%02d%%)",
+ sum ? "; " : "",
+ (parse->status == i) ? "*" : "",
+ clockstatus(i),
+ l_mktime(stime),
+ percent / 100, percent % 100);
+ sum += stime;
+ tt += strlen(tt);
+ }
+ }
+
+ sprintf(tt, "; running time: %s\"", l_mktime(sum));
+
+ tt = add_var(&out->kv_list, 32, RO);
+ sprintf(tt, "refclock_id=\"%s\"", parse->parse_type->cl_id);
+
+ tt = add_var(&out->kv_list, 80, RO);
+ sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description);
+
+ tt = add_var(&out->kv_list, 128, RO);
+ sprintf(tt, "refclock_driver_version=\"refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp\"");
+
+ out->lencode = strlen(outstatus);
+ out->lastcode = outstatus;
+ out->timereset = parse->timestarted;
+ out->polls = parse->polls;
+ out->noresponse = parse->noresponse;
+ out->badformat = parse->badformat;
+ out->baddata = parse->baddata;
+ out->lastevent = parse->lastevent;
+ out->currentstatus = parse->status;
+ }
+}
+
+/**===========================================================================
+ ** processing routines
+ **/
+
+/*--------------------------------------------------
+ * event handling - note that nominal events will also be posted
+ */
+static void
+parse_event(parse, event)
+ struct parseunit *parse;
+ int event;
+{
+ if (parse->status != (u_char) event)
+ {
+ parse->statetime[parse->status] += current_time - parse->lastchange;
+ parse->lastchange = current_time;
+
+ parse->status = (u_char)event;
+ if (event != CEVNT_NOMINAL)
+ parse->lastevent = parse->status;
+
+ report_event(EVNT_PEERCLOCK, parse->peer);
+ }
+}
+
+/*--------------------------------------------------
+ * process a PARSE time sample
+ */
+static void
+parse_process(parse, parsetime)
+ struct parseunit *parse;
+ parsetime_t *parsetime;
+{
+ unsigned char leap;
+ struct timeval usecdisp;
+ l_fp off, rectime, reftime, dispersion;
+
+ /*
+ * check for changes in conversion status
+ * (only one for each new status !)
+ */
+ if (parse->laststatus != parsetime->parse_status)
+ {
+ char buffer[200];
+
+ syslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
+ CL_UNIT(parse->unit), parsestatus(parsetime->parse_status, buffer));
+
+ if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
+ {
+ /*
+ * tell more about the story - list time code
+ * there is a slight change for a race condition and
+ * the time code might be overwritten by the next packet
+ */
+ parsectl_t tmpctl;
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CL_UNIT(parse->unit));
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\"",
+ CL_UNIT(parse->unit), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1));
+ parse->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+ }
+
+ parse->laststatus = parsetime->parse_status;
+ }
+
+ /*
+ * examine status and post appropriate events
+ */
+ if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
+ {
+ /*
+ * got bad data - tell the rest of the system
+ */
+ switch (parsetime->parse_status & CVT_MASK)
+ {
+ case CVT_NONE:
+ break; /* well, still waiting - timeout is handled at higher levels */
+
+ case CVT_FAIL:
+ parse->badformat++;
+ if (parsetime->parse_status & CVT_BADFMT)
+ {
+ parse_event(parse, CEVNT_BADREPLY);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADDATE)
+ {
+ parse_event(parse, CEVNT_BADDATE);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADTIME)
+ {
+ parse_event(parse, CEVNT_BADTIME);
+ }
+ else
+ {
+ parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
+ }
+ }
+ return; /* skip the rest - useless */
+ }
+
+ /*
+ * check for format changes
+ * (in case somebody has swapped clocks 8-)
+ */
+ if (parse->lastformat != parsetime->parse_format)
+ {
+ parsectl_t tmpctl;
+
+ tmpctl.parseformat.parse_format = parsetime->parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CL_UNIT(parse->unit));
+ }
+ else
+ {
+ syslog(LOG_INFO, "PARSE receiver #%d: new packet format \"%s\"",
+ CL_UNIT(parse->unit), tmpctl.parseformat.parse_buffer);
+ }
+ parse->lastformat = parsetime->parse_format;
+ }
+
+ /*
+ * now, any changes ?
+ */
+ if (parse->time.parse_state != parsetime->parse_state)
+ {
+ char tmp1[200];
+ char tmp2[200];
+ /*
+ * something happend
+ */
+
+ (void) parsestate(parsetime->parse_state, tmp1);
+ (void) parsestate(parse->time.parse_state, tmp2);
+
+ syslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
+ CL_UNIT(parse->unit), tmp2, tmp1);
+ }
+
+ /*
+ * remember for future
+ */
+ parse->time = *parsetime;
+
+ /*
+ * check to see, whether the clock did a complete powerup or lost PZF signal
+ * and post correct events for current condition
+ */
+ if (PARSE_POWERUP(parsetime->parse_state))
+ {
+ /*
+ * this is bad, as we have completely lost synchronisation
+ * well this is a problem with the receiver here
+ * for PARSE U/A 31 the lost synchronisation ist true
+ * as it is the powerup state and the time is taken
+ * from a crude real time clock chip
+ * for the PZF series this is only partly true, as
+ * PARSE_POWERUP only means that the pseudo random
+ * phase shift sequence cannot be found. this is only
+ * bad, if we have never seen the clock in the SYNC
+ * state, where the PHASE and EPOCH are correct.
+ * for reporting events the above business does not
+ * really matter, but we can use the time code
+ * even in the POWERUP state after having seen
+ * the clock in the synchronized state (PZF class
+ * receivers) unless we have had a telegram disruption
+ * after having seen the clock in the SYNC state. we
+ * thus require having seen the clock in SYNC state
+ * *after* having missed telegrams (noresponse) from
+ * the clock. one problem remains: we might use erroneously
+ * POWERUP data if the disruption is shorter than 1 polling
+ * interval. fortunately powerdowns last usually longer than 64
+ * seconds and the receiver is at least 2 minutes in the
+ * POWERUP or NOSYNC state before switching to SYNC
+ */
+ parse_event(parse, CEVNT_FAULT);
+ if (parse->nosynctime)
+ {
+ /*
+ * repeated POWERUP/NOSYNC state - look whether
+ * the message should be repeated
+ */
+ if (current_time - parse->nosynctime > PARSENOSYNCREPEAT)
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: *STILL* NOT SYNCHRONIZED (POWERUP or no PZF signal)",
+ CL_UNIT(parse->unit));
+ parse->nosynctime = current_time;
+ }
+ }
+ else
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED",
+ CL_UNIT(parse->unit));
+ parse->nosynctime = current_time;
+ }
+ }
+ else
+ {
+ /*
+ * we have two states left
+ *
+ * SYNC:
+ * this state means that the EPOCH (timecode) and PHASE
+ * information has be read correctly (at least two
+ * successive PARSE timecodes were received correctly)
+ * this is the best possible state - full trust
+ *
+ * NOSYNC:
+ * The clock should be on phase with respect to the second
+ * signal, but the timecode has not been received correctly within
+ * at least the last two minutes. this is a sort of half baked state
+ * for PARSE U/A 31 this is bad news (clock running without timecode
+ * confirmation)
+ * PZF 535 has also no time confirmation, but the phase should be
+ * very precise as the PZF signal can be decoded
+ */
+ parse->nosynctime = 0; /* current state is better than worst state */
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * currently completely synchronized - best possible state
+ */
+ parse->lastsync = current_time;
+ /*
+ * log OK status
+ */
+ parse_event(parse, CEVNT_NOMINAL);
+ }
+ else
+ {
+ /*
+ * we have had some problems receiving the time code
+ */
+ parse_event(parse, CEVNT_PROP);
+ }
+ }
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ l_fp offset;
+
+ /*
+ * calculate time offset including systematic delays
+ * off = PARSE-timestamp + propagation delay - kernel time stamp
+ */
+ offset = parse->basedelay;
+
+ off = parsetime->parse_time.fp;
+
+ reftime = off;
+
+ L_ADD(&off, &offset);
+ rectime = off; /* this makes org time and xmt time somewhat artificial */
+
+ L_SUB(&off, &parsetime->parse_stime.fp);
+
+ if ((parse->flags & PARSE_STAT_FILTER) &&
+ (off.l_i > -60) &&
+ (off.l_i < 60)) /* take usec error only if within +- 60 secs */
+ {
+ struct timeval usecerror;
+ /*
+ * offset is already calculated
+ */
+ usecerror.tv_sec = parsetime->parse_usecerror / 1000000;
+ usecerror.tv_usec = parsetime->parse_usecerror % 1000000;
+
+ sTVTOTS(&usecerror, &off);
+ L_ADD(&off, &offset);
+ }
+ }
+
+ if (PARSE_PPS(parsetime->parse_state) && CL_PPS(parse->unit))
+ {
+ l_fp offset;
+
+ /*
+ * we have a PPS signal - much better than the RS232 stuff (we hope)
+ */
+ offset = parsetime->parse_ptime.fp;
+
+ L_ADD(&offset, &parse->ppsdelay);
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ if (M_ISGEQ(off.l_i, off.l_f, -1, 0x80000000) &&
+ M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_f))
+ {
+ /*
+ * RS232 offsets within [-0.5..0.5[ - take PPS offsets
+ */
+
+ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
+ {
+ reftime = off = offset;
+ rectime = offset;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ else
+ {
+ /*
+ * time code describes pulse
+ */
+ off = parsetime->parse_time.fp;
+
+ rectime = reftime = off; /* take reference time - fake rectime */
+
+ L_SUB(&off, &offset); /* true offset */
+ }
+ }
+ /*
+ * take RS232 offset when PPS when out of bounds
+ */
+ }
+ else
+ {
+ /*
+ * Well, no time code to guide us - assume on second pulse
+ * and pray, that we are within [-0.5..0.5[
+ */
+ reftime = off = offset;
+ rectime = offset;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ }
+ else
+ {
+ if (!PARSE_TIMECODE(parsetime->parse_state))
+ {
+ /*
+ * Well, no PPS, no TIMECODE, no more work ...
+ */
+ return;
+ }
+ }
+
+
+#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) || defined(PARSEPPS)
+ if (CL_PPS(parse->unit) && !parse->pollonly && PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * only provide PPS information when clock
+ * is in sync
+ * thus PHASE and EPOCH are correct and PPS is not
+ * done via the CIOGETEV loopfilter mechanism
+ */
+#ifdef PPSPPS
+ if (fdpps != parse->fd)
+#endif
+ (void) pps_sample(&off);
+ }
+#endif /* PPS || PPSCLK || PPSPPS || PARSEPPS */
+
+ /*
+ * ready, unless the machine wants a sample
+ */
+ if (!parse->pollonly && !parse->pollneeddata)
+ return;
+
+ parse->pollneeddata = 0;
+
+ if (PARSE_PPS(parsetime->parse_state))
+ {
+ L_CLR(&dispersion);
+ }
+ else
+ {
+ /*
+ * convert usec dispersion into NTP TS world
+ */
+
+ usecdisp.tv_sec = parsetime->parse_usecdisp / 1000000;
+ usecdisp.tv_usec = parsetime->parse_usecdisp % 1000000;
+
+ TVTOTS(&usecdisp, &dispersion);
+ }
+
+ /*
+ * and now stick it into the clock machine
+ * samples are only valid iff lastsync is not too old and
+ * we have seen the clock in sync at least once
+ * after the last time we didn't see an expected data telegram
+ * see the clock states section above for more reasoning
+ */
+ if (((current_time - parse->lastsync) > parse->parse_type->cl_maxunsync) ||
+ (parse->lastsync <= parse->lastmissed))
+ {
+ leap = LEAP_NOTINSYNC;
+ }
+ else
+ {
+ if (PARSE_LEAPADD(parsetime->parse_state))
+ {
+ /*
+ * we pick this state also for time code that pass leap warnings
+ * without direction information (as earth is currently slowing
+ * down).
+ */
+ leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
+ }
+ else
+ if (PARSE_LEAPDEL(parsetime->parse_state))
+ {
+ leap = LEAP_DELSECOND;
+ }
+ else
+ {
+ leap = LEAP_NOWARNING;
+ }
+ }
+
+ refclock_receive(parse->peer, &off, 0, LFPTOFP(&dispersion), &reftime, &rectime, leap);
+}
+
+/**===========================================================================
+ ** clock polling support
+ **/
+
+struct poll_timer
+{
+ struct event timer; /* we'd like to poll a a higher rate than 1/64s */
+};
+
+typedef struct poll_timer poll_timer_t;
+
+/*--------------------------------------------------
+ * direct poll routine
+ */
+static void
+poll_dpoll(parse)
+ struct parseunit *parse;
+{
+ register int rtc;
+ register char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
+ register int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
+
+ rtc = write(parse->fd, ps, ct);
+ if (rtc < 0)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CL_UNIT(parse->unit));
+ }
+ else
+ if (rtc != ct)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CL_UNIT(parse->unit), rtc, ct);
+ }
+}
+
+/*--------------------------------------------------
+ * periodic poll routine
+ */
+static void
+poll_poll(parse)
+ struct parseunit *parse;
+{
+ register poll_timer_t *pt = (poll_timer_t *)parse->localdata;
+
+ poll_dpoll(parse);
+
+ if (pt != (poll_timer_t *)0)
+ {
+ pt->timer.event_time = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ TIMER_ENQUEUE(timerqueue, &pt->timer);
+ }
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+poll_init(parse)
+ struct parseunit *parse;
+{
+ register poll_timer_t *pt;
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->localdata = (void *)malloc(sizeof(poll_timer_t));
+ memset((char *)parse->localdata, 0, sizeof(poll_timer_t));
+
+ pt = (poll_timer_t *)parse->localdata;
+
+ pt->timer.peer = (struct peer *)parse; /* well, only we know what it is */
+ pt->timer.event_handler = poll_poll;
+ poll_poll(parse);
+ }
+ else
+ {
+ parse->localdata = (void *)0;
+ }
+
+ return 0;
+}
+
+/*--------------------------------------------------
+ * end routine - clean up timer
+ */
+static void
+poll_end(parse)
+ struct parseunit *parse;
+{
+ if (parse->localdata != (void *)0)
+ {
+ TIMER_DEQUEUE(&((poll_timer_t *)parse->localdata)->timer);
+ free((char *)parse->localdata);
+ parse->localdata = (void *)0;
+ }
+}
+
+/**===========================================================================
+ ** special code for special clocks
+ **/
+
+
+/*--------------------------------------------------
+ * trimble TAIP init routine - setup EOL and then do poll_init.
+ */
+static int
+trimbletaip_init(parse)
+ struct parseunit *parse;
+{
+#ifdef HAVE_TERMIOS
+ struct termios tm;
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tm;
+#endif
+ /*
+ * configure terminal line for trimble receiver
+ */
+ if (TTY_GETATTR(parse->fd, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ tm.c_cc[VEOL] = TRIMBLETAIP_EOL;
+
+ if (TTY_SETATTR(parse->fd, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ }
+ return poll_init(parse);
+}
+
+/*
+ * This driver supports the Trimble SVee Six Plus GPS receiver module.
+ * It should support other Trimble receivers which use the Trimble Standard
+ * Interface Protocol (see below).
+ *
+ * The module has a serial I/O port for command/data and a 1 pulse-per-second
+ * output, about 1 microsecond wide. The leading edge of the pulse is
+ * coincident with the change of the GPS second. This is the same as
+ * the change of the UTC second +/- ~1 microsecond. Some other clocks
+ * specifically use a feature in the data message as a timing reference, but
+ * the SVee Six Plus does not do this. In fact there is considerable jitter
+ * on the timing of the messages, so this driver only supports the use
+ * of the PPS pulse for accurate timing. Where it is determined that
+ * the offset is way off, when first starting up xntpd for example,
+ * the timing of the data stream is used until the offset becomes low enough
+ * (|offset| < CLOCK_MAX), at which point the pps offset is used.
+ *
+ * It can use either option for receiving PPS information - the 'ppsclock'
+ * stream pushed onto the serial data interface to timestamp the Carrier
+ * Detect interrupts, where the 1PPS connects to the CD line. This only
+ * works on SunOS 4.1.x currently. To select this, define PPSPPS in
+ * Config.local. The other option is to use a pulse-stretcher/level-converter
+ * to convert the PPS pulse into a RS232 start pulse & feed this into another
+ * tty port. To use this option, define PPSCLK in Config.local. The pps input,
+ * by whichever method, is handled in ntp_loopfilter.c
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+#define PI 3.1415926535898 /* lots of sig figs */
+#define D2R PI/180.0
+
+/*-------------------------------------------------------------------
+ * sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
+ * interface to the receiver.
+ *
+ * CAVEAT: the sendflt, sendint routines are byte order dependend and
+ * float implementation dependend - these must be converted to portable
+ * versions !
+ */
+
+union {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+} uval;
+
+struct txbuf
+{
+ short idx; /* index to first unused byte */
+ u_char *txt; /* pointer to actual data buffer */
+};
+
+void
+sendcmd(buf, c)
+ struct txbuf *buf;
+ u_char c;
+{
+ buf->txt[0] = DLE;
+ buf->txt[1] = c;
+ buf->idx = 2;
+}
+
+void sendbyte(buf, b)
+ struct txbuf *buf;
+ u_char b;
+{
+ if (b == DLE)
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = b;
+}
+
+void
+sendetx(buf, parse)
+ struct txbuf *buf;
+ struct parseunit *parse;
+{
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = ETX;
+
+ if (write(parse->fd, buf->txt, buf->idx) != buf->idx)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CL_UNIT(parse->unit));
+ }
+}
+
+void
+sendint(buf, a)
+ struct txbuf *buf;
+ int a;
+{
+ uval.iv = a;
+ sendbyte(buf, uval.bd[2]);
+ sendbyte(buf, uval.bd[3]);
+}
+
+void
+sendflt(buf, a)
+ struct txbuf *buf;
+ float a;
+{
+ int i;
+
+ uval.fv = a;
+ for (i=0; i<=3; i++)
+ sendbyte(buf, uval.bd[i]);
+}
+
+/*--------------------------------------------------
+ * trimble TSIP init routine
+ */
+static int
+trimbletsip_init(parse)
+ struct parseunit *parse;
+{
+ u_char buffer[256];
+ struct txbuf buf;
+
+ buf.txt = buffer;
+
+ if (!poll_init(parse))
+ {
+ sendcmd(&buf, 0x1f); /* request software versions */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x2c); /* set operating parameters */
+ sendbyte(&buf, 4); /* static */
+ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
+ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
+ sendflt(&buf, 12.0); /* PDOP mask = 12 */
+ sendflt(&buf, 8.0); /* PDOP switch level = 8 */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x22); /* fix mode select */
+ sendbyte(&buf, 0); /* automatic */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x28); /* request system message */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x8e); /* superpacket fix */
+ sendbyte(&buf, 0x2); /* binary mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x35); /* set I/O options */
+ sendbyte(&buf, 0); /* no position output */
+ sendbyte(&buf, 0); /* no velocity output */
+ sendbyte(&buf, 7); /* UTC, compute on seconds, send only on request */
+ sendbyte(&buf, 0); /* no raw measurements */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x2f); /* request UTC correction data */
+ sendetx(&buf, parse);
+ return 0;
+ }
+ else
+ return 1;
+}
+
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * Revision 3.53 1994/03/25 13:07:39 kardel
+ * fixed offset calculation for large (>4 Min) offsets
+ *
+ * Revision 3.52 1994/03/03 09:58:00 kardel
+ * stick -kv in cvs is no fun
+ *
+ * Revision 3.49 1994/02/20 13:26:00 kardel
+ * rcs id cleanup
+ *
+ * Revision 3.48 1994/02/20 13:04:56 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.47 1994/02/02 17:44:30 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.45 1994/01/25 19:06:27 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.44 1994/01/25 17:32:23 kardel
+ * settable extended variables
+ *
+ * Revision 3.43 1994/01/23 16:28:39 kardel
+ * HAVE_TERMIOS introduced
+ *
+ * Revision 3.42 1994/01/22 11:35:04 kardel
+ * added HAVE_TERMIOS
+ *
+ * Revision 3.41 1993/11/27 18:44:37 kardel
+ * can't trust GPS166 on unsync
+ *
+ * Revision 3.40 1993/11/21 18:03:36 kardel
+ * useless declaration deleted
+ *
+ * Revision 3.39 1993/11/21 15:30:15 kardel
+ * static funcitions may be declared only at outer level
+ *
+ * Revision 3.38 1993/11/15 21:26:49 kardel
+ * conditional define comments fixed
+ *
+ * Revision 3.37 1993/11/11 11:20:49 kardel
+ * declaration fixes
+ *
+ * Revision 3.36 1993/11/10 12:17:14 kardel
+ * #ifdef glitch
+ *
+ * Revision 3.35 1993/11/01 21:15:06 kardel
+ * comments updated
+ *
+ * Revision 3.34 1993/11/01 20:01:08 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.33 1993/10/30 09:44:58 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.32 1993/10/22 14:28:43 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.31 1993/10/10 21:19:10 kardel
+ * compilation cleanup - (minimal porting tests)
+ *
+ * Revision 3.30 1993/10/09 21:44:35 kardel
+ * syslog strings fixed
+ *
+ * Revision 3.29 1993/10/09 14:40:15 kardel
+ * default precision setting fixed
+ *
+ * Revision 3.28 1993/10/08 14:48:22 kardel
+ * Changed offset determination logic:
+ * Take the PPS offset if it is available and the time
+ * code offset is within [-0.5..0.5[, otherwise stick
+ * to the time code offset
+ *
+ * Revision 3.27 1993/10/08 00:53:17 kardel
+ * announce also simulated PPS via CIOGETEV in ntpq cl
+ *
+ * Revision 3.26 1993/10/07 23:29:35 kardel
+ * trimble fixes
+ *
+ * Revision 3.25 1993/10/06 21:13:35 kardel
+ * test reversed (CIOGETEV support)
+ *
+ * Revision 3.24 1993/10/03 20:18:26 kardel
+ * Well, values > 999999 in the usec field from uniqtime() timestamps
+ * can prove harmful.
+ *
+ * Revision 3.23 1993/10/03 19:49:54 kardel
+ * buftvtots where failing on uninitialized time stamps
+ *
+ * Revision 3.22 1993/10/03 19:11:09 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.21 1993/09/29 11:30:18 kardel
+ * special init for trimble to set EOL
+ *
+ * Revision 3.20 1993/09/27 22:46:28 kardel
+ * preserve module stack if I_PUSH parse fails
+ *
+ * Revision 3.19 1993/09/27 21:10:11 kardel
+ * wrong structure member
+ *
+ * Revision 3.18 1993/09/27 13:05:06 kardel
+ * Trimble is true polling only
+ *
+ * Revision 3.17 1993/09/27 12:47:10 kardel
+ * poll string support generalized
+ *
+ * Revision 3.16 1993/09/26 23:40:56 kardel
+ * new parse driver logic
+ *
+ * Revision 3.15 1993/09/24 15:00:51 kardel
+ * Sep 23rd distribution...
+ *
+ * Revision 3.14 1993/09/22 18:21:15 kardel
+ * support ppsclock streams module (-DSTREAM -DPPSPPS -DPARSEPPS -UPARSESTREAM)
+ *
+ * Revision 3.13 1993/09/05 15:38:33 kardel
+ * not every cpp understands #error...
+ *
+ * Revision 3.12 1993/09/02 20:04:19 kardel
+ * TTY cleanup
+ *
+ * Revision 3.11 1993/09/01 21:48:47 kardel
+ * conditional cleanup
+ *
+ * Revision 3.10 1993/09/01 11:32:45 kardel
+ * assuming HAVE_POSIX_TTYS when STREAM defined
+ *
+ * Revision 3.9 1993/08/31 22:31:46 kardel
+ * SINIX-M SysVR4 integration
+ *
+ * Revision 3.8 1993/08/27 00:29:50 kardel
+ * compilation cleanup
+ *
+ * Revision 3.7 1993/08/24 22:27:30 kardel
+ * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad
+ *
+ * Revision 3.6 1993/08/24 21:36:23 kardel
+ * casting and ifdefs
+ *
+ * Revision 3.5 1993/07/09 23:36:59 kardel
+ * HAVE_POSIX_TTYS used to produce errors 8-( - BSD driver support still lacking
+ *
+ * Revision 3.4 1993/07/09 12:42:29 kardel
+ * RAW DCF now officially released
+ *
+ * Revision 3.3 1993/07/09 11:50:37 kardel
+ * running GPS also on 960 to be able to switch GPS/DCF77
+ *
+ * Revision 3.2 1993/07/09 11:37:34 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:01:07 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/xntpd/refclock_pst.c b/usr.sbin/xntpd/xntpd/refclock_pst.c
new file mode 100644
index 0000000..edc77f9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_pst.c
@@ -0,0 +1,329 @@
+/*
+ * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
+ */
+#if defined(REFCLOCK) && defined(PST)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ * Receivers. No specific claim of accuracy is made for these receiver,
+ * but actual experience suggests that 10 ms would be a conservative
+ * assumption.
+ *
+ * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ * of-year format and UTC time zone. Automatic correction for DST should
+ * be disabled. It is very important that the year be set correctly in
+ * the DIPswitches; otherwise, the day of year will be incorrect after
+ * 28 April of a normal or leap year. The propagation delay DIPswitches
+ * should be set according to the distance from the transmitter for both
+ * WWV and WWVH, as described in the instructions. While the delay can
+ * be set only to within 11 ms, the fudge time1 parameter can be used
+ * for vernier corrections.
+ *
+ * Using the poll sequence QTQDQM, the response timecode is in three
+ * sections totalling 50 ASCII printing characters, as concatenated by
+ * the driver, in the following format:
+ *
+ * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+ *
+ * on-time = first <cr> * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ * a = AM/PM indicator (' ' for 24-hour mode)
+ * yy = year (from internal switches)
+ * dd/mm/ddd = day of month, month, day of year
+ * s = daylight-saving indicator (' ' for 24-hour mode)
+ * f = frequency enable (O = all frequencies enabled)
+ * r = baud rate (3 = 1200, 6 = 9600)
+ * d = features indicator (@ = month/day display enabled)
+ * z = time zone (0 = UTC)
+ * y = year (5 = 91)
+ * cc = WWV propagation delay (52 = 22 ms)
+ * hh = WWVH propagation delay (81 = 33 ms)
+ * SS = status (80 or 82 = operating correctly)
+ * F = current receive frequency (4 = 15 MHz)
+ * T = transmitter (C = WWV, H = WWVH)
+ * tttt = time since last update (0000 = minutes)
+ * uu = flush character (03 = ^c)
+ * xx = 94 (unknown)
+ *
+ * The alarm condition is indicated by other than '8' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * an extended period; unlock condition is indicated by other than
+ * "0000" in the tttt subfield at Q.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pst%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define WWVREFID "WWV\0" /* WWV reference ID */
+#define WWVHREFID "WWVH" /* WWVH reference ID */
+#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENPST 46 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct pstunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ char *lastptr; /* pointer to timecode data */
+};
+
+/*
+ * Function prototypes
+ */
+static int pst_start P((int, struct peer *));
+static void pst_shutdown P((int, struct peer *));
+static void pst_receive P((struct recvbuf *));
+static void pst_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pst = {
+ pst_start, /* start up driver */
+ pst_shutdown, /* shut down driver */
+ pst_poll, /* transmit poll message */
+ noentry, /* not used (old pst_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old pst_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pst_start - open the devices and initialize data for processing
+ */
+static int
+pst_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct pstunit *)
+ emalloc(sizeof(struct pstunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct pstunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = pst_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * pst_shutdown - shut down the clock
+ */
+static void
+pst_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * pst_receive - receive data from the serial interface
+ */
+static void
+pst_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ char ampmchar; /* AM/PM indicator */
+ char daychar; /* standard/daylight indicator */
+ char junque[10]; /* "yy/dd/mm/" discard */
+ char info[14]; /* "frdzycchhSSFT" clock info */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->lastcode
+ + BMAX - 2 - up->lastptr, &trtmp);
+ *up->lastptr++ = ' ';
+ *up->lastptr = '\0';
+
+ /*
+ * Note we get a buffer and timestamp for each <cr>, but only
+ * the first timestamp is retained.
+ */
+ if (!up->tcswitch)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->lastcode;
+ if (up->tcswitch < 3)
+ return;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("pst: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENPST) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format:
+ * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
+ */
+ if (sscanf(pp->lastcode, "%c%2d:%2d:%2d.%3d%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second,
+ &pp->msec, &daychar, junque, &pp->day,
+ info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and last update. If
+ * unsynchronized, set the leap bits accordingly and exit. Once
+ * synchronized, the dispersion depends only on when the clock
+ * was last heard, which depends on the time since last update,
+ * as reported by the clock.
+ */
+ if (info[9] != '8') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time - ltemp;
+ if (info[12] == 'H')
+ memcpy((char *)&pp->refid, WWVHREFID, 4);
+ else
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ trtmp = pp->lastrec;
+ trtmp.l_ui -= ltemp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion, &trtmp,
+ &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * pst_poll - called by the transmit procedure
+ */
+static void
+pst_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The PSTI/Traconex clock responds to a
+ * "QTQDQMT" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ up->tcswitch = 0;
+ up->lastptr = pp->lastcode;
+ if (write(pp->io.fd, "QTQDQMT", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_tpro.c b/usr.sbin/xntpd/xntpd/refclock_tpro.c
new file mode 100644
index 0000000..d530fbf
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_tpro.c
@@ -0,0 +1,227 @@
+/*
+ * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
+ */
+#if defined(REFCLOCK) && defined(TPRO) && defined(sun)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
+ * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
+ * tpro.o device driver be installed and loaded.
+ */
+
+/*
+ * TPRO interface definitions
+ */
+#define DEVICE "/dev/tpro%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct tprounit {
+ struct tproval tprodata; /* data returned from tpro read */
+};
+
+/*
+ * Function prototypes
+ */
+static int tpro_start P((int, struct peer *));
+static void tpro_shutdown P((int, struct peer *));
+static void tpro_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tpro = {
+ tpro_start, /* start up driver */
+ tpro_shutdown, /* shut down driver */
+ tpro_poll, /* transmit poll message */
+ noentry, /* not used (old tpro_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tpro_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * tpro_start - open the TPRO device and initialize data for processing
+ */
+static int
+tpro_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+
+ /*
+ * Open TPRO device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "tpro_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tprounit *)
+ emalloc(sizeof(struct tprounit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tprounit));
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tpro_shutdown - shut down the clock
+ */
+static void
+tpro_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tpro_poll - called by the transmit procedure
+ */
+static void
+tpro_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ struct tproval *tp;
+
+ /*
+ * This is the main routine. It snatches the time from the TPRO
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+
+ tp = &up->tprodata;
+ if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ gettstamp(&pp->lastrec);
+ pp->lasttime = current_time;
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ */
+ if (sprintf(pp->lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
+ tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
+ tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
+ tp->status)) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug)
+ printf("tpro: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->lastcode);
+#endif
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (tp->status != 0xff) {
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_trak.c b/usr.sbin/xntpd/xntpd/refclock_trak.c
new file mode 100644
index 0000000..d10d752
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_trak.c
@@ -0,0 +1,339 @@
+/*
+ * refclock_trak - clock driver for the TRAK 8820 GPS Station Clock
+ *
+ * Thanks to Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> for the
+ * previous version from which this one was developed.
+ */
+#if defined(REFCLOCK) && defined(TRAK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the TRAK 8820 GPS Station Clock. The claimed
+ * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * For best accuracy, this radio requires the LDISC_ACTS line
+ * discipline, which captures a timestamp at the '*' on-time character
+ * of the timecode. Using this discipline the jitter is in the order of
+ * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
+ * timestamp is used, which is captured at the \r ending the timecode
+ * message. This introduces a systematic error of 23 character times, or
+ * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
+ * IPC-class machines.
+ *
+ * Using the memus, the radio should be set for 9600 bps, one stop bit
+ * and no parity. It should be set to operate in computer (no echo)
+ * mode. The timecode format includes neither the year nor leap-second
+ * warning. No provisions are included in this preliminary version of
+ * the driver to read and record detailed internal radio status.
+ *
+ * In operation, this driver sends a RQTS\r request to the radio at
+ * initialization in order to put it in continuous time output mode. The
+ * radio then sends the following message once each second:
+ *
+ * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+ *
+ * on-time = '*' * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * q = quality indicator (phase error), 0-6:
+ * 0 > 20 us
+ * 6 > 10 us
+ * 5 > 1 us
+ * 4 > 100 ns
+ * 3 > 10 ns
+ * 2 < 10 ns
+ *
+ * The alarm condition is indicated by '0' at Q, which means the radio
+ * has a phase error than 20 usec relative to the broadcast time. The
+ * absence of year, DST and leap-second warning in this format is also
+ * alarming.
+ *
+ * The continuous time mode is disabled using the RQTX<cr> request,
+ * following which the radio sends a RQTX DONE<cr><lf> response. In the
+ * normal mode, other control and status requests are effective,
+ * including the leap-second status request RQLS<cr>. The radio responds
+ * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
+ * day. Presumably, this gives the epoch of the next leap second,
+ * RQLS 00,00,00 if none is specified in the GPS message. Specified in
+ * this form, the information is generally useless and is ignored by
+ * the driver.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/trak%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "TRAK" /* reference ID */
+#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENTRAK 24 /* timecode length */
+#define C_CTO "RQTS\r" /* start continuous time output */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct wwvbunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ char qualchar; /* quality indicator */
+};
+
+/*
+ * Function prototypes
+ */
+static int trak_start P((int, struct peer *));
+static void trak_shutdown P((int, struct peer *));
+static void trak_receive P((struct recvbuf *));
+static void trak_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_trak = {
+ trak_start, /* start up driver */
+ trak_shutdown, /* shut down driver */
+ trak_poll, /* transmit poll message */
+ noentry, /* not used (old trak_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old trak_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * trak_start - open the devices and initialize data for processing
+ */
+static int
+trak_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. The LDISC_ACTS line discipline inserts a
+ * timestamp following the "*" on-time character of the
+ * timecode.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = trak_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+
+ /*
+ * Start continuous time output. If something breaks, fold the
+ * tent and go home.
+ */
+ if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
+ refclock_report(peer, CEVNT_FAULT);
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ return (1);
+}
+
+
+/*
+ * trak_shutdown - shut down the clock
+ */
+static void
+trak_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * trak_receive - receive data from the serial interface
+ */
+static void
+trak_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char *dpt, *dpend;
+ char qchar;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. We
+ * then chuck out everything, including runts, except one
+ * message each poll interval.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (up->tcswitch || pp->lencode < 9)
+ return;
+ up->tcswitch = 1;
+
+ /*
+ * We get a buffer and timestamp following the '*' on-time
+ * character. If a valid timestamp, we use that in place of the
+ * buffer timestamp and edit out the timestamp for prettyprint
+ * billboards.
+ */
+ dpt = pp->lastcode;
+ dpend = dpt + pp->lencode;
+ if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
+ if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
+ pp->lastrec.l_i + 1) {
+ pp->lastrec = trtmp;
+ dpt += 9;
+ while (dpt < dpend)
+ *(dpt - 8) = *dpt++;
+ }
+ }
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("trak: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTRAK) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
+ */
+ if (sscanf(pp->lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality and leap characters. If unsynchronized, set
+ * the leap bits accordingly and exit.
+ */
+ if (qchar == '0')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * trak_poll - called by the transmit procedure
+ */
+static void
+trak_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ up->tcswitch = 0;
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_wwvb.c b/usr.sbin/xntpd/xntpd/refclock_wwvb.c
new file mode 100644
index 0000000..ea0c82e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_wwvb.c
@@ -0,0 +1,420 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB receivers
+ */
+#if defined(REFCLOCK) && defined(WWVB)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ * Synchronized Clock. This clock has proven a reliable source of time,
+ * except in some cases of high ambient conductive RF interference. The
+ * claimed accuracy of the clock is 100 usec relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * The DIPswitches on this clock should be set to 24-hour display, AUTO
+ * DST off, time zone 0 (UTC), data format 0 or 2 (see below) and baud
+ * rate 9600. If this clock is to used as the source for the IRIG Audio
+ * Decoder (refclock_irig.c in this distribution), set the DIPswitches
+ * for AM IRIG output and IRIG format 1 (IRIG B with signature control).
+ *
+ * There are two timecode formats used by these clocks. Format 0, which
+ * is available with both the Netclock/2 and 8170, and format 2, which
+ * is available only with the Netclock/2 and specially modified 8170.
+ *
+ * Format 0 (22 ASCII printing characters):
+ *
+ * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+ *
+ * on-time = first <cr> * hh:mm:ss = hours, minutes, seconds
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ *
+ * The alarm condition is indicated by other than ' ' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours.
+ *
+ * Format 2 (24 ASCII printing characters):
+ *
+ * <cr><lf>iqyy ddd hh:mm:ss.fff ld
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ * yy = year (as broadcast)
+ * ddd = day of year
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ *
+ * The alarm condition is indicated by other than ' ' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours. The unlock condition is indicated by other than ' '
+ * at Q.
+ *
+ * The Q is normally ' ' when the time error is less than 1 ms and a
+ * character in the set 'A'...'D' when the time error is less than 10,
+ * 100, 500 and greater than 500 ms respectively. The L is normally ' ',
+ * but is set to 'L' early in the month of an upcoming UTC leap second
+ * and reset to ' ' on the first day of the following month. The D is
+ * set to 'S' for standard time 'I' on the day preceding a switch to
+ * daylight time, 'D' for daylight time and 'O' on the day preceding a
+ * switch to standard time. The start bit of the first <cr> is
+ * synchronized to the indicated time as returned.
+ *
+ * This driver does not need to be told which format is in use - it
+ * figures out which one from the length of the message. A three-stage
+ * median filter is used to reduce jitter and provide a dispersion
+ * measure. The driver makes no attempt to correct for the intrinsic
+ * jitter of the radio itself, which is a known problem with the older
+ * radios.
+ *
+ * Fudge Factors
+ *
+ * This driver can retrieve a table of quality data maintained
+ * internally by the Netclock/2 receiver. If flag4 of the fudge
+ * configuration command is set to 1, the driver will retrieve this
+ * table and write it to the clockstats file on when the first timecode
+ * message of a new day is received.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Spectracom WWVB Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENWWVB0 22 /* format 0 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * WWVB unit control structure
+ */
+struct wwvbunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwvb_start P((int, struct peer *));
+static void wwvb_shutdown P((int, struct peer *));
+static void wwvb_receive P((struct recvbuf *));
+static void wwvb_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwvb = {
+ wwvb_start, /* start up driver */
+ wwvb_shutdown, /* shut down driver */
+ wwvb_poll, /* transmit poll message */
+ noentry, /* not used (old wwvb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwvb_start - open the devices and initialize data for processing
+ */
+static int
+wwvb_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = wwvb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * wwvb_shutdown - shut down the clock
+ */
+static void
+wwvb_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * wwvb_receive - receive data from the serial interface
+ */
+static void
+wwvb_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ int temp;
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char leapchar; /* leap indicator */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. Note: in format 0 on
+ * a Netclock/2 or upgraded 8170 the start bit is delayed 100
+ * +-50 us relative to the pps; however, on an unmodified 8170
+ * the start bit can be delayed up to 10 ms. In format 2 the
+ * reading precision is only to the millisecond. Thus, unless
+ * you have a pps gadget and don't have to have the year, format
+ * 0 provides the lowest jitter.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code uses the timecode length to determine
+ * whether format 0 or format 2. If the timecode has invalid
+ * length or is not in proper format, we declare bad format and
+ * exit.
+ */
+ switch (pp->lencode) {
+
+ case LENWWVB0:
+
+ /*
+ * Timecode format 0: "I ddd hh:mm:ss TZ=nn"
+ */
+ qualchar = leapchar = ' ';
+ if (sscanf(pp->lastcode, "%c %3d %2d:%2d:%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second) == 5)
+ break;
+
+ case LENWWVB2:
+
+ /*
+ * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD"
+ */
+ if (sscanf(pp->lastcode, "%c%c %2d %3d %2d:%2d:%2d.%3d %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->msec,
+ &leapchar) == 9)
+ break;
+
+ default:
+
+ if (up->linect > 0)
+ up->linect--;
+ else
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ * Once synchronized, the dispersion depends only on when the
+ * clock was last heard. The first time the clock is heard, the
+ * time last heard is faked based on the quality indicator. The
+ * magic numbers (in seconds) are from the clock specifications.
+ */
+ switch (qualchar) {
+
+ case ' ':
+ ltemp = 0;
+ break;
+
+ case 'A':
+ ltemp = 800;
+ break;
+
+ case 'B':
+ ltemp = 5300;
+ break;
+
+ case 'C':
+ ltemp = 25300;
+ break;
+
+ case 'D':
+ ltemp = NTP_MAXAGE;
+ break;
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time - ltemp;
+ }
+
+ /*
+ * If the monitor flag is set (flag4), we dump the internal
+ * quality table at the first timecode beginning the day.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
+ up->lasthour)
+ up->linect = MONLIN;
+ up->lasthour = pp->hour;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ trtmp = pp->lastrec;
+ trtmp.l_ui -= ltemp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &trtmp, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * wwvb_poll - called by the transmit procedure
+ */
+static void
+wwvb_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ char poll;
+
+ /*
+ * Time to poll the clock. The Spectracom clock responds to a
+ * 'T' by returning a timecode in the format(s) specified above.
+ * Note there is no checking on state, since this may not be the
+ * only customer reading the clock. Only one customer need poll
+ * the clock; all others just listen in. If nothing is heard
+ * from the clock for two polls, declare a timeout and keep
+ * going.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (up->linect > 0)
+ poll = 'R';
+ else
+ poll = 'T';
+ if (write(pp->io.fd, &poll, 1) != 1) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpdc/Makefile b/usr.sbin/xntpd/xntpdc/Makefile
new file mode 100644
index 0000000..a3c2ad8
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id: Makefile,v 1.8 1998/03/07 09:46:17 bde Exp $
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+DPADD= ${LIBNTP}
+LDADD= ${LIBNTP}
+
+PROG= xntpdc
+MAN8= ${.CURDIR}/../doc/xntpdc.8
+CLEANFILES+= .version version.c
+
+SRCS= ntpdc.c ntpdc_ops.c version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion xntpdc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/xntpdc/README b/usr.sbin/xntpd/xntpdc/README
new file mode 100644
index 0000000..9d0b661
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/README
@@ -0,0 +1,6 @@
+README file for directory ./xntpdc of the NTP Version 3 distribution
+
+This directory contains the sources for the xntpdc utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc.c b/usr.sbin/xntpd/xntpdc/ntpdc.c
new file mode 100644
index 0000000..6af6d1a
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc.c
@@ -0,0 +1,1533 @@
+/*
+ * xntpdc - control and monitor your xntpd daemon
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntpdc.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Because we now potentially understand a lot of commands (and
+ * it requires a lot of commands to talk to xntpd) we will run
+ * interactive if connected to a terminal.
+ */
+static int interactive = 0; /* set to 1 when we should prompt */
+static char * prompt = "xntpdc> "; /* prompt to ask him about */
+
+
+/*
+ * Keyid used for authenticated requests. Obtained on the fly.
+ */
+static u_long info_auth_keyid;
+
+/*
+ * Type of key md5 or des
+ */
+#define KEY_TYPE_DES 3
+#define KEY_TYPE_MD5 4
+
+static int info_auth_keytype = KEY_TYPE_DES; /* DES */
+
+
+/*
+ * Built in command handler declarations
+ */
+static int openhost P((char *));
+static int sendpkt P((char *, int));
+static void growpktdata P((void));
+static int getresponse P((int, int, int *, int *, char **));
+static int sendrequest P((int, int, int, int, int, char *));
+static void getcmds P((void));
+static RETSIGTYPE abortcmd P((int));
+static void docmd P((char *));
+static void tokenize P((char *, char **, int *));
+static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
+static int getarg P((char *, int, arg_v *));
+static int getnetnum P((char *, u_long *, char *));
+static void help P((struct parse *, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int helpsort P((const void *, const void *));
+#else
+static int helpsort P((char **, char **));
+#endif /* sgi */
+static void printusage P((struct xcmd *, FILE *));
+static void timeout P((struct parse *, FILE *));
+static void delay P((struct parse *, FILE *));
+static void host P((struct parse *, FILE *));
+static void keyid P((struct parse *, FILE *));
+static void keytype P((struct parse *, FILE *));
+static void passwd P((struct parse *, FILE *));
+static void hostnames P((struct parse *, FILE *));
+static void setdebug P((struct parse *, FILE *));
+static void quit P((struct parse *, FILE *));
+static void version P((struct parse *, FILE *));
+static void warning P((char *, char *, char *));
+static void error P((char *, char *, char *));
+static u_long getkeyid P((char *));
+
+
+/*
+ * Built-in commands we understand
+ */
+static struct xcmd builtins[] = {
+ { "?", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "help", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "timeout", timeout, { OPT|UINT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the primary receive time out" },
+ { "delay", delay, { OPT|INT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the delay added to encryption time stamps" },
+ { "host", host, { OPT|NTP_STR, NO, NO, NO },
+ { "hostname", "", "", "" },
+ "specify the host whose NTP server we talk to" },
+ { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO },
+ { "", "", "", "" },
+ "specify a password to use for authenticated requests"},
+ { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "specify whether hostnames or net numbers are printed"},
+ { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
+ { "no|more|less", "", "", "" },
+ "set/change debugging level" },
+ { "quit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit xntpdc" },
+ { "keyid", keyid, { OPT|UINT, NO, NO, NO },
+ { "key#", "", "", "" },
+ "set keyid to use for authenticated requests" },
+ { "keytype", keytype, { NTP_STR, NO, NO, NO },
+ { "key type (md5|des)", "", "", "" },
+ "set key type to use for authenticated requests (des|md5)" },
+ { "version", version, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print version number" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Default values we use.
+ */
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define DEFHOST "localhost" /* default host name */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 100 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
+
+/*
+ * Some variables used and manipulated locally
+ */
+static struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+static struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
+static l_fp delay_time; /* delay time */
+static char currenthost[LENHOSTNAME]; /* current host name */
+static struct sockaddr_in hostaddr = { 0 }; /* host address */
+static int showhostnames = 1; /* show host names by default */
+
+static int sockfd; /* fd socket is openned on */
+static int havehost = 0; /* set to 1 when host open */
+ struct servent *server_entry = NULL; /* server entry for ntp */
+
+/*
+ * Holds data returned from queries. We allocate INITDATASIZE
+ * octets to begin with, increasing this as we need to.
+ */
+#define INITDATASIZE (sizeof(struct resp_pkt) * 16)
+#define INCDATASIZE (sizeof(struct resp_pkt) * 8)
+
+static char *pktdata;
+static int pktdatasize;
+
+/*
+ * For commands typed on the command line (with the -c option)
+ */
+static int numcmds = 0;
+static char *ccmds[MAXCMDS];
+#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
+
+/*
+ * When multiple hosts are specified.
+ */
+static int numhosts = 0;
+static char *chosts[MAXHOSTS];
+#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_INCOMPLETE 16
+#define ERR_TIMEOUT 17
+
+/*
+ * Macro definitions we use
+ */
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * For converting time stamps to dates
+ */
+#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */
+
+/*
+ * Jump buffer for longjumping back to the command level
+ */
+static jmp_buf interrupt_buf;
+static int jump = 0;
+
+/*
+ * Pointer to current output unit
+ */
+static FILE *current_output;
+
+/*
+ * Command table imported from ntpdc_ops.c
+ */
+extern struct xcmd opcmds[];
+
+char *progname;
+int debug;
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ delay_time.l_ui = 0;
+ delay_time.l_uf = DEFDELAY;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "c:dilnps")) != EOF)
+ switch (c) {
+ case 'c':
+ ADDCMD(ntp_optarg);
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'l':
+ ADDCMD("listpeers");
+ break;
+ case 'n':
+ showhostnames = 0;
+ break;
+ case 'p':
+ ADDCMD("peers");
+ break;
+ case 's':
+ ADDCMD("dmpeers");
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr,
+ "usage: %s [-dilnps] [-c cmd] host ...\n",
+ progname);
+ exit(2);
+ }
+ if (ntp_optind == argc) {
+ ADDHOST(DEFHOST);
+ } else {
+ for (; ntp_optind < argc; ntp_optind++)
+ ADDHOST(argv[ntp_optind]);
+ }
+
+ if (numcmds == 0 && interactive == 0
+ && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
+ interactive = 1;
+ }
+
+ if (interactive)
+ (void) signal_no_reset(SIGINT, abortcmd);
+
+ /*
+ * Initialize the packet data buffer
+ */
+ pktdata = (char *)malloc(INITDATASIZE);
+ if (pktdata == NULL) {
+ (void) fprintf(stderr, "%s: malloc() failed!\n", progname);
+ exit(1);
+ }
+ pktdatasize = INITDATASIZE;
+
+ if (numcmds == 0) {
+ (void) openhost(chosts[0]);
+ getcmds();
+ } else {
+ int ihost;
+ int icmd;
+
+ for (ihost = 0; ihost < numhosts; ihost++) {
+ if (openhost(chosts[ihost]))
+ for (icmd = 0; icmd < numcmds; icmd++) {
+ if (numhosts > 1)
+ printf ("--- %s ---\n",chosts[ihost]);
+ docmd(ccmds[icmd]);
+ }
+ }
+ }
+ exit(0);
+}
+
+
+/*
+ * openhost - open a socket to a host
+ */
+static int
+openhost(hname)
+ char *hname;
+{
+ u_long netnum;
+ char temphost[LENHOSTNAME];
+
+ if (server_entry == NULL) {
+ server_entry = getservbyname("ntp", "udp");
+ if (server_entry == NULL) {
+ (void) fprintf(stderr, "%s: ntp/udp: unknown service\n",
+ progname);
+ exit(1);
+ }
+ if (debug > 2)
+ printf("Got ntp/udp service entry\n");
+ }
+
+ if (!getnetnum(hname, &netnum, temphost))
+ return 0;
+
+ if (debug > 2)
+ printf("Opening host %s\n", temphost);
+
+ if (havehost == 1) {
+ if (debug > 2)
+ printf("Closing old host %s\n", currenthost);
+ (void) close(sockfd);
+ havehost = 0;
+ }
+ (void) strcpy(currenthost, temphost);
+
+ hostaddr.sin_family = AF_INET;
+ hostaddr.sin_port = server_entry->s_port;
+ hostaddr.sin_addr.s_addr = netnum;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1)
+ error("socket", "", "");
+
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+#ifdef SO_RCVBUF
+ { int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ &rbufsize, sizeof(int)) == -1)
+ error("setsockopt", "", "");
+ }
+#endif
+#endif
+
+ if (connect(sockfd, (struct sockaddr *)&hostaddr,
+ sizeof(hostaddr)) == -1)
+ error("connect", "", "");
+
+ havehost = 1;
+ return 1;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the remote host
+ */
+static int
+sendpkt(xdata, xdatalen)
+ char *xdata;
+ int xdatalen;
+{
+ if (write(sockfd, xdata, xdatalen) == -1) {
+ warning("write to %s failed", currenthost, "");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * growpktdata - grow the packet data area
+ */
+static void
+growpktdata()
+{
+ pktdatasize += INCDATASIZE;
+ pktdata = (char *)realloc(pktdata, pktdatasize);
+ if (pktdata == 0) {
+ (void) fprintf(stderr, "%s: realloc() failed!\n", progname);
+ exit(1);
+ }
+}
+
+
+/*
+ * getresponse - get a (series of) response packet(s) and return the data
+ */
+static int
+getresponse(implcode, reqcode, ritems, rsize, rdata)
+ int implcode;
+ int reqcode;
+ int *ritems;
+ int *rsize;
+ char **rdata;
+{
+ struct resp_pkt rpkt;
+ struct timeval tvo;
+ int items;
+ int size;
+ int datasize;
+ char *datap;
+ char haveseq[MAXSEQ+1];
+ int firstpkt;
+ int lastseq;
+ int numrecv;
+ int seq;
+ fd_set fds;
+ int n;
+
+ /*
+ * This is pretty tricky. We may get between 1 and many packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how many we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *ritems = 0;
+ *rsize = 0;
+ firstpkt = 1;
+ numrecv = 0;
+ *rdata = datap = pktdata;
+ lastseq = 999; /* too big to be a sequence number */
+ memset(haveseq, 0, sizeof(haveseq));
+ FD_ZERO(&fds);
+
+again:
+ if (firstpkt)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+
+ FD_SET(sockfd, &fds);
+ n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+
+ if (n == -1) {
+ warning("select fails", "", "");
+ return -1;
+ }
+ if (n == 0) {
+ /*
+ * Timed out. Return what we have
+ */
+ if (firstpkt) {
+ (void) fprintf(stderr,
+ "%s: timed out, nothing received\n", currenthost);
+ return ERR_TIMEOUT;
+ } else {
+ (void) fprintf(stderr,
+ "%s: timed out with incomplete data\n",
+ currenthost);
+ if (debug) {
+ printf("Received sequence numbers");
+ for (n = 0; n <= MAXSEQ; n++)
+ if (haveseq[n])
+ printf(" %d,", n);
+ if (lastseq != 999)
+ printf(" last frame received\n");
+ else
+ printf(" last frame not received\n");
+ }
+ return ERR_INCOMPLETE;
+ }
+ }
+
+ n = read(sockfd, (char *)&rpkt, sizeof(rpkt));
+ if (n == -1) {
+ warning("read", "", "");
+ return -1;
+ }
+
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ if (debug)
+ printf("Short (%d byte) packet received\n", n);
+ goto again;
+ }
+ if (INFO_VERSION(rpkt.rm_vn_mode) != NTP_VERSION) {
+ if (debug)
+ printf("Packet received with version %d\n",
+ INFO_VERSION(rpkt.rm_vn_mode));
+ goto again;
+ }
+ if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
+ if (debug)
+ printf("Packet received with mode %d\n",
+ INFO_MODE(rpkt.rm_vn_mode));
+ goto again;
+ }
+ if (INFO_IS_AUTH(rpkt.auth_seq)) {
+ if (debug)
+ printf("Encrypted packet received\n");
+ goto again;
+ }
+ if (!ISRESPONSE(rpkt.rm_vn_mode)) {
+ if (debug)
+ printf("Received request packet, wanted response\n");
+ goto again;
+ }
+ if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
+ if (debug)
+ printf("Received packet with nonzero MBZ field!\n");
+ goto again;
+ }
+
+ /*
+ * Check implementation/request. Could be old data getting to us.
+ */
+ if (rpkt.implementation != implcode || rpkt.request != reqcode) {
+ if (debug)
+ printf(
+ "Received implementation/request of %d/%d, wanted %d/%d",
+ rpkt.implementation, rpkt.request,
+ implcode, reqcode);
+ goto again;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
+ if (debug && ISMORE(rpkt.rm_vn_mode)) {
+ printf("Error code %d received on not-final packet\n",
+ INFO_ERR(rpkt.err_nitems));
+ }
+ return (int)INFO_ERR(rpkt.err_nitems);
+ }
+
+
+ /*
+ * Collect items and size. Make sure they make sense.
+ */
+ items = INFO_NITEMS(rpkt.err_nitems);
+ size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
+
+ if ((datasize = items*size) > (n-RESP_HEADER_SIZE)) {
+ if (debug)
+ printf(
+ "Received items %d, size %d (total %d), data in packet is %d\n",
+ items, size, datasize, n-RESP_HEADER_SIZE);
+ goto again;
+ }
+
+ /*
+ * If this isn't our first packet, make sure the size matches
+ * the other ones.
+ */
+ if (!firstpkt && size != *rsize) {
+ if (debug)
+ printf("Received itemsize %d, previous %d\n",
+ size, *rsize);
+ goto again;
+ }
+
+ /*
+ * If we've received this before, toss it
+ */
+ seq = INFO_SEQ(rpkt.auth_seq);
+ if (haveseq[seq]) {
+ if (debug)
+ printf("Received duplicate sequence number %d\n", seq);
+ goto again;
+ }
+ haveseq[seq] = 1;
+
+ /*
+ * If this is the last in the sequence, record that.
+ */
+ if (!ISMORE(rpkt.rm_vn_mode)) {
+ if (lastseq != 999) {
+ printf("Received second end sequence packet\n");
+ goto again;
+ }
+ lastseq = seq;
+ }
+
+ /*
+ * So far, so good. Copy this data into the output array.
+ */
+ if ((datap + datasize) > (pktdata + pktdatasize)) {
+ int offset = datap - pktdata;
+ growpktdata();
+ *rdata = pktdata; /* might have been realloced ! */
+ datap = pktdata + offset;
+ }
+ memmove(datap, (char *)rpkt.data, datasize);
+ datap += datasize;
+ if (firstpkt) {
+ firstpkt = 0;
+ *rsize = size;
+ }
+ *ritems += items;
+
+ /*
+ * Finally, check the count of received packets. If we've got them
+ * all, return
+ */
+ ++numrecv;
+ if (numrecv <= lastseq)
+ goto again;
+ return INFO_OKAY;
+}
+
+
+/*
+ * sendrequest - format and send a request packet
+ */
+static int
+sendrequest(implcode, reqcode, auth, qitems, qsize, qdata)
+ int implcode;
+ int reqcode;
+ int auth;
+ int qitems;
+ int qsize;
+ char *qdata;
+{
+ struct req_pkt qpkt;
+ int datasize;
+
+ memset((char *)&qpkt, 0, sizeof qpkt);
+
+ qpkt.rm_vn_mode = RM_VN_MODE(0, 0);
+ qpkt.implementation = (u_char)implcode;
+ qpkt.request = (u_char)reqcode;
+
+ datasize = qitems * qsize;
+ if (datasize != 0 && qdata != NULL) {
+ memmove((char *)qpkt.data, qdata, datasize);
+ qpkt.err_nitems = ERR_NITEMS(0, qitems);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
+ } else {
+ qpkt.err_nitems = ERR_NITEMS(0, 0);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+ }
+
+ if (!auth) {
+ qpkt.auth_seq = AUTH_SEQ(0, 0);
+ return sendpkt((char *)&qpkt, REQ_LEN_NOMAC);
+ } else {
+ l_fp ts;
+ char *pass;
+
+ if (info_auth_keyid == 0) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == 0) {
+ (void) fprintf(stderr,
+ "Keyid must be defined, request not sent\n");
+ return 1;
+ }
+ }
+ if (!auth_havekey(info_auth_keyid)) {
+ pass = getpass("Password: ");
+ if (*pass != '\0')
+ authusekey(info_auth_keyid, info_auth_keytype,
+ pass);
+ }
+ if (auth_havekey(info_auth_keyid)) {
+ int maclen;
+
+ qpkt.auth_seq = AUTH_SEQ(1, 0);
+ qpkt.keyid = htonl(info_auth_keyid);
+ auth1crypt(info_auth_keyid, (U_LONG *)&qpkt,
+ REQ_LEN_NOMAC);
+ gettstamp(&ts);
+ L_ADD(&ts, &delay_time);
+ HTONL_FP(&ts, &qpkt.tstamp);
+ maclen = auth2crypt(info_auth_keyid, (U_LONG *)&qpkt,
+ REQ_LEN_NOMAC);
+ return sendpkt((char *)&qpkt, REQ_LEN_NOMAC+maclen);
+ } else {
+ (void) fprintf(stderr,
+ "No password, request not sent\n");
+ return 1;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * doquery - send a request and process the response
+ */
+int
+doquery(implcode, reqcode, auth, qitems, qsize, qdata, ritems, rsize, rdata,
+ quiet_mask)
+ int implcode;
+ int reqcode;
+ int auth;
+ int qitems;
+ int qsize;
+ char *qdata;
+ int *ritems;
+ int *rsize;
+ char **rdata;
+ int quiet_mask;
+{
+ int res;
+ char junk[512];
+ fd_set fds;
+ struct timeval tvzero;
+
+ /*
+ * Check to make sure host is open
+ */
+ if (!havehost) {
+ (void) fprintf(stderr, "***No host open, use `host' command\n");
+ return -1;
+ }
+
+ /*
+ * Poll the socket and clear out any pending data
+ */
+ do {
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(sockfd, &fds);
+ res = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ if (res == -1) {
+ warning("polling select", "", "");
+ return -1;
+ } else if (res > 0)
+ (void) read(sockfd, junk, sizeof junk);
+ } while (res > 0);
+
+
+ /*
+ * send a request
+ */
+ res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
+ if (res != 0)
+ return res;
+
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = getresponse(implcode, reqcode, ritems, rsize, rdata);
+
+ /* log error message if not told to be quiet */
+ if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
+ switch(res) {
+ case INFO_ERR_IMPL:
+ (void) fprintf(stderr,
+ "***Server implementation incompatable with our own\n");
+ break;
+ case INFO_ERR_REQ:
+ (void) fprintf(stderr,
+ "***Server doesn't implement this request\n");
+ break;
+ case INFO_ERR_FMT:
+ (void) fprintf(stderr,
+"***Server reports a format error in the received packet (shouldn't happen)\n");
+ break;
+ case INFO_ERR_NODATA:
+ (void) fprintf(stderr,
+ "***Server reports data not found\n");
+ break;
+ case INFO_ERR_AUTH:
+ (void) fprintf(stderr, "***Permission denied\n");
+ break;
+ case ERR_TIMEOUT:
+ (void) fprintf(stderr, "***Request timed out\n");
+ break;
+ case ERR_INCOMPLETE:
+ (void) fprintf(stderr,
+ "***Response from server was incomplete\n");
+ break;
+ default:
+ (void) fprintf(stderr,
+ "***Server returns unknown error code %d\n", res);
+ break;
+ }
+ }
+ return res;
+}
+
+
+/*
+ * getcmds - read commands from the standard input and execute them
+ */
+static void
+getcmds()
+{
+ char line[MAXLINE];
+
+ for (;;) {
+ if (interactive) {
+ (void) fputs(prompt, stderr);
+ (void) fflush(stderr);
+ }
+
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return;
+
+ docmd(line);
+ }
+}
+
+
+/*
+ * abortcmd - catch interrupts and abort the current command
+ */
+static RETSIGTYPE
+abortcmd(sig)
+int sig;
+{
+ if (current_output == stdout)
+ (void) fflush(stdout);
+ putc('\n', stderr);
+ (void) fflush(stderr);
+ if (jump) longjmp(interrupt_buf, 1);
+}
+
+
+/*
+ * docmd - decode the command line and execute a command
+ */
+static void
+docmd(cmdline)
+ char *cmdline;
+{
+ char *tokens[1+MAXARGS+2];
+ struct parse pcmd;
+ int ntok;
+ static int i;
+ struct xcmd *xcmd;
+
+ /*
+ * Tokenize the command line. If nothing on it, return.
+ */
+ tokenize(cmdline, tokens, &ntok);
+ if (ntok == 0)
+ return;
+
+ /*
+ * Find the appropriate command description.
+ */
+ i = findcmd(tokens[0], builtins, opcmds, &xcmd);
+ if (i == 0) {
+ (void) fprintf(stderr, "***Command `%s' unknown\n",
+ tokens[0]);
+ return;
+ } else if (i >= 2) {
+ (void) fprintf(stderr, "***Command `%s' ambiguous\n",
+ tokens[0]);
+ return;
+ }
+
+ /*
+ * Save the keyword, then walk through the arguments, interpreting
+ * as we go.
+ */
+ pcmd.keyword = tokens[0];
+ pcmd.nargs = 0;
+ for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
+ if ((i+1) >= ntok) {
+ if (!(xcmd->arg[i] & OPT)) {
+ printusage(xcmd, stderr);
+ return;
+ }
+ break;
+ }
+ if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
+ break;
+ if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
+ return;
+ pcmd.nargs++;
+ }
+
+ i++;
+ if (i < ntok && *tokens[i] == '>') {
+ char *fname;
+
+ if (*(tokens[i]+1) != '\0')
+ fname = tokens[i]+1;
+ else if ((i+1) < ntok)
+ fname = tokens[i+1];
+ else {
+ (void) fprintf(stderr, "***No file for redirect\n");
+ return;
+ }
+
+ current_output = fopen(fname, "w");
+ if (current_output == NULL) {
+ (void) fprintf(stderr, "***Error opening %s: ", fname);
+ perror("");
+ return;
+ }
+ i = 1; /* flag we need a close */
+ } else {
+ current_output = stdout;
+ i = 0; /* flag no close */
+ }
+
+ if (interactive && setjmp(interrupt_buf)) {
+ return;
+ } else {
+ jump = 1;
+ (xcmd->handler)(&pcmd, current_output);
+ jump = 0;
+ if (i) (void) fclose(current_output);
+ }
+}
+
+
+/*
+ * tokenize - turn a command line into tokens
+ */
+static void
+tokenize(line, tokens, ntok)
+ char *line;
+ char **tokens;
+ int *ntok;
+{
+ register char *cp;
+ register char *sp;
+ static char tspace[MAXLINE];
+
+ sp = tspace;
+ cp = line;
+ for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
+ tokens[*ntok] = sp;
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ do {
+ *sp++ = *cp++;
+ } while (!ISSPACE(*cp) && !ISEOL(*cp));
+
+ *sp++ = '\0';
+ }
+}
+
+
+
+/*
+ * findcmd - find a command in a command description table
+ */
+static int
+findcmd(str, clist1, clist2, cmd)
+ register char *str;
+ struct xcmd *clist1;
+ struct xcmd *clist2;
+ struct xcmd **cmd;
+{
+ register struct xcmd *cl;
+ register int clen;
+ int nmatch;
+ struct xcmd *nearmatch = NULL;
+ struct xcmd *clist;
+
+ clen = strlen(str);
+ nmatch = 0;
+ if (clist1 != 0)
+ clist = clist1;
+ else if (clist2 != 0)
+ clist = clist2;
+ else
+ return 0;
+
+again:
+ for (cl = clist; cl->keyword != 0; cl++) {
+ /* do a first character check, for efficiency */
+ if (*str != *(cl->keyword))
+ continue;
+ if (strncmp(str, cl->keyword, clen) == 0) {
+ /*
+ * Could be extact match, could be approximate.
+ * Is exact if the length of the keyword is the
+ * same as the str.
+ */
+ if (*((cl->keyword) + clen) == '\0') {
+ *cmd = cl;
+ return 1;
+ }
+ nmatch++;
+ nearmatch = cl;
+ }
+ }
+
+ /*
+ * See if there is more to do. If so, go again. Sorry about the
+ * goto, too much looking at BSD sources...
+ */
+ if (clist == clist1 && clist2 != 0) {
+ clist = clist2;
+ goto again;
+ }
+
+ /*
+ * If we got extactly 1 near match, use it, else return number
+ * of matches.
+ */
+ if (nmatch == 1) {
+ *cmd = nearmatch;
+ return 1;
+ }
+ return nmatch;
+}
+
+
+/*
+ * getarg - interpret an argument token
+ */
+static int
+getarg(str, code, argp)
+ char *str;
+ int code;
+ arg_v *argp;
+{
+ int isneg;
+ char *cp, *np;
+ static char *digits = "0123456789";
+
+ switch (code & ~OPT) {
+ case NTP_STR:
+ argp->string = str;
+ break;
+ case ADD:
+ if (!getnetnum(str, &(argp->netnum), (char *)0)) {
+ return 0;
+ }
+ break;
+ case INT:
+ case UINT:
+ isneg = 0;
+ np = str;
+ if (*np == '-') {
+ np++;
+ isneg = 1;
+ }
+
+ argp->uval = 0;
+ do {
+ cp = strchr(digits, *np);
+ if (cp == NULL) {
+ (void) fprintf(stderr,
+ "***Illegal integer value %s\n", str);
+ return 0;
+ }
+ argp->uval *= 10;
+ argp->uval += (cp - digits);
+ } while (*(++np) != '\0');
+
+ if (isneg) {
+ if ((code & ~OPT) == UINT) {
+ (void) fprintf(stderr,
+ "***Value %s should be unsigned\n", str);
+ return 0;
+ }
+ argp->ival = -argp->ival;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+/*
+ * getnetnum - given a host name, return its net number
+ * and (optional) full name
+ */
+static int
+getnetnum(host, num, fullhost)
+ char *host;
+ u_long *num;
+ char *fullhost;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ if (fullhost != 0) {
+ (void) sprintf(fullhost,
+ "%u.%u.%u.%u", (u_int)((htonl(*num)>>24)&0xff),
+ (u_int)((htonl(*num)>>16)&0xff), (u_int)((htonl(*num)>>8)&0xff),
+ (u_int)(htonl(*num)&0xff));
+ }
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(u_long));
+ if (fullhost != 0)
+ (void) strcpy(fullhost, hp->h_name);
+ return 1;
+ } else {
+ (void) fprintf(stderr, "***Can't find host %s\n", host);
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * nntohost - convert network number to host name. This routine enforces
+ * the showhostnames setting.
+ */
+char *
+nntohost(netnum)
+ u_long netnum;
+{
+ if (!showhostnames)
+ return numtoa(netnum);
+ if ((ntohl(netnum) & REFCLOCK_MASK) == REFCLOCK_ADDR)
+ return refnumtoa(netnum);
+ return numtohost(netnum);
+}
+
+
+/*
+ * Finally, the built in command handlers
+ */
+
+/*
+ * help - tell about commands, or details of a particular command
+ */
+static void
+help(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int n;
+ struct xcmd *xcp;
+ char *cmd;
+ char *cmdsort[100];
+ int length[100];
+ int maxlength;
+ int numperline;
+ static char *spaces = " "; /* 20 spaces */
+
+ if (pcmd->nargs == 0) {
+ n = 0;
+ for (xcp = builtins; xcp->keyword != 0; xcp++) {
+ if (*(xcp->keyword) != '?')
+ cmdsort[n++] = xcp->keyword;
+ }
+ for (xcp = opcmds; xcp->keyword != 0; xcp++)
+ cmdsort[n++] = xcp->keyword;
+
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)cmdsort, n, sizeof(char *), helpsort);
+#else
+ qsort((char *)cmdsort, n, sizeof(char *), helpsort);
+#endif /* sgi */
+
+ maxlength = 0;
+ for (i = 0; i < n; i++) {
+ length[i] = strlen(cmdsort[i]);
+ if (length[i] > maxlength)
+ maxlength = length[i];
+ }
+ maxlength++;
+ numperline = 76 / maxlength;
+
+ (void) fprintf(fp, "Commands available:\n");
+ for (i = 0; i < n; i++) {
+ if ((i % numperline) == (numperline-1)
+ || i == (n-1))
+ (void) fprintf(fp, "%s\n", cmdsort[i]);
+ else
+ (void) fprintf(fp, "%s%s", cmdsort[i],
+ spaces+20-maxlength+length[i]);
+ }
+ } else {
+ cmd = pcmd->argval[0].string;
+ n = findcmd(cmd, builtins, opcmds, &xcp);
+ if (n == 0) {
+ (void) fprintf(stderr,
+ "Command `%s' is unknown\n", cmd);
+ return;
+ } else if (n >= 2) {
+ (void) fprintf(stderr,
+ "Command `%s' is ambiguous\n", cmd);
+ return;
+ }
+ (void) fprintf(fp, "function: %s\n", xcp->comment);
+ printusage(xcp, fp);
+ }
+}
+
+
+/*
+ * helpsort - do hostname qsort comparisons
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+helpsort(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const char **name1 = (const char **)t1;
+ const char **name2 = (const char **)t2;
+#else
+helpsort(name1, name2)
+ char **name1;
+ char **name2;
+{
+#endif /* sgi || bsdi */
+ return strcmp(*name1, *name2);
+}
+
+
+/*
+ * printusage - print usage information for a command
+ */
+static void
+printusage(xcp, fp)
+ struct xcmd *xcp;
+ FILE *fp;
+{
+ register int i;
+
+ (void) fprintf(fp, "usage: %s", xcp->keyword);
+ for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
+ if (xcp->arg[i] & OPT)
+ (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
+ else
+ (void) fprintf(fp, " %s", xcp->desc[i]);
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+/*
+ * timeout - set time out time
+ */
+static void
+timeout(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int val;
+
+ if (pcmd->nargs == 0) {
+ val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
+ (void) fprintf(fp, "primary timeout %d ms\n", val);
+ } else {
+ tvout.tv_sec = pcmd->argval[0].uval / 1000;
+ tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
+ * 1000;
+ }
+}
+
+
+/*
+ * delay - set delay for auth requests
+ */
+static void
+delay(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int isneg;
+ u_long val;
+
+ if (pcmd->nargs == 0) {
+ val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
+ (void) fprintf(fp, "delay %lu ms\n", val);
+ } else {
+ if (pcmd->argval[0].ival < 0) {
+ isneg = 1;
+ val = (u_long)(-pcmd->argval[0].ival);
+ } else {
+ isneg = 0;
+ val = (u_long)pcmd->argval[0].ival;
+ }
+
+ delay_time.l_ui = val / 1000;
+ val %= 1000;
+ delay_time.l_uf = val * 4294967; /* 2**32/1000 */
+
+ if (isneg)
+ L_NEG(&delay_time);
+ }
+}
+
+
+/*
+ * host - set the host we are dealing with.
+ */
+static void
+host(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (havehost)
+ (void) fprintf(fp, "current host is %s\n", currenthost);
+ else
+ (void) fprintf(fp, "no current host\n");
+ } else if (openhost(pcmd->argval[0].string)) {
+ (void) fprintf(fp, "current host set to %s\n", currenthost);
+ } else {
+ if (havehost)
+ (void) fprintf(fp,
+ "current host remains %s\n", currenthost);
+ else
+ (void) fprintf(fp, "still no current host\n");
+ }
+}
+
+
+/*
+ * keyid - get a keyid to use for authenticating requests
+ */
+static void
+keyid(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (info_auth_keyid == 0)
+ (void) fprintf(fp, "no keyid defined\n");
+ else
+ (void) fprintf(fp, "keyid is %lu\n", info_auth_keyid);
+ } else {
+ info_auth_keyid = pcmd->argval[0].uval;
+ }
+}
+
+
+/*
+ * keytype - get type of key to use for authenticating requests
+ */
+static void
+keytype(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0)
+ fprintf(fp, "keytype is %s",
+ (info_auth_keytype == KEY_TYPE_MD5) ? "md5" : "des");
+ else
+ switch (*(pcmd->argval[0].string)) {
+ case 'm':
+ case 'M':
+ info_auth_keytype = KEY_TYPE_MD5;
+ break;
+
+ case 'd':
+ case 'D':
+ info_auth_keytype = KEY_TYPE_DES;
+ break;
+
+ default:
+ fprintf(fp, "keytype must be 'md5' or 'des'\n");
+ }
+}
+
+
+
+/*
+ * passwd - get an authentication key
+ */
+/*ARGSUSED*/
+static void
+passwd(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *pass;
+
+ if (info_auth_keyid == 0) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == 0) {
+ (void)fprintf(fp, "Keyid must be defined\n");
+ return;
+ }
+ }
+ if (!interactive) {
+ authusekey(info_auth_keyid, info_auth_keytype,
+ pcmd->argval[0].string);
+ } else {
+ pass = getpass("Password: ");
+ if (*pass == '\0')
+ (void) fprintf(fp, "Password unchanged\n");
+ else
+ authusekey(info_auth_keyid, info_auth_keytype, pass);
+ }
+}
+
+
+/*
+ * hostnames - set the showhostnames flag
+ */
+static void
+hostnames(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (showhostnames)
+ (void) fprintf(fp, "hostnames being shown\n");
+ else
+ (void) fprintf(fp, "hostnames not being shown\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes"))
+ showhostnames = 1;
+ else if (STREQ(pcmd->argval[0].string, "no"))
+ showhostnames = 0;
+ else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+/*
+ * setdebug - set/change debugging level
+ */
+static void
+setdebug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "debug level is %d\n", debug);
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ debug = 0;
+ } else if (STREQ(pcmd->argval[0].string, "more")) {
+ debug++;
+ } else if (STREQ(pcmd->argval[0].string, "less")) {
+ debug--;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "debug level set to %d\n", debug);
+}
+
+
+/*
+ * quit - stop this nonsense
+ */
+/*ARGSUSED*/
+static void
+quit(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (havehost)
+ (void) close(sockfd); /* cleanliness next to godliness */
+ exit(0);
+}
+
+
+/*
+ * version - print the current version number
+ */
+/*ARGSUSED*/
+static void
+version(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern char *Version;
+
+ (void) fprintf(fp, "%s\n", Version);
+}
+
+
+/*
+ * warning - print a warning message
+ */
+static void
+warning(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, st1, st2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+}
+
+
+/*
+ * error - print a message and exit
+ */
+static void
+error(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ warning(fmt, st1, st2);
+ exit(1);
+}
+
+/*
+ * getkeyid - prompt the user for a keyid to use
+ */
+static u_long
+getkeyid(prompt)
+char *prompt;
+{
+ register char *p;
+ register c;
+ FILE *fi;
+ char pbuf[20];
+
+ if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ fprintf(stderr, "%s", prompt); fflush(stderr);
+ for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+ if (p < &pbuf[18])
+ *p++ = c;
+ }
+ *p = '\0';
+ if (fi != stdin)
+ fclose(fi);
+ return (u_long)atoi(pbuf);
+}
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc.h b/usr.sbin/xntpd/xntpdc/ntpdc.h
new file mode 100644
index 0000000..bb23024
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc.h
@@ -0,0 +1,59 @@
+/*
+ * ntpdc.h - definitions of interest to xntpdc
+ */
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_request.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+
+/*
+ * Maximum number of arguments
+ */
+#define MAXARGS 4
+
+/*
+ * Flags for forming descriptors.
+ */
+#define OPT 0x80 /* this argument is optional, or'd with type */
+
+#define NO 0x0
+#define NTP_STR 0x1 /* string argument */
+#define UINT 0x2 /* unsigned integer */
+#define INT 0x3 /* signed integer */
+#define ADD 0x4 /* IP network address */
+
+/*
+ * Arguments are returned in a union
+ */
+typedef union {
+ char *string;
+ long ival;
+ u_long uval;
+ u_long netnum;
+} arg_v;
+
+/*
+ * Structure for passing parsed command line
+ */
+struct parse {
+ char *keyword;
+ arg_v argval[MAXARGS];
+ int nargs;
+};
+
+/*
+ * xntpdc includes a command parser which could charitably be called
+ * crude. The following structure is used to define the command
+ * syntax.
+ */
+struct xcmd {
+ char *keyword; /* command key word */
+ void (*handler) P((struct parse *, FILE *)); /* command handler */
+ u_char arg[MAXARGS]; /* descriptors for arguments */
+ char *desc[MAXARGS]; /* descriptions for arguments */
+ char *comment;
+};
+
+extern int doquery P((int, int, int, int, int, char *, int *, int *, char **, int));
+extern char * nntohost P((u_long));
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc_ops.c b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c
new file mode 100644
index 0000000..7010f9b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c
@@ -0,0 +1,2441 @@
+/*
+ * ntpdc_ops.c - subroutines which are called to perform operations by xntpdc
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#ifndef __bsdi__
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "ntpdc.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Declarations for command handlers in here
+ */
+static int checkitems P((int, FILE *));
+static int checkitemsize P((int, int));
+static int check1item P((int, FILE *));
+static void peerlist P((struct parse *, FILE *));
+static void peers P((struct parse *, FILE *));
+static void dmpeers P((struct parse *, FILE *));
+static void dopeers P((struct parse *, FILE *, int));
+static void printpeer P((struct info_peer *, FILE *));
+static void showpeer P((struct parse *, FILE *));
+static void peerstats P((struct parse *, FILE *));
+static void loopinfo P((struct parse *, FILE *));
+static void sysinfo P((struct parse *, FILE *));
+static void sysstats P((struct parse *, FILE *));
+static void iostats P((struct parse *, FILE *));
+static void memstats P((struct parse *, FILE *));
+static void timerstats P((struct parse *, FILE *));
+static void addpeer P((struct parse *, FILE *));
+static void addserver P((struct parse *, FILE *));
+static void broadcast P((struct parse *, FILE *));
+static void doconfig P((struct parse *, FILE *, int));
+static void unconfig P((struct parse *, FILE *));
+static void set P((struct parse *, FILE *));
+static void sys_clear P((struct parse *, FILE *));
+static void doset P((struct parse *, FILE *, int));
+static void reslist P((struct parse *, FILE *));
+static void restrict P((struct parse *, FILE *));
+static void unrestrict P((struct parse *, FILE *));
+static void delrestrict P((struct parse *, FILE *));
+static void do_restrict P((struct parse *, FILE *, int));
+static void monlist P((struct parse *, FILE *));
+static void monitor P((struct parse *, FILE *));
+static void reset P((struct parse *, FILE *));
+static void preset P((struct parse *, FILE *));
+static void readkeys P((struct parse *, FILE *));
+static void trustkey P((struct parse *, FILE *));
+static void untrustkey P((struct parse *, FILE *));
+static void do_trustkey P((struct parse *, FILE *, int));
+static void authinfo P((struct parse *, FILE *));
+static void traps P((struct parse *, FILE *));
+static void addtrap P((struct parse *, FILE *));
+static void clrtrap P((struct parse *, FILE *));
+static void do_addclr_trap P((struct parse *, FILE *, int));
+static void requestkey P((struct parse *, FILE *));
+static void controlkey P((struct parse *, FILE *));
+static void do_changekey P((struct parse *, FILE *, int));
+static void ctlstats P((struct parse *, FILE *));
+static void leapinfo P((struct parse *, FILE *));
+static void clockstat P((struct parse *, FILE *));
+static void fudge P((struct parse *, FILE *));
+static void clkbug P((struct parse *, FILE *));
+static void setprecision P((struct parse *, FILE *));
+static void kerninfo P((struct parse *, FILE *));
+
+/*
+ * Commands we understand. Ntpdc imports this.
+ */
+struct xcmd opcmds[] = {
+ { "listpeers", peerlist, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display list of peers the server knows about" },
+ { "peers", peers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer summary information" },
+ { "dmpeers", dmpeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer summary info the way Dave Mills likes it" },
+ { "showpeer", showpeer, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "display detailed information for one or more peers" },
+ { "pstats", peerstats, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "display statistical information for one or more peers" },
+ { "loopinfo", loopinfo, { OPT|NTP_STR, NO, NO, NO },
+ { "oneline|multiline", "", "", "" },
+ "display loop filter information" },
+ { "sysinfo", sysinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display local server information" },
+ { "sysstats", sysstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display local server statistics" },
+ { "memstats", memstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer memory usage statistics" },
+ { "iostats", iostats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display I/O subsystem statistics" },
+ { "timerstats", timerstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display event timer subsystem statistics" },
+ { "addpeer", addpeer, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll|prefer" },
+ "configure a new peer association" },
+ { "addserver", addserver, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll|prefer" },
+ "configure a new server" },
+ { "broadcast", broadcast, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll" },
+ "configure broadcasting time service" },
+ { "unconfig", unconfig, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "unconfigure existing peer assocations" },
+ { "enable", set, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "auth|bclient|pll|pps|monitor|stats", "...", "...", "..." },
+ "set a system flag (auth, bclient, pll, pps, monitor, stats)" },
+ { "disable", sys_clear, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "auth|bclient|pll|pps|monitor|stats", "...", "...", "..." },
+ "clear a system flag (auth, bclient, pll, pps, monitor, stats)" },
+ { "reslist", reslist, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the server's restrict list" },
+ { "restrict", restrict, { ADD, ADD, NTP_STR, OPT|NTP_STR },
+ { "address", "mask",
+ "ntpport|ignore|noserve|notrust|noquery|nomodify|nopeer",
+ "..." },
+ "create restrict entry/add flags to entry" },
+ { "unrestrict", unrestrict, { ADD, ADD, NTP_STR, OPT|NTP_STR },
+ { "address", "mask",
+ "ntpport|ignore|noserve|notrust|noquery|nomodify|nopeer",
+ "..." },
+ "remove flags from a restrict entry" },
+ { "delrestrict", delrestrict, { ADD, ADD, OPT|NTP_STR, NO },
+ { "address", "mask", "ntpport", "" },
+ "delete a restrict entry" },
+ { "monlist", monlist, { OPT|INT, NO, NO, NO },
+ { "version", "", "", "" },
+ "display data the server's monitor routines have collected" },
+ { "monitor", monitor, { NTP_STR, NO, NO, NO },
+ { "on|off", "", "", "" },
+ "turn the server's monitoring facility on or off" },
+ { "reset", reset, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "io|sys|mem|timer|auth|allpeers", "...", "...", "..." },
+ "reset various subsystem statistics counters" },
+ { "preset", preset, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "reset stat counters associated with particular peer(s)" },
+ { "readkeys", readkeys, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "request a reread of the keys file and re-init of system keys" },
+ { "trustkey", trustkey, { UINT, OPT|UINT, OPT|UINT, OPT|UINT },
+ { "keyid", "keyid", "keyid", "keyid" },
+ "add one or more key ID's to the trusted list" },
+ { "untrustkey", untrustkey, { UINT, OPT|UINT, OPT|UINT, OPT|UINT },
+ { "keyid", "keyid", "keyid", "keyid" },
+ "remove one or more key ID's from the trusted list" },
+ { "authinfo", authinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the state of the authentication code" },
+ { "traps", traps, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the traps set in the server" },
+ { "addtrap", addtrap, { ADD, OPT|UINT, OPT|ADD, NO },
+ { "address", "port", "interface", "" },
+ "configure a trap in the server" },
+ { "clrtrap", clrtrap, { ADD, OPT|UINT, OPT|ADD, NO },
+ { "address", "port", "interface", "" },
+ "remove a trap (configured or otherwise) from the server" },
+ { "requestkey", requestkey, { UINT, NO, NO, NO },
+ { "keyid", "", "", "" },
+ "change the keyid the server uses to authenticate requests" },
+ { "controlkey", controlkey, { UINT, NO, NO, NO },
+ { "keyid", "", "", "" },
+ "change the keyid the server uses to authenticate control messages" },
+ { "ctlstats", ctlstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display packet count statistics from the control module" },
+ { "leapinfo", leapinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the current leap second state" },
+ { "clockstat", clockstat, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "address", "address", "address", "address" },
+ "display clock status information" },
+ { "fudge", fudge, { ADD, NTP_STR, NTP_STR, NO },
+ { "address", "time1|time2|val1|val2|flags", "value", "" },
+ "set/change one of a clock's fudge factors" },
+ { "clkbug", clkbug, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "address", "address", "address", "address" },
+ "display clock debugging information" },
+ { "setprecision", setprecision, { INT, NO, NO, NO },
+ { "sys_precision", "", "", "" },
+ "set the server's advertised precision" },
+ { "kerninfo", kerninfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the kernel pll/pps variables" },
+
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Imported from ntpdc.c
+ */
+extern int showhostnames;
+extern int debug;
+extern struct servent *server_entry;
+
+/*
+ * For quick string comparisons
+ */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+
+/*
+ * checkitems - utility to print a message if no items were returned
+ */
+static int
+checkitems(items, fp)
+ int items;
+ FILE *fp;
+{
+ if (items == 0) {
+ (void) fprintf(fp, "No data returned in response to query\n");
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * checkitemsize - utility to print a message if the item size is wrong
+ */
+static int
+checkitemsize(itemsize, expected)
+ int itemsize;
+ int expected;
+{
+ if (itemsize != expected) {
+ (void) fprintf(stderr,
+ "***Incorrect item size returned by remote host (%d should be %d)\n",
+ itemsize, expected);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * check1item - check to make sure we have exactly one item
+ */
+static int
+check1item(items, fp)
+ int items;
+ FILE *fp;
+{
+ if (items == 0) {
+ (void) fprintf(fp, "No data returned in response to query\n");
+ return 0;
+ }
+ if (items > 1) {
+ (void) fprintf(fp, "Expected one item in response, got %d\n",
+ items);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+/*
+ * peerlist - get a short list of peers
+ */
+/*ARGSUSED*/
+static void
+peerlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer_list *plist;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_LIST, 0, 0, 0, (char *)NULL, &items,
+ &itemsize, (char **)&plist, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_list)))
+ return;
+
+ while (items > 0) {
+ (void) fprintf(fp, "%-9s %s\n", modetoa(plist->hmode),
+ nntohost(plist->address));
+ plist++;
+ items--;
+ }
+}
+
+
+/*
+ * peers - show peer summary
+ */
+static void
+peers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(pcmd, fp, 0);
+}
+
+/*
+ * dmpeers - show peer summary, Dave Mills style
+ */
+static void
+dmpeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(pcmd, fp, 1);
+}
+
+
+/*
+ * peers - show peer summary
+ */
+/*ARGSUSED*/
+static void
+dopeers(pcmd, fp, dmstyle)
+ struct parse *pcmd;
+ FILE *fp;
+ int dmstyle;
+{
+ struct info_peer_summary *plist;
+ int items;
+ int itemsize;
+ int ntp_poll;
+ int res;
+ int c;
+ l_fp tempts;
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_LIST_SUM, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&plist, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_summary)))
+ return;
+
+ (void) fprintf(fp,
+ " remote local st poll reach delay offset disp\n");
+ (void) fprintf(fp,
+ "=======================================================================\n");
+ while (items > 0) {
+ if (!dmstyle) {
+ if (plist->flags & INFO_FLAG_SYSPEER)
+ c = '*';
+ else if (plist->hmode == MODE_ACTIVE)
+ c = '+';
+ else if (plist->hmode == MODE_PASSIVE)
+ c = '-';
+ else if (plist->hmode == MODE_CLIENT)
+ c = '=';
+ else if (plist->hmode == MODE_BROADCAST)
+ c = '^';
+ else if (plist->hmode == MODE_BCLIENT)
+ c = '~';
+ else
+ c = ' ';
+ } else {
+ if (plist->flags & INFO_FLAG_SYSPEER)
+ c = '*';
+ else if (plist->flags & INFO_FLAG_SHORTLIST)
+ c = '+';
+ else if (plist->flags & INFO_FLAG_SEL_CANDIDATE)
+ c = '.';
+ else
+ c = ' ';
+ }
+ NTOHL_FP(&(plist->offset), &tempts);
+ ntp_poll = 1<<max(min3(plist->ppoll, plist->hpoll, NTP_MAXPOLL),
+ NTP_MINPOLL);
+ (void) fprintf(fp,
+ "%c%-15.15s %-15.15s %2d %4d %3o %7.7s %9.9s %7.7s\n",
+ c, nntohost(plist->srcadr),
+ numtoa(plist->dstadr),
+ plist->stratum, ntp_poll, plist->reach,
+ fptoa(NTOHS_FP(plist->delay), 5),
+ lfptoa(&tempts, 6),
+ ufptoa(NTOHS_FP(plist->dispersion), 5));
+
+ plist++;
+ items--;
+ }
+}
+
+/* Convert a refid & stratum (in host order) to a string */
+static char*
+refid_string(refid, stratum)
+ u_long refid;
+ int stratum;
+{
+ if (stratum <= 1) {
+ static char junk[5];
+ junk[4] = 0;
+ memmove(junk, (char *)&refid, 4);
+ return junk;
+ }
+
+ return numtoa(refid);
+}
+
+/*
+ * printpeer - print detail information for a peer
+ */
+static void
+printpeer(pp, fp)
+ register struct info_peer *pp;
+ FILE *fp;
+{
+ register int i;
+ char *str;
+ l_fp tempts;
+
+ (void) fprintf(fp, "remote %s, local %s\n",
+ numtoa(pp->srcadr), numtoa(pp->dstadr));
+
+ (void) fprintf(fp, "hmode %s, pmode %s, stratum %d, precision %d\n",
+ modetoa(pp->hmode), modetoa(pp->pmode),
+ pp->stratum, pp->precision);
+
+ (void) fprintf(fp,
+ "leap %c%c, refid [%s], rootdistance %s, rootdispersion %s\n",
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0',
+ refid_string(pp->refid, pp->stratum), fptoa(NTOHS_FP(pp->rootdelay), 5),
+ ufptoa(NTOHS_FP(pp->rootdispersion), 5));
+
+ (void) fprintf(fp,
+ "ppoll %d, hpoll %d, keyid %lu, version %d, association %u\n",
+ pp->ppoll, pp->hpoll, (u_long)pp->keyid, pp->version, ntohs(pp->associd));
+
+ (void) fprintf(fp,
+ "valid %d, reach %03o, unreach %d, flash %03o, ",
+ pp->valid, pp->reach, pp->unreach, pp->flash);
+
+ (void) fprintf(fp, "boffset %s, ttl %d\n",
+ fptoa(NTOHS_FP(pp->estbdelay), 5), pp->ttl);
+
+ (void) fprintf(fp, "timer %lds, flags", (long)ntohl(pp->timer));
+ if (pp->flags == 0) {
+ (void) fprintf(fp, " none\n");
+ } else {
+ str = "";
+ if (pp->flags & INFO_FLAG_SYSPEER) {
+ (void) fprintf(fp, " system_peer");
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_CONFIG) {
+ (void) fprintf(fp, "%s config", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_REFCLOCK) {
+ (void) fprintf(fp, "%s refclock", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_AUTHENABLE) {
+ (void) fprintf(fp, "%s auth", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_BCLIENT) {
+ (void) fprintf(fp, "%s bclient", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_PREFER) {
+ (void) fprintf(fp, "%s prefer", str);
+ }
+ (void) fprintf(fp, "\n");
+ }
+
+ NTOHL_FP(&pp->reftime, &tempts);
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->org, &tempts);
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->rec, &tempts);
+ (void) fprintf(fp, "receive timestamp: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->xmt, &tempts);
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&tempts));
+
+ (void) fprintf(fp, "filter delay: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s",
+ fptoa(NTOHS_FP(pp->filtdelay[i]), 5));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter offset:");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ NTOHL_FP(&pp->filtoffset[i], &tempts);
+ (void) fprintf(fp, " %-8.8s", lfptoa(&tempts, 6));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter order: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8d", pp->order[i]);
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+
+ NTOHL_FP(&pp->offset, &tempts);
+ (void) fprintf(fp,
+ "offset %s, delay %s, dispersion %s, selectdisp %s\n",
+ lfptoa(&tempts, 6), fptoa(NTOHS_FP(pp->delay), 5),
+ ufptoa(NTOHS_FP(pp->dispersion), 5),
+ ufptoa(NTOHS_FP(pp->selectdisp), 5));
+}
+
+
+/*
+ * showpeer - show detailed information for a peer
+ */
+static void
+showpeer(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer *pp;
+ /* 4 is the maximum number of peers which will fit in a packet */
+ struct info_peer_list plist[min(MAXARGS, 4)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 4); qitems++) {
+ plist[qitems].address = pcmd->argval[qitems].netnum;
+ plist[qitems].port = server_entry->s_port;
+ plist[qitems].hmode = plist[qitems].flags = 0;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_INFO, 0, qitems,
+ sizeof(struct info_peer_list), (char *)plist, &items,
+ &itemsize, (char **)&pp, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer)))
+ return;
+
+ while (items-- > 0) {
+ printpeer(pp, fp);
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ pp++;
+ }
+}
+
+
+/*
+ * peerstats - return statistics for a peer
+ */
+static void
+peerstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer_stats *pp;
+ /* 4 is the maximum number of peers which will fit in a packet */
+ struct info_peer_list plist[min(MAXARGS, 4)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 4); qitems++) {
+ plist[qitems].address = pcmd->argval[qitems].netnum;
+ plist[qitems].port = server_entry->s_port;
+ plist[qitems].hmode = plist[qitems].flags = 0;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_STATS, 0, qitems,
+ sizeof(struct info_peer_list), (char *)plist, &items,
+ &itemsize, (char **)&pp, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_stats)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "remote host: %s\n",
+ nntohost(pp->srcadr));
+ (void) fprintf(fp, "local interface: %s\n",
+ numtoa(pp->dstadr));
+ (void) fprintf(fp, "time last received: %lds\n",
+ (long)ntohl(pp->timereceived));
+ (void) fprintf(fp, "time until next send: %lds\n",
+ (long)ntohl(pp->timetosend));
+ (void) fprintf(fp, "reachability change: %lds\n",
+ (long)ntohl(pp->timereachable));
+ (void) fprintf(fp, "packets sent: %ld\n",
+ (long)ntohl(pp->sent));
+ (void) fprintf(fp, "packets received: %ld\n",
+ (long)ntohl(pp->processed));
+ (void) fprintf(fp, "bad authentication: %ld\n",
+ (long)ntohl(pp->badauth));
+ (void) fprintf(fp, "bogus origin: %ld\n",
+ (long)ntohl(pp->bogusorg));
+ (void) fprintf(fp, "duplicate: %ld\n",
+ (long)ntohl(pp->oldpkt));
+ (void) fprintf(fp, "bad dispersion: %ld\n",
+ (long)ntohl(pp->seldisp));
+ (void) fprintf(fp, "bad reference time: %ld\n",
+ (long)ntohl(pp->selbroken));
+ (void) fprintf(fp, "candidate order: %d\n",
+ (int)pp->candidate);
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ pp++;
+ }
+}
+
+
+/*
+ * loopinfo - show loop filter information
+ */
+static void
+loopinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_loop *il;
+ int items;
+ int itemsize;
+ int oneline = 0;
+ int res;
+ l_fp tempts;
+
+ if (pcmd->nargs > 0) {
+ if (STREQ(pcmd->argval[0].string, "oneline"))
+ oneline = 1;
+ else if (STREQ(pcmd->argval[0].string, "multiline"))
+ oneline = 0;
+ else {
+ (void) fprintf(stderr, "How many lines?\n");
+ return;
+ }
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_LOOP_INFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&il, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_loop)))
+ return;
+
+ if (oneline) {
+ l_fp temp2ts;
+
+ NTOHL_FP(&il->last_offset, &tempts);
+ NTOHL_FP(&il->drift_comp, &temp2ts);
+
+ (void) fprintf(fp,
+ "offset %s, frequency %s, time_const %ld, watchdog %ld\n",
+ lfptoa(&tempts, 6),
+ lfptoa(&temp2ts, 3),
+ (u_long)ntohl(il->compliance),
+ (u_long)ntohl(il->watchdog_timer));
+ } else {
+ NTOHL_FP(&il->last_offset, &tempts);
+ (void) fprintf(fp, "offset: %s s\n",
+ lfptoa(&tempts, 6));
+ NTOHL_FP(&il->drift_comp, &tempts);
+ (void) fprintf(fp, "frequency: %s ppm\n",
+ lfptoa(&tempts, 3));
+ (void) fprintf(fp, "poll adjust: %ld\n",
+ (u_long)ntohl(il->compliance));
+ (void) fprintf(fp, "watchdog timer: %ld s\n",
+ (u_long)ntohl(il->watchdog_timer));
+ }
+}
+
+
+/*
+ * sysinfo - show current system state
+ */
+/*ARGSUSED*/
+static void
+sysinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_sys *is;
+ int items;
+ int itemsize;
+ int res;
+ l_fp tempts;
+
+ res = doquery(IMPL_XNTPD, REQ_SYS_INFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&is, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_sys)))
+ return;
+
+ (void) fprintf(fp, "system peer: %s\n", nntohost(is->peer));
+ (void) fprintf(fp, "system peer mode: %s\n", modetoa(is->peer_mode));
+ (void) fprintf(fp, "leap indicator: %c%c\n",
+ is->leap & 0x2 ? '1' : '0',
+ is->leap & 0x1 ? '1' : '0');
+ (void) fprintf(fp, "stratum: %d\n", (int)is->stratum);
+ (void) fprintf(fp, "precision: %d\n", (int)is->precision);
+ (void) fprintf(fp, "root distance: %s s\n",
+ fptoa(NTOHS_FP(is->rootdelay), 5));
+ (void) fprintf(fp, "root dispersion: %s s\n",
+ ufptoa(NTOHS_FP(is->rootdispersion), 5));
+ (void) fprintf(fp, "reference ID: [%s]\n",
+ refid_string(is->refid, is->stratum));
+ NTOHL_FP(&is->reftime, &tempts);
+ (void) fprintf(fp, "reference time: %s\n", prettydate(&tempts));
+
+ (void) fprintf(fp, "system flags: ");
+ if ((is->flags & (INFO_FLAG_BCLIENT | INFO_FLAG_AUTHENABLE |
+ INFO_FLAG_PLL | INFO_FLAG_PPS | INFO_FLAG_PLL_SYNC |
+ INFO_FLAG_PPS_SYNC | INFO_FLAG_MONITOR | INFO_FLAG_FILEGEN)) == 0) {
+ (void) fprintf(fp, "none\n");
+ } else {
+ if (is->flags & INFO_FLAG_BCLIENT)
+ (void) fprintf(fp, "bclient ");
+ if (is->flags & INFO_FLAG_AUTHENABLE)
+ (void) fprintf(fp, "auth ");
+ if (is->flags & INFO_FLAG_PLL)
+ (void) fprintf(fp, "pll ");
+ if (is->flags & INFO_FLAG_PPS)
+ (void) fprintf(fp, "pps ");
+ if (is->flags & INFO_FLAG_PLL_SYNC)
+ (void) fprintf(fp, "kernel_sync ");
+ if (is->flags & INFO_FLAG_PPS_SYNC)
+ (void) fprintf(fp, "pps_sync ");
+ if (is->flags & INFO_FLAG_MONITOR)
+ (void) fprintf(fp, "monitor ");
+ if (is->flags & INFO_FLAG_FILEGEN)
+ (void) fprintf(fp, "stats ");
+ (void) fprintf(fp, "\n");
+ }
+ (void) fprintf(fp, "frequency: %s ppm\n",
+ fptoa(ntohl(is->frequency), 3));
+ (void) fprintf(fp, "stability: %s ppm\n",
+ ufptoa(ntohl(is->stability), 3));
+ (void) fprintf(fp, "broadcastdelay: %s s\n",
+ fptoa(NTOHS_FP(is->bdelay), 6));
+ NTOHL_FP(&is->authdelay, &tempts);
+ (void) fprintf(fp, "authdelay: %s s\n", lfptoa(&tempts, 6));
+}
+
+
+/*
+ * sysstats - print system statistics
+ */
+/*ARGSUSED*/
+static void
+sysstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_sys_stats *ss;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_SYS_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ss, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (itemsize != sizeof(struct info_sys_stats) &&
+ itemsize != sizeof(struct old_info_sys_stats)) {
+ /* issue warning according to new structure size */
+ checkitemsize(itemsize, sizeof(struct info_sys_stats));
+ return;
+ }
+
+ (void) fprintf(fp, "system uptime: %ld\n",
+ (u_long)ntohl(ss->timeup));
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ss->timereset));
+ (void) fprintf(fp, "bad stratum in packet: %ld\n",
+ (u_long)ntohl(ss->badstratum));
+ (void) fprintf(fp, "old version packets: %ld\n",
+ (u_long)ntohl(ss->oldversionpkt));
+ (void) fprintf(fp, "new version packets: %ld\n",
+ (u_long)ntohl(ss->newversionpkt));
+ (void) fprintf(fp, "unknown version number: %ld\n",
+ (u_long)ntohl(ss->unknownversion));
+ (void) fprintf(fp, "bad packet length: %ld\n",
+ (u_long)ntohl(ss->badlength));
+ (void) fprintf(fp, "packets processed: %ld\n",
+ (u_long)ntohl(ss->processed));
+ (void) fprintf(fp, "bad authentication: %ld\n",
+ (u_long)ntohl(ss->badauth));
+ if (itemsize != sizeof(struct info_sys_stats))
+ return;
+
+ (void) fprintf(fp, "limitation rejects: %ld\n",
+ (u_long)ntohl(ss->limitrejected));
+}
+
+
+
+/*
+ * iostats - print I/O statistics
+ */
+/*ARGSUSED*/
+static void
+iostats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_io_stats *io;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_IO_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&io, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_io_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(io->timereset));
+ (void) fprintf(fp, "receive buffers: %d\n",
+ ntohs(io->totalrecvbufs));
+ (void) fprintf(fp, "free receive buffers: %d\n",
+ ntohs(io->freerecvbufs));
+ (void) fprintf(fp, "used receive buffers: %d\n",
+ ntohs(io->fullrecvbufs));
+ (void) fprintf(fp, "low water refills: %d\n",
+ ntohs(io->lowwater));
+ (void) fprintf(fp, "dropped packets: %ld\n",
+ (u_long)ntohl(io->dropped));
+ (void) fprintf(fp, "ignored packets: %ld\n",
+ (u_long)ntohl(io->ignored));
+ (void) fprintf(fp, "received packets: %ld\n",
+ (u_long)ntohl(io->received));
+ (void) fprintf(fp, "packets sent: %ld\n",
+ (u_long)ntohl(io->sent));
+ (void) fprintf(fp, "packets not sent: %ld\n",
+ (u_long)ntohl(io->notsent));
+ (void) fprintf(fp, "interrupts handled: %ld\n",
+ (u_long)ntohl(io->interrupts));
+ (void) fprintf(fp, "received by int: %ld\n",
+ (u_long)ntohl(io->int_received));
+}
+
+
+/*
+ * memstats - print peer memory statistics
+ */
+/*ARGSUSED*/
+static void
+memstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_mem_stats *mem;
+ int i;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_MEM_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&mem, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_mem_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(mem->timereset));
+ (void) fprintf(fp, "total peer memory: %d\n",
+ ntohs(mem->totalpeermem));
+ (void) fprintf(fp, "free peer memory: %d\n",
+ ntohs(mem->freepeermem));
+ (void) fprintf(fp, "calls to findpeer: %ld\n",
+ (u_long)ntohl(mem->findpeer_calls));
+ (void) fprintf(fp, "new peer allocations: %ld\n",
+ (u_long)ntohl(mem->allocations));
+ (void) fprintf(fp, "peer demobilizations: %ld\n",
+ (u_long)ntohl(mem->demobilizations));
+
+ (void) fprintf(fp, "hash table counts: ");
+ for (i = 0; i < HASH_SIZE; i++) {
+ (void) fprintf(fp, "%4d", (int)mem->hashcount[i]);
+ if ((i % 8) == 7 && i != (HASH_SIZE-1)) {
+ (void) fprintf(fp, "\n ");
+ }
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+
+/*
+ * timerstats - print timer statistics
+ */
+/*ARGSUSED*/
+static void
+timerstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_timer_stats *tim;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_TIMER_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&tim, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_timer_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(tim->timereset));
+ (void) fprintf(fp, "alarms handled: %ld\n",
+ (u_long)ntohl(tim->alarms));
+ (void) fprintf(fp, "alarm overruns: %ld\n",
+ (u_long)ntohl(tim->overflows));
+ (void) fprintf(fp, "calls to transmit: %ld\n",
+ (u_long)ntohl(tim->xmtcalls));
+}
+
+
+/*
+ * addpeer - configure an active mode association
+ */
+static void
+addpeer(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_ACTIVE);
+}
+
+
+/*
+ * addserver - configure a client mode association
+ */
+static void
+addserver(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_CLIENT);
+}
+
+/*
+ * broadcast - configure a broadcast mode association
+ */
+static void
+broadcast(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_BROADCAST);
+}
+
+
+/*
+ * config - configure a new peer association
+ */
+static void
+doconfig(pcmd, fp, mode)
+ struct parse *pcmd;
+ FILE *fp;
+ int mode;
+{
+ struct conf_peer cpeer;
+ int items;
+ int itemsize;
+ char *dummy;
+ u_long keyid;
+ u_int version;
+ u_int flags;
+ int res;
+
+ keyid = 0;
+ version = NTP_VERSION;
+ flags = 0;
+ res = 0;
+ if (pcmd->nargs > 1) {
+ keyid = pcmd->argval[1].uval;
+ if (keyid > 0) {
+ flags |= CONF_FLAG_AUTHENABLE;
+ }
+ if (pcmd->nargs > 2) {
+ version = (u_int)pcmd->argval[2].uval;
+ if (version > NTP_VERSION
+ || version < NTP_OLDVERSION) {
+ (void) fprintf(fp,
+ "funny version number %u specified\n",
+ version);
+ res++;
+ }
+
+ items = 3;
+ while (pcmd->nargs > items) {
+ if (STREQ(pcmd->argval[items].string,
+ "prefer"))
+ flags |= CONF_FLAG_PREFER;
+ else {
+ (void) fprintf(fp,
+ "%s not understood\n",
+ pcmd->argval[3].string);
+ res++;
+ break;
+ }
+ items++;
+ }
+ }
+ }
+
+ if (res)
+ return;
+
+ cpeer.peeraddr = pcmd->argval[0].netnum;
+ cpeer.hmode = (u_char) mode;
+ cpeer.keyid = keyid;
+ cpeer.version = (u_char) version;
+ cpeer.minpoll = NTP_MINDPOLL;
+ cpeer.maxpoll = NTP_MAXPOLL;
+ cpeer.flags = (u_char)flags;
+
+ res = doquery(IMPL_XNTPD, REQ_CONFIG, 1, 1,
+ sizeof(struct conf_peer), (char *)&cpeer, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * unconfig - unconfigure some associations
+ */
+static void
+unconfig(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_unpeer plist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++) {
+ plist[qitems].peeraddr = pcmd->argval[qitems].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_UNCONFIG, 1, qitems,
+ sizeof(struct conf_unpeer), (char *)plist, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * set - set some system flags
+ */
+static void
+set(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doset(pcmd, fp, REQ_SET_SYS_FLAG);
+}
+
+
+/*
+ * clear - clear some system flags
+ */
+static void
+sys_clear(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doset(pcmd, fp, REQ_CLR_SYS_FLAG);
+}
+
+
+/*
+ * doset - set/clear system flags
+ */
+static void
+doset(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_sys_flags sys;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ sys.flags = 0;
+ res = 0;
+ for (items = 0; items < pcmd->nargs; items++) {
+ if (STREQ(pcmd->argval[items].string, "bclient"))
+ sys.flags |= SYS_FLAG_BCLIENT;
+ else if (STREQ(pcmd->argval[items].string, "auth"))
+ sys.flags |= SYS_FLAG_AUTHENTICATE;
+ else if (STREQ(pcmd->argval[items].string, "pll"))
+ sys.flags |= SYS_FLAG_PLL;
+ else if (STREQ(pcmd->argval[items].string, "pps"))
+ sys.flags |= SYS_FLAG_PPS;
+ else if (STREQ(pcmd->argval[items].string, "monitor"))
+ sys.flags |= SYS_FLAG_MONITOR;
+ else if (STREQ(pcmd->argval[items].string, "stats"))
+ sys.flags |= SYS_FLAG_FILEGEN;
+ else {
+ (void) fprintf(fp, "Unknown flag %s\n",
+ pcmd->argval[items].string);
+ res = 1;
+ }
+ }
+
+ if (res || sys.flags == 0)
+ return;
+
+ res = doquery(IMPL_XNTPD, req, 1, 1,
+ sizeof(struct conf_sys_flags), (char *)&sys, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * data for printing/interrpreting the restrict flags
+ */
+struct resflags {
+ char *str;
+ int bit;
+};
+
+static struct resflags resflags[] = {
+ { "ignore", RES_IGNORE },
+ { "noserve", RES_DONTSERVE },
+ { "notrust", RES_DONTTRUST },
+ { "noquery", RES_NOQUERY },
+ { "nomodify", RES_NOMODIFY },
+ { "nopeer", RES_NOPEER },
+ { "notrap", RES_NOTRAP },
+ { "lptrap", RES_LPTRAP },
+ { "limited", RES_LIMITED },
+ { "", 0 }
+};
+
+static struct resflags resmflags[] = {
+ { "ntpport", RESM_NTPONLY },
+ { "interface", RESM_INTERFACE },
+ { "", 0 }
+};
+
+
+/*
+ * reslist - obtain and print the server's restrict list
+ */
+/*ARGSUSED*/
+static void
+reslist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_restrict *rl;
+ int items;
+ int itemsize;
+ int res;
+ char *addr;
+ char *mask;
+ struct resflags *rf;
+ u_long count;
+ u_short flags;
+ u_short mflags;
+ char flagstr[300];
+ static char *comma = ", ";
+
+ res = doquery(IMPL_XNTPD, REQ_GET_RESTRICT, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&rl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_restrict)))
+ return;
+
+ (void) fprintf(fp,
+ " address mask count flags\n");
+ (void) fprintf(fp,
+ "=====================================================================\n");
+ while (items > 0) {
+ if ((rl->mask == (U_LONG)0xffffffff))
+ addr = numtohost(rl->addr);
+ else
+ addr = numtoa( rl->addr );
+ mask = numtoa(rl->mask);
+ count = ntohl(rl->count);
+ flags = ntohs(rl->flags);
+ mflags = ntohs(rl->mflags);
+ flagstr[0] = '\0';
+
+ res = 1;
+ rf = &resmflags[0];
+ while (rf->bit != 0) {
+ if (mflags & rf->bit) {
+ if (!res)
+ (void) strcat(flagstr, comma);
+ res = 0;
+ (void) strcat(flagstr, rf->str);
+ }
+ rf++;
+ }
+
+ rf = &resflags[0];
+ while (rf->bit != 0) {
+ if (flags & rf->bit) {
+ if (!res)
+ (void) strcat(flagstr, comma);
+ res = 0;
+ (void) strcat(flagstr, rf->str);
+ }
+ rf++;
+ }
+
+ if (flagstr[0] == '\0')
+ (void) strcpy(flagstr, "none");
+
+ (void) fprintf(fp, "%-15.15s %-15.15s %9ld %s\n",
+ addr, mask, count, flagstr);
+ rl++;
+ items--;
+ }
+}
+
+
+
+/*
+ * restrict - create/add a set of restrictions
+ */
+static void
+restrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_RESADDFLAGS);
+}
+
+
+/*
+ * unrestrict - remove restriction flags from existing entry
+ */
+static void
+unrestrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_RESSUBFLAGS);
+}
+
+
+/*
+ * delrestrict - delete an existing restriction
+ */
+static void
+delrestrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_UNRESTRICT);
+}
+
+
+/*
+ * do_restrict - decode commandline restrictions and make the request
+ */
+static void
+do_restrict(pcmd, fp, req_code)
+ struct parse *pcmd;
+ FILE *fp;
+ int req_code;
+{
+ struct conf_restrict cres;
+ int items;
+ int itemsize;
+ char *dummy;
+ u_long num;
+ u_long bit;
+ int i;
+ int res;
+ int err;
+
+ cres.addr = pcmd->argval[0].netnum;
+ cres.mask = pcmd->argval[1].netnum;
+ cres.flags = 0;
+ cres.mflags = 0;
+ err = 0;
+ for (res = 2; res < pcmd->nargs; res++) {
+ if (STREQ(pcmd->argval[res].string, "ntpport")) {
+ cres.mflags |= RESM_NTPONLY;
+ } else {
+ for (i = 0; resflags[i].bit != 0; i++) {
+ if (STREQ(pcmd->argval[res].string,
+ resflags[i].str))
+ break;
+ }
+ if (resflags[i].bit != 0) {
+ cres.flags |= resflags[i].bit;
+ if (req_code == REQ_UNRESTRICT) {
+ (void) fprintf(fp,
+ "Flag %s inappropriate\n",
+ resflags[i].str);
+ err++;
+ }
+ } else {
+ (void) fprintf(fp, "Unknown flag %s\n",
+ pcmd->argval[res].string);
+ err++;
+ }
+ }
+ }
+
+ /*
+ * Make sure mask for default address is zero. Otherwise,
+ * make sure mask bits are contiguous.
+ */
+ if (cres.addr == 0) {
+ cres.mask = 0;
+ } else {
+ num = ntohl(cres.mask);
+ for (bit = 0x80000000; bit != 0; bit >>= 1)
+ if ((num & bit) == 0)
+ break;
+ for ( ; bit != 0; bit >>= 1)
+ if ((num & bit) != 0)
+ break;
+ if (bit != 0) {
+ (void) fprintf(fp, "Invalid mask %s\n",
+ numtoa(cres.mask));
+ err++;
+ }
+ }
+
+ if (err)
+ return;
+
+ res = doquery(IMPL_XNTPD, req_code, 1, 1,
+ sizeof(struct conf_restrict), (char *)&cres, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * monlist - obtain and print the server's monitor data
+ */
+/*ARGSUSED*/
+static void
+monlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *struct_star;
+ struct in_addr addr;
+ int items;
+ int itemsize;
+ int res;
+ int version = -1;
+
+ if (pcmd->nargs > 0) {
+ version = pcmd->argval[0].ival;
+ }
+
+ res = doquery(IMPL_XNTPD,
+ (version == 1 || version == -1) ? REQ_MON_GETLIST_1 :
+ REQ_MON_GETLIST, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, &struct_star,
+ (version < 0) ? (1 << INFO_ERR_REQ) : 0);
+
+ if (res == INFO_ERR_REQ && version < 0)
+ res = doquery(IMPL_XNTPD, REQ_MON_GETLIST, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, &struct_star, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (itemsize == sizeof(struct info_monitor_1)) {
+ struct info_monitor_1 *ml = (struct info_monitor_1 *) struct_star;
+
+ (void) fprintf(fp,
+ "remote address port local address count m ver drop last first\n");
+ (void) fprintf(fp,
+ "===============================================================================\n");
+ while (items > 0) {
+ addr.s_addr = ml->daddr;
+ (void) fprintf(fp,
+ "%-22.22s %5d %-15s %8ld %1d %1d %6lu %6lu %7lu\n",
+ nntohost(ml->addr),
+ ntohs(ml->port),
+ inet_ntoa(addr),
+ (u_long)ntohl(ml->count),
+ ml->mode,
+ ml->version,
+ (u_long)ntohl(ml->lastdrop),
+ (u_long)ntohl(ml->lasttime),
+ (u_long)ntohl(ml->firsttime));
+ ml++;
+ items--;
+ }
+ } else if (itemsize == sizeof(struct info_monitor)) {
+ struct info_monitor *ml = (struct info_monitor *) struct_star;
+
+ (void) fprintf(fp,
+ " address port count mode ver lastdrop lasttime firsttime\n");
+ (void) fprintf(fp,
+ "===============================================================================\n");
+ while (items > 0) {
+ addr.s_addr = ml->lastdrop;
+ (void) fprintf(fp,
+ "%-25.25s %5d %9ld %4d %2d %9lu %9lu %9lu\n",
+ nntohost(ml->addr),
+ ntohs(ml->port),
+ (u_long)ntohl(ml->count),
+ ml->mode,
+ ml->version,
+ (u_long)ntohl(ml->lastdrop),
+ (u_long)ntohl(ml->lasttime),
+ (u_long)ntohl(ml->firsttime));
+ ml++;
+ items--;
+ }
+ } else if (itemsize == sizeof(struct old_info_monitor)) {
+ struct old_info_monitor *oml = (struct old_info_monitor *)struct_star;
+ (void) fprintf(fp,
+ " address port count mode version lasttime firsttime\n");
+ (void) fprintf(fp,
+ "======================================================================\n");
+ while (items > 0) {
+ (void) fprintf(fp, "%-20.20s %5d %9ld %4d %3d %9lu %9lu\n",
+ nntohost(oml->addr),
+ ntohs(oml->port),
+ (u_long)ntohl(oml->count),
+ oml->mode,
+ oml->version,
+ (u_long)ntohl(oml->lasttime),
+ (u_long)ntohl(oml->firsttime));
+ oml++;
+ items--;
+ }
+ } else {
+ /* issue warning according to new info_monitor size */
+ checkitemsize(itemsize, sizeof(struct info_monitor));
+ }
+}
+
+
+/*
+ * monitor - turn the server's monitor facility on or off
+ */
+static void
+monitor(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int items;
+ int itemsize;
+ char *dummy;
+ int req_code;
+ int res;
+
+ if (STREQ(pcmd->argval[0].string, "on"))
+ req_code = REQ_MONITOR;
+ else if (STREQ(pcmd->argval[0].string, "off"))
+ req_code = REQ_NOMONITOR;
+ else {
+ (void) fprintf(fp, "monitor what?\n");
+ return;
+ }
+
+ res = doquery(IMPL_XNTPD, req_code, 1, 0, 0, (char *)0,
+ &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * Mapping between command line strings and stat reset flags
+ */
+struct statreset {
+ char *str;
+ int flag;
+} sreset[] = {
+ { "io", RESET_FLAG_IO },
+ { "sys", RESET_FLAG_SYS },
+ { "mem", RESET_FLAG_MEM },
+ { "timer", RESET_FLAG_TIMER },
+ { "auth", RESET_FLAG_AUTH },
+ { "allpeers", RESET_FLAG_ALLPEERS },
+ { "", 0 }
+};
+
+/*
+ * reset - reset statistic counters
+ */
+static void
+reset(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct reset_flags rflags;
+ int items;
+ int itemsize;
+ char *dummy;
+ int i;
+ int res;
+ int err;
+
+ err = 0;
+ rflags.flags = 0;
+ for (res = 0; res < pcmd->nargs; res++) {
+ for (i = 0; sreset[i].flag != 0; i++) {
+ if (STREQ(pcmd->argval[res].string, sreset[i].str))
+ break;
+ }
+ if (sreset[i].flag == 0) {
+ (void) fprintf(fp, "Flag %s unknown\n",
+ pcmd->argval[res].string);
+ err++;
+ } else {
+ rflags.flags |= sreset[i].flag;
+ }
+ }
+
+ if (err) {
+ (void) fprintf(fp, "Not done due to errors\n");
+ return;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_RESET_STATS, 1, 1,
+ sizeof(struct reset_flags), (char *)&rflags, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * preset - reset stat counters for particular peers
+ */
+static void
+preset(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_unpeer plist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++) {
+ plist[qitems].peeraddr = pcmd->argval[qitems].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_RESET_PEER, 1, qitems,
+ sizeof(struct conf_unpeer), (char *)plist, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * readkeys - request the server to reread the keys file
+ */
+/*ARGSUSED*/
+static void
+readkeys(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_REREAD_KEYS, 1, 0, 0, (char *)0,
+ &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * trustkey - add some keys to the trusted key list
+ */
+static void
+trustkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_trustkey(pcmd, fp, REQ_TRUSTKEY);
+}
+
+
+/*
+ * untrustkey - remove some keys from the trusted key list
+ */
+static void
+untrustkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_trustkey(pcmd, fp, REQ_UNTRUSTKEY);
+}
+
+
+/*
+ * do_trustkey - do grunge work of adding/deleting keys
+ */
+static void
+do_trustkey(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ u_long keyids[MAXARGS];
+ int i;
+ int items;
+ int itemsize;
+ char *dummy;
+ int ritems;
+ int res;
+
+ ritems = 0;
+ for (i = 0; i < pcmd->nargs; i++) {
+ keyids[ritems++] = pcmd->argval[i].uval;
+ }
+
+ res = doquery(IMPL_XNTPD, req, 1, ritems, sizeof(u_long),
+ (char *)keyids, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * authinfo - obtain and print info about authentication
+ */
+/*ARGSUSED*/
+static void
+authinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_auth *ia;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_AUTHINFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ia, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_auth)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ia->timereset));
+ (void) fprintf(fp, "key lookups: %ld\n",
+ (u_long)ntohl(ia->keylookups));
+ (void) fprintf(fp, "keys not found: %ld\n",
+ (u_long)ntohl(ia->keynotfound));
+ (void) fprintf(fp, "uncached keys: %ld\n",
+ (u_long)ntohl(ia->keyuncached));
+ (void) fprintf(fp, "encryptions: %ld\n",
+ (u_long)ntohl(ia->encryptions));
+ (void) fprintf(fp, "decryptions: %ld\n",
+ (u_long)ntohl(ia->decryptions));
+}
+
+
+
+/*
+ * traps - obtain and print a list of traps
+ */
+/*ARGSUSED*/
+static void
+traps(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ struct info_trap *it;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_TRAPS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&it, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_trap)))
+ return;
+
+ for (i = 0; i < items; i++ ) {
+ if (i != 0)
+ (void) fprintf(fp, "\n");
+ (void) fprintf(fp, "address %s, port %d\n",
+ numtoa(it->trap_address), ntohs(it->trap_port));
+ (void) fprintf(fp, "interface: %s, ",
+ it->local_address==0?"wildcard":numtoa(it->local_address));
+
+ if (htonl(it->flags) & TRAP_CONFIGURED)
+ (void) fprintf(fp, "configured\n");
+ else if (it->flags & TRAP_NONPRIO)
+ (void) fprintf(fp, "low priority\n");
+ else
+ (void) fprintf(fp, "normal priority\n");
+
+ (void) fprintf(fp, "set for %ld secs, last set %ld secs ago\n",
+ (long)it->origtime, (long)it->settime);
+ (void) fprintf(fp, "sequence %d, number of resets %ld\n",
+ it->sequence, (long)it->resets);
+ }
+}
+
+
+/*
+ * addtrap - configure a trap
+ */
+static void
+addtrap(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_addclr_trap(pcmd, fp, REQ_ADD_TRAP);
+}
+
+
+/*
+ * clrtrap - clear a trap from the server
+ */
+static void
+clrtrap(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_addclr_trap(pcmd, fp, REQ_CLR_TRAP);
+}
+
+
+/*
+ * do_addclr_trap - do grunge work of adding/deleting traps
+ */
+static void
+do_addclr_trap(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ struct conf_trap ctrap;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ ctrap.trap_address = pcmd->argval[0].netnum;
+ ctrap.local_address = 0;
+ ctrap.trap_port = htons(TRAPPORT);
+ ctrap.unused = 0;
+
+ if (pcmd->nargs > 1) {
+ ctrap.trap_port
+ = htons((u_short)(pcmd->argval[1].uval & 0xffff));
+ if (pcmd->nargs > 2)
+ ctrap.local_address = pcmd->argval[2].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, req, 1, 1, sizeof(struct conf_trap),
+ (char *)&ctrap, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * requestkey - change the server's request key (a dangerous request)
+ */
+static void
+requestkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_changekey(pcmd, fp, REQ_REQUEST_KEY);
+}
+
+
+/*
+ * controlkey - change the server's control key
+ */
+static void
+controlkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_changekey(pcmd, fp, REQ_CONTROL_KEY);
+}
+
+
+
+/*
+ * do_changekey - do grunge work of changing keys
+ */
+static void
+do_changekey(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ u_long key;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+
+ key = htonl(pcmd->argval[0].uval);
+
+ res = doquery(IMPL_XNTPD, req, 1, 1, sizeof(u_long),
+ (char *)&key, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * ctlstats - obtain and print info about authentication
+ */
+/*ARGSUSED*/
+static void
+ctlstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_control *ic;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CTLSTATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ic, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_control)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ic->ctltimereset));
+ (void) fprintf(fp, "requests received: %ld\n",
+ (u_long)ntohl(ic->numctlreq));
+ (void) fprintf(fp, "responses sent: %ld\n",
+ (u_long)ntohl(ic->numctlresponses));
+ (void) fprintf(fp, "fragments sent: %ld\n",
+ (u_long)ntohl(ic->numctlfrags));
+ (void) fprintf(fp, "async messages sent: %ld\n",
+ (u_long)ntohl(ic->numasyncmsgs));
+ (void) fprintf(fp, "error msgs sent: %ld\n",
+ (u_long)ntohl(ic->numctlerrors));
+ (void) fprintf(fp, "total bad pkts: %ld\n",
+ (u_long)ntohl(ic->numctlbadpkts));
+ (void) fprintf(fp, "packet too short: %ld\n",
+ (u_long)ntohl(ic->numctltooshort));
+ (void) fprintf(fp, "response on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputresp));
+ (void) fprintf(fp, "fragment on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputfrag));
+ (void) fprintf(fp, "error set on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputerr));
+ (void) fprintf(fp, "bad offset on input: %ld\n",
+ (u_long)ntohl(ic->numctlbadoffset));
+ (void) fprintf(fp, "bad version packets: %ld\n",
+ (u_long)ntohl(ic->numctlbadversion));
+ (void) fprintf(fp, "data in pkt too short: %ld\n",
+ (u_long)ntohl(ic->numctldatatooshort));
+ (void) fprintf(fp, "unknown op codes: %ld\n",
+ (u_long)ntohl(ic->numctlbadop));
+}
+
+
+
+/*
+ * Table for human printing leap bits
+ */
+char *leapbittab[] = {
+ "00 (no leap second scheduled)",
+ "01 (second to be added at end of month)",
+ "10 (second to be deleted at end of month)",
+ "11 (clock out of sync)"
+};
+
+char *controlleapbittab[] = {
+ "00 (leap controlled by lower stratum)",
+ "01 (second to be added at end of month)",
+ "10 (second to be deleted at end of month)",
+ "11 (lower stratum leap information ignored - no leap)"
+};
+
+/*
+ * leapinfo - obtain information about the state of the leap second support
+ */
+/*ARGSUSED*/
+static void
+leapinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_leap *il;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_LEAPINFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&il, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_leap)))
+ return;
+
+ (void) fprintf(fp, "sys.leap: %s\n",
+ leapbittab[il->sys_leap & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.indicator: %s\n",
+ controlleapbittab[il->leap_indicator & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.warning: %s\n",
+ controlleapbittab[il->leap_warning & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.bits: %s\n",
+ leapbittab[il->leap_bits & INFO_LEAP_MASK]);
+ if (il->leap_bits & INFO_LEAP_OVERRIDE)
+ (void) fprintf(fp, "Leap overide option in effect\n");
+ if (il->leap_bits & INFO_LEAP_SEENSTRATUM1)
+ (void) fprintf(fp, "Stratum 1 restrictions in effect\n");
+ (void) fprintf(fp, "time to next leap interrupt: %ld s\n",
+ (u_long)ntohl(il->leap_timer));
+ gettstamp(&ts);
+ (void) fprintf(fp, "date of next leap interrupt: %s\n",
+ humandate(ts.l_ui + ntohl(il->leap_timer)));
+ (void) fprintf(fp, "calls to leap process: %lu\n",
+ (u_long)ntohl(il->leap_processcalls));
+ (void) fprintf(fp, "leap more than month away: %lu\n",
+ (u_long)ntohl(il->leap_notclose));
+ (void) fprintf(fp, "leap less than month away: %lu\n",
+ (u_long)ntohl(il->leap_monthofleap));
+ (void) fprintf(fp, "leap less than day away: %lu\n",
+ (u_long)ntohl(il->leap_dayofleap));
+ (void) fprintf(fp, "leap in less than 2 hours: %lu\n",
+ (u_long)ntohl(il->leap_hoursfromleap));
+ (void) fprintf(fp, "leap happened: %lu\n",
+ (u_long)ntohl(il->leap_happened));
+}
+
+
+/*
+ * clockstat - get and print clock status information
+ */
+static void
+clockstat(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern struct clktype clktypes[];
+ struct info_clock *cl;
+ /* 8 is the maximum number of clocks which will fit in a packet */
+ u_long clist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+ struct clktype *clk;
+ u_long ltemp;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++)
+ clist[qitems] = pcmd->argval[qitems].netnum;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CLOCKINFO, 0, qitems,
+ sizeof(U_LONG), (char *)clist, &items,
+ &itemsize, (char **)&cl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_clock)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "clock address: %s\n",
+ numtoa(cl->clockadr));
+ for (clk = clktypes; clk->code >= 0; clk++)
+ if (clk->code == cl->type)
+ break;
+ if (clk->code >= 0)
+ (void) fprintf(fp, "clock type: %s\n",
+ clk->clocktype);
+ else
+ (void) fprintf(fp, "clock type: unknown type (%d)\n",
+ cl->type);
+ (void) fprintf(fp, "last event: %d\n",
+ cl->lastevent);
+ (void) fprintf(fp, "current status: %d\n",
+ cl->currentstatus);
+ (void) fprintf(fp, "number of polls: %lu\n",
+ (u_long)ntohl(cl->polls));
+ (void) fprintf(fp, "no response to poll: %lu\n",
+ (u_long)ntohl(cl->noresponse));
+ (void) fprintf(fp, "bad format responses: %lu\n",
+ (u_long)ntohl(cl->badformat));
+ (void) fprintf(fp, "bad data responses: %lu\n",
+ (u_long)ntohl(cl->baddata));
+ (void) fprintf(fp, "running time: %lu\n",
+ (u_long)ntohl(cl->timestarted));
+ NTOHL_FP(&cl->fudgetime1, &ts);
+ (void) fprintf(fp, "fudge time 1: %s\n",
+ lfptoa(&ts, 6));
+ NTOHL_FP(&cl->fudgetime2, &ts);
+ (void) fprintf(fp, "fudge time 2: %s\n",
+ lfptoa(&ts, 6));
+ (void) fprintf(fp, "stratum: %ld\n",
+ (u_long)ntohl(cl->fudgeval1));
+ ltemp = ntohl(cl->fudgeval2);
+ (void) fprintf(fp, "reference ID: %s\n",
+ (char *)&ltemp);
+ (void) fprintf(fp, "fudge flags: 0x%x\n",
+ cl->flags);
+
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ cl++;
+ }
+}
+
+
+/*
+ * fudge - set clock fudge factors
+ */
+static void
+fudge(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct conf_fudge fudgedata;
+ int items;
+ int itemsize;
+ char *dummy;
+ l_fp ts;
+ int res;
+ long val;
+ int err;
+
+
+ err = 0;
+ memset((char *)&fudgedata, 0, sizeof fudgedata);
+ fudgedata.clockadr = pcmd->argval[0].netnum;
+
+ if (STREQ(pcmd->argval[1].string, "time1")) {
+ fudgedata.which = htonl(FUDGE_TIME1);
+ if (!atolfp(pcmd->argval[2].string, &ts))
+ err = 1;
+ else
+ NTOHL_FP(&ts, &fudgedata.fudgetime);
+ } else if (STREQ(pcmd->argval[1].string, "time2")) {
+ fudgedata.which = htonl(FUDGE_TIME2);
+ if (!atolfp(pcmd->argval[2].string, &ts))
+ err = 1;
+ else
+ NTOHL_FP(&ts, &fudgedata.fudgetime);
+ } else if (STREQ(pcmd->argval[1].string, "val1")) {
+ fudgedata.which = htonl(FUDGE_VAL1);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val);
+ } else if (STREQ(pcmd->argval[1].string, "val2")) {
+ fudgedata.which = htonl(FUDGE_VAL2);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val);
+ } else if (STREQ(pcmd->argval[1].string, "flags")) {
+ fudgedata.which = htonl(FUDGE_FLAGS);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val & 0xf);
+ } else {
+ (void) fprintf(stderr, "What fudge is %s?\n",
+ pcmd->argval[1].string);
+ return;
+ }
+
+ if (err) {
+ (void) fprintf(stderr, "Unknown fudge parameter %s\n",
+ pcmd->argval[2].string);
+ return;
+ }
+
+
+ res = doquery(IMPL_XNTPD, REQ_SET_CLKFUDGE, 1, 1,
+ sizeof(struct conf_fudge), (char *)&fudgedata, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+/*
+ * clkbug - get and print clock debugging information
+ */
+static void
+clkbug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ register int i;
+ register int n;
+ register u_long s;
+ struct info_clkbug *cl;
+ /* 8 is the maximum number of clocks which will fit in a packet */
+ u_long clist[min(MAXARGS, 8)];
+ u_long ltemp;
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++)
+ clist[qitems] = pcmd->argval[qitems].netnum;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CLKBUGINFO, 0, qitems,
+ sizeof(U_LONG), (char *)clist, &items,
+ &itemsize, (char **)&cl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_clkbug)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "clock address: %s\n",
+ numtoa(cl->clockadr));
+ n = (int)cl->nvalues;
+ (void) fprintf(fp, "values: %d", n);
+ s = ntohs(cl->svalues);
+ if (n > NUMCBUGVALUES)
+ n = NUMCBUGVALUES;
+ for (i = 0; i < n; i++) {
+ ltemp = (u_long)ntohl(cl->values[i]);
+ ltemp &= 0xffffffff;
+ if ((i & 0x3) == 0)
+ (void) fprintf(fp, "\n");
+ if (s & (1 << i))
+ (void) fprintf(fp, "%12ld", ltemp);
+ else
+ (void) fprintf(fp, "%12lu", ltemp);
+ }
+ (void) fprintf(fp, "\n");
+
+ n = (int)cl->ntimes;
+ (void) fprintf(fp, "times: %d", n);
+ s = ntohl(cl->stimes);
+ if (n > NUMCBUGTIMES)
+ n = NUMCBUGTIMES;
+ for (i = 0; i < n; i++) {
+ int needsp = 0;
+ if ((i & 0x1) == 0)
+ (void) fprintf(fp, "\n");
+ else {
+ for (;needsp > 0; needsp--)
+ putc(' ', fp);
+ }
+ NTOHL_FP(&cl->times[i], &ts);
+ if (s & (1 << i)) {
+ (void) fprintf(fp, "%17s",
+ lfptoa(&ts, 6));
+ needsp = 22;
+ } else {
+ (void) fprintf(fp, "%37s",
+ uglydate(&ts));
+ needsp = 2;
+ }
+ }
+ (void) fprintf(fp, "\n");
+ if (items > 0) {
+ cl++;
+ (void) fprintf(fp, "\n");
+ }
+ }
+}
+
+
+/*
+ * setprecision - set the server's value of sys.precision
+ */
+static void
+setprecision(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ long precision;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ precision = htonl(pcmd->argval[0].ival);
+
+ res = doquery(IMPL_XNTPD, REQ_SET_PRECISION, 1, 1, sizeof(long),
+ (char *)&precision, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * kerninfo - display the kernel pll/pps variables
+ */
+static void
+kerninfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_kernel *ik;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_KERNEL, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ik, 0);
+ if (res != 0 && items == 0)
+ return;
+ if (!check1item(items, fp))
+ return;
+ if (!checkitemsize(itemsize, sizeof(struct info_kernel)))
+ return;
+
+ /*
+ * pll variables
+ */
+ (void)fprintf(fp, "pll offset: %ld us\n",
+ (u_long)ntohl(ik->offset));
+ (void)fprintf(fp, "pll frequency: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->freq), 3));
+ (void)fprintf(fp, "maximum error: %ld us\n",
+ (u_long)ntohl(ik->maxerror));
+ (void)fprintf(fp, "estimated error: %ld us\n",
+ (u_long)ntohl(ik->esterror));
+ (void)fprintf(fp, "status: %04x\n",
+ ntohs(ik->status & 0xffff));
+ (void)fprintf(fp, "pll time constant: %ld\n",
+ (u_long)ntohl(ik->constant));
+ (void)fprintf(fp, "precision: %ld us\n",
+ (u_long)ntohl(ik->precision));
+ (void)fprintf(fp, "frequency tolerance: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->tolerance), 0));
+
+ /*
+ * For backwards compatibility (ugh), we find the pps variables
+ * only if the shift member is nonzero.
+ */
+ if (!ik->shift)
+ return;
+
+ /*
+ * pps variables
+ */
+ (void)fprintf(fp, "pps frequency: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->ppsfreq), 3));
+ (void)fprintf(fp, "pps stability: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->stabil), 3));
+ (void)fprintf(fp, "pps jitter: %ld us\n",
+ (u_long)ntohl(ik->jitter));
+ (void)fprintf(fp, "calibration interval: %d s\n",
+ 1 << ntohs(ik->shift));
+ (void)fprintf(fp, "calibration cycles: %ld\n",
+ (u_long)ntohl(ik->calcnt));
+ (void)fprintf(fp, "jitter exceeded: %ld\n",
+ (u_long)ntohl(ik->jitcnt));
+ (void)fprintf(fp, "stability exceeded: %ld\n",
+ (u_long)ntohl(ik->stbcnt));
+ (void)fprintf(fp, "calibration errors: %ld\n",
+ (u_long)ntohl(ik->errcnt));
+}
diff --git a/usr.sbin/xten/Makefile b/usr.sbin/xten/Makefile
new file mode 100644
index 0000000..02d408d
--- /dev/null
+++ b/usr.sbin/xten/Makefile
@@ -0,0 +1,7 @@
+# Makefile for xten (Stark) 10/30/93
+# $Id$
+
+PROG= xten
+CFLAGS+= -I${.CURDIR}/../../libexec/xtend
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xten/README b/usr.sbin/xten/README
new file mode 100644
index 0000000..1ea5ddd
--- /dev/null
+++ b/usr.sbin/xten/README
@@ -0,0 +1,367 @@
+Installation Notes for X-10 software
+Eugene W. Stark (stark@cs.sunysb.edu)
+October 30, 1993
+(latest update May 29, 1997)
+
+The TW523 is a carrier-current modem for home control/automation purposes.
+It is made by:
+
+ X-10 Inc.
+ 185A LeGrand Ave.
+ Northvale, NJ 07647
+ USA
+ (201) 784-9700 or 1-800-526-0027
+
+ X-10 Home Controls Inc.
+ 1200 Aerowood Drive, Unit 20
+ Mississauga, Ontario
+ (416) 624-4446 or 1-800-387-3346
+
+The TW523 is designed for communications using the X-10 protocol,
+which is compatible with a number of home control systems, including
+Radio Shack "Plug 'n Power(tm)" and Stanley "Lightmaker(tm)."
+I bought my TW523 from:
+
+ Home Control Concepts
+ 9353-C Activity Road
+ San Diego, CA 92126
+ (619) 693-8887
+
+They supplied me with the TW523 (which has an RJ-11 four-wire modular
+telephone connector), a modular cable, an RJ-11 to DB-25 connector with
+internal wiring, documentation from X-10 on the TW523 (very good),
+an instruction manual by Home Control Concepts (not very informative),
+and a floppy disk containing binary object code of some demonstration/test
+programs and of a C function library suitable for controlling the TW523
+by an IBM PC under MS-DOS (not useful to me other than to verify that
+the unit worked). I suggest saving money and buying the bare TW523
+rather than the TW523 development kit (what I bought), because if you
+are running FreeBSD you don't really care about the DOS binaries.
+For details on the X-10 protocol itself, refer to the documentation from
+X-10 Inc.
+
+The interface to the TW-523 consists of four wires on the RJ-11 connector,
+which are jumpered to somewhat more wires on the DB-25 connector, which
+in turn is intended to plug into the PC parallel printer port. I dismantled
+the DB-25 connector to find out what they had done:
+
+ Signal RJ-11 pin DB-25 pin(s) Parallel Port
+ Transmit TX 4 (Y) 2, 4, 6, 8 Data out
+ Receive RX 3 (G) 10, 14 -ACK, -AutoFeed
+ Common 2 (R) 25 Common
+ Zero crossing 1 (B) 17 or 12 -Select or +PaperEnd
+
+NOTE: In the original cable I have (which I am still using, May, 1997)
+the Zero crossing signal goes to pin 17 (-Select) on the parallel port.
+In retrospect, this doesn't make a whole lot of sense, given that the
+-Select signal propagates the other direction. Indeed, some people have
+reported problems with this, and have had success using pin 12 (+PaperEnd)
+instead. This driver searches for the zero crossing signal on either
+pin 17 or pin 12, so it should work with either cable configuration.
+My suggestion would be to start by making the cable so that the zero
+crossing signal goes to pin 12 on the parallel port.
+
+I use the TW-523 and this software in the USA with 120V/60Hz power.
+Phil Sampson (vk2jnt@gw.vk2jnt.ampr.org OR sampson@gidday.enet.dec.com)
+in Australia has reported success in using a TW-7223 (a local version
+of the TW-523) and Tandy modules with this software under 240V/50Hz power.
+For reasons explained in the comments in the driver, it will probably not
+work if you have three-phase power, but this is usually not the case for
+normal residences and offices.
+
+
+1. Installing the TW523 Device Driver
+
+I assume that you are running FreeBSD. If you are running some other
+system, you are more or less on your own, though I can try to help if you
+have problems.
+
+Check the configuration parameters at the beginning of the file
+
+ /sys/i386/isa/tw.c
+
+Probably the only thing you might need to change is to change the
+definition of HALFCYCLE from 8333 to 10000 if you are using 50Hz power.
+The driver assumes that the TW523 device is connected to a parallel port.
+See the comments near the beginning of the file to find out where to
+get a TW523 if you don't have one, and how to make a cable for it to
+connect to your parallel port.
+
+Add a line like the following
+
+ device tw0 at isa? port 0x278 tty irq 5
+
+to /sys/i386/conf/YOURSYSTEM, but make sure to change the I/O port and
+interrupt to match your hardware configuration.
+
+Cd to /sys/i386/conf and do "config YOURSYSTEM".
+Cd to /sys/compile/YOURSYSTEM and do "make depend", then "make".
+(If you have any troubles, I suggest starting fresh by doing a full
+"make clean; make depend; make".) Assuming the make works correctly, do
+
+ make install
+
+(Take the usual precautions by saving a known working kernel until you
+verify that the new kernel actually boots.)
+
+Reboot the system. You should see a line indicating that the TW523 has
+been configured as the system comes up. If you see this line, then probably
+everything is going to work OK, because the TW523 will only get configured
+if the driver is able to sync to the power line. If the TW523 is not plugged
+in, or the driver is not getting sync for some reason, then you won't see
+any message on bootup.
+
+NOTE: I have received a report that some multi IDE/SIO/PARALLEL cards
+"cheat" and use TTL outputs rather than pullup open collector outputs,
+and this can mess up the scheme by which sync gets to the driver.
+If you are having trouble getting the driver to work, you might want to
+look into this possibility.
+
+In directory /dev, execute the command
+
+ MAKEDEV tw0
+
+
+2. Installing the X-10 Daemon
+
+The X-10 daemon "xtend" is integrated in to the FreeBSD "/etc/sysconfig"
+system configuration file. To enable the daemon, simply edit that file,
+find the "xtend" line, change it to read as below.
+
+ # Set to YES if you want to run the X-10 power controller daemon
+ xtend=YES
+
+This will cause the X-10 daemon to be invoked automatically when you boot
+the system. To test the installation, you can either reboot now, or
+you can just run "xtend" by hand. The daemon should start up, and it should
+create files in /var/spool/xten. Check the file /var/spool/xten/Log to
+make sure that the daemon started up without any errors.
+
+Now you are ready to start trying X-10 commands. Try doing
+
+ xten A 1 Off
+ xten A 1 On 1 Dim:10
+
+etc. The "xten" program expects a house code as its first argument, then
+a series of key codes, which are either unit names ("1" through "16") or
+else are command names. You can find the list of command names by looking
+at the table in the file "xten.c". Each key code can optionally be followed
+by a colon : then a number specifying the number of times that command is
+to be transmitted without gaps between packets. The default is 2, and this
+is the normal case, but some commands like Bright and Dim are designed to
+be transmitted with counts other than 2. See the X-10 documentation for
+more detail.
+
+The "xten" program works by connecting to "xtend" through a socket, and
+asking that the X-10 codes be transmitted over the TW523. All activity
+on the TW523 is logged by the daemon in /var/spool/xten/Log. The daemon
+also attempts to track the state of all devices. (Of course, most X-10
+devices do not transmit when they are operated manually, so if somebody
+operates a device manually there is no way the X-10 daemon will know
+about it.)
+
+3. Low-level Programming of the TW523 Driver
+
+Normally, you would never operate the TW523 directly, rather you would
+use the shell command "xten" or you would connect to "xtend" through its
+socket. However, if you don't want to run "xtend", you can manipulate
+the TW523 directly through the device /dev/tw0. Have a look at the
+xtend code for a programming example.
+
+The driver supports read(), write(), and select() system calls.
+The driver allows multiple processes to read and write simultaneously,
+but there is probably not much sense in having more than one reader or more
+than one writer at a time, and in fact there may currently be a race
+condition in the driver if two processes try to transmit simultaneously
+(due to unsynchronized access to the sc_pkt structure in tw_sc).
+
+Transmission is done by calling write() to send three byte packets of data.
+The first byte contains a four bit house code (0=A to 15=P). The second byte
+contains five bit unit/key code (0=unit 1 to 15=unit 16, 16=All Units Off
+to 31 = Status Request). The third byte specifies the number of times the
+packet is to be transmitted without any gaps between successive transmissions.
+Normally this is 2, as per the X-10 documentation, but sometimes (e.g. for
+bright and dim codes) it can be another value. Each call to write can specify
+an arbitrary number of data bytes, but at most one packet will actually be
+processed in any call. Any incomplete packet is buffered until a subsequent
+call to write() provides data to complete it. Successive calls to write()
+leave a three-cycle gap between transmissions, per the X-10 documentation.
+The driver transmits each bit only once per half cycle, not three times as
+the X-10 documentation states, because the TW523 only provides sync on
+each power line zero crossing. So, the driver will probably not work
+properly if you have three-phase service. Most residences use a two-wire
+system, for which the driver does work.
+
+Reception is done using read(). The driver produces a series of three
+character packets. In each packet, the first character consists of flags,
+the second character is a four bit house code (0-15), and the third character
+is a five bit key/function code (0-31). The flags are the following:
+
+#define TW_RCV_LOCAL 1 /* The packet arrived during a local transmission */
+#define TW_RCV_ERROR 2 /* An invalid/corrupted packet was received */
+
+The select() system call can be used in the usual way to determine if there
+is data ready for reading.
+
+
+ Happy Controlling!
+ Gene Stark
+ stark@cs.sunysb.edu
+
+
+Appendix. Miscellaneous Additional Information
+
+The following excerpts from my E-mail correspondence may be relevant
+to some situations:
+
+
+From: Steve Passe
+Subject: Re: tw woes
+Date: Sat, 09 Dec 1995 20:57:15 -0700
+
+Hi,
+
+I have just verified that /dev/tw works on 2.1.0-RELEASE. I can
+send and receive x10 commands via my x10 daemon and X11 based tools.
+
+I used a "cross-over" cable between tw523 and db-25 connector:
+
+ |||||-----------|||||
+ \ /
+
+NOTE: I am NOT using the RadioShack brand of hood:
+
+looking at INSIDE of hood:
+
+----------
+| |
+| |
+| B G B | < Black, Green, Blue
+| W R Y | < White, Red, Yellow
+| |||||| |
+| |||||| |
+| |||||| |
+| |
+| |
+| |
+----------
+
+OUTSIDE:
+
+ Hood TW523
+---------- ------------------
+| | | |
+| | | |
+| ------ | | +------+ |
+| |||||| | | | |||| | |
+| |||||| | | | |||| | |
+| -- -- | | +-- --+ |
+| | | | | | | |
+| -- | | -- |
+| | | |
+| | | 1 2 3 4 |
+---------- ------------------
+ Y G R B B R G Y
+ | | | | | | | |
+ | | | |--------------------| | | |
+ | | |------------------------| | |
+ | |----------------------------| |
+ |--------------------------------|
+
+Be sure that the tw523 is NOWHERE NEAR a surge protector. I have seen
+x-10 devices fail to work when plugged in NEXT to a surge protector!
+
+
+I placed the tw option before the lpt entries in my config file:
+
+device tw0 at isa? port 0x378 tty irq 7
+device lpt0 at isa? port? tty irq 7
+
+from dmesg I get:
+
+Dec 9 19:11:59 ilsa /kernel: tw0 at 0x378-0x37f irq 7 on isa
+Dec 9 19:11:59 ilsa /kernel: lpt0 not probed due to I/O address conflict with
+tw0 at 0x378
+
+Once I have opened /dev/tw with my daemon I get messages
+ (pressing UNIT J, key 16):
+
+Dec 9 20:18:26 ilsa /kernel: TWRCV: valid packet: (22, 1f8) J 16
+Dec 9 20:18:26 ilsa /kernel: TWRCV: valid packet: (22, 1f8) J 16
+
+These messages from the driver should be dis-abled once you get it working,
+you'll fill up the var partition with a lot of useless garbage otherwise!
+
+
+
+From: Steve Passe
+Subject: Re: tw woes
+Date: Sat, 16 Dec 1995 11:56:59 -0700
+
+Hi,
+
+I now more or less understand the set of problems concerning cabling
+for using /dev/tw and a tw523. Summary:
+
+
+ 1: modular cables come in 2 flavors:
+
+|||||----------||||| <- "phone" cable
+ \ /
+
+ \
+|||||----------||||| <- "data" cable
+ \
+
+ we need to be able to clearly differentiate the two. I suggest we
+ standardize on using "phone" cables only.
+
+
+ 2: modular db25 connectors ARE NOT CONSISTANT in their color code
+ scheme, EVEN within the same BRAND!
+
+ we can't describe the connection in terms of cable/connector wire color.
+ we must clearly explain the consequences of mis-connection:
+ POSSIBLE damage to (but NOT limited to) the parallel port and/or tw523.
+
+
+ 3: not all parallel ports have pullups on their status inputs. I found
+ 2 different port boards in my junk box without pullups on paper-out.
+ As is, these boards failed to work, ie the probe routine failed.
+ By adding 10K pullup resistors (to +5v) to both ACK and paper-out
+ (pins 10 & 12) I was able to make these boards work: probe succeeds,
+ transmit and receive work reliably.
+
+ we must describe a test to determine if a parallel port will work as is.
+ perhaps something like:
+
+--------------------------------------------------------------------------
+Not a parallel ports will work with the connector described in this paper.
+To test your port for usability you should take the following measurements
+with a voltmeter. The computer must be powered-up, and preferably in
+a safe state for tinkering, such as halted in a startup menu. Nothing
+should be attached to the parallel ports, except perhaps an extension
+cable for testing convenience.
+
+ 1: measure the voltage between pins 10 & 25 (GND) of the parallel port.
+
+ 2: measure the voltage between pins 12 & 25 (GND) of the parallel port.
+
+If both of these measurements have a value of >= 4.0 volts your port
+should work as is. If either is below 4.0 volts (typically less than
+1.0 volt) your port will NOT WORK RELIABLY as is. It can be made to
+work by adding 10k ohm pull-up resistors to either line that is below
+the minimum 4.0 volts. This is an ADVANCED TECHNIQUE that should NOT
+be attempted by anyone without some hardware construction experience.
+
+Assuming that you do feel competant to make these modifications it is
+easiest to tack 10k resistors on the bottom side of the port board
+from each of pins 10 & 12 of the parallel port connector to a source
+of +5 volts. This will probably be the power pin of one of the ICs.
+CAUTION: there may also be +-12 volts on a port board supplying some
+of the ICs. If your port is on your motherboard it would probably be
+best to obtain an external port card, and disable/re-address the 1st
+parallel port.
+--------------------------------------------------------------------------
+
+
diff --git a/usr.sbin/xten/xten.1 b/usr.sbin/xten/xten.1
new file mode 100644
index 0000000..76f8668
--- /dev/null
+++ b/usr.sbin/xten/xten.1
@@ -0,0 +1,113 @@
+.\" Copyright (c) 1992, 1993 Eugene W. Stark
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Eugene W. Stark.
+.\" 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 EUGENE W. STARK (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: xten.1,v 1.7 1997/10/27 12:27:20 charnier Exp $
+.\"
+.Dd October 30, 1993
+.Dt XTEN 1
+.Os
+.Sh NAME
+.Nm xten
+.Nd transmit X-10 commands
+.Sh SYNOPSIS
+.Nm xten
+.Op Fl ""
+.Ar house Ar key Ns Op Ar :cnt
+.Oo
+.Op Ar house
+.Ar key Ns Op Ar :cnt
+.Ar ...
+.Oc
+.Sh DESCRIPTION
+.Nm Xten
+is a command-line interface to the X-10 daemon.
+When invoked with a one-letter house code (A-P) and a series of key/unit
+codes as arguments, it requests the X-10 daemon to transmit a corresponding
+series of X-10 packets. The X-10 daemon makes its best effort to ensure
+that the packets are all transmitted correctly, though in general it is
+not possible to tell whether the commands were actually received and
+executed by the remote X-10 devices.
+.Pp
+When invoked with the single argument
+.Fl "" ,
+.Nm
+enters an interactive mode in which a line is repeatedly read from the
+standard input, sent to the X-10 daemon, and the one-line response from
+the daemon printed on the standard output.
+.Sh OPTIONS
+The
+.Ar house
+argument is a one-letter house code in the range A-P.
+All the X-10 requests generated will refer to this house code.
+Each
+.Ar key
+is either a numeric unit code in the range 1-16, or else
+is a string that specifies an X-10 function. The possible
+function code strings are:
+.Bl -diag
+.It AllUnitsOff
+.It AllLightsOn
+.It On
+.It Off
+.It Dim
+.It Bright
+.It AllLightsOff
+.It ExtendedCode
+.It HailRequest
+.It HailAcknowledge
+.It PreSetDim0
+.It PreSetDim1
+.It ExtendedData
+.It StatusOn
+.It StatusOff
+.It StatusRequest
+.El
+.Pp
+Each
+.Ar key
+may be followed by an optional numeric
+.Ar cnt ,
+which specifies the number of packets that are to be sent with that
+key code without gaps. If this argument is omitted, two packets
+are transmitted. The ability to specify numbers of packets other than
+two is used by the X-10
+.Em Dim
+and
+.Em Bright
+commands.
+.Sh SEE ALSO
+.Xr tw 4 ,
+.Xr xtend 8
+.Sh FILES
+.Bl -tag -width /var/spool/xten/Status -compact
+.It Pa /dev/tw0
+the TW523 special file
+.El
+.Sh AUTHORS
+.An Eugene W. Stark Aq stark@cs.sunysb.edu
diff --git a/usr.sbin/xten/xten.c b/usr.sbin/xten/xten.c
new file mode 100644
index 0000000..f59e76f
--- /dev/null
+++ b/usr.sbin/xten/xten.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1992, 1993 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 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 EUGENE W. STARK (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[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Xten - user command interface to X-10 daemon
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "xtend.h"
+#include "xten.h"
+#include "paths.h"
+
+#define RETRIES 10
+#define CMDLEN 512
+
+char *X10housenames[] = {
+ "A", "B", "C", "D", "E", "F", "G", "H",
+ "I", "J", "K", "L", "M", "N", "O", "P",
+ NULL
+};
+
+char *X10cmdnames[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15", "16",
+ "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff",
+ "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1",
+ "ExtendedData", "StatusOn", "StatusOff", "StatusRequest",
+ NULL
+};
+
+int find __P((char *, char *[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c, tmp, h, k, sock, error;
+ FILE *daemon;
+ struct sockaddr_un sa;
+ char *sockpath = SOCKPATH;
+ char reply[CMDLEN], cmd[CMDLEN], *cp;
+ int interactive = 0;
+
+ if(argc == 2 && !strcmp(argv[1], "-")) interactive++;
+ else if(argc < 3)
+ usage();
+ if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ errx(1, "can't create socket");
+ strcpy(sa.sun_path, sockpath);
+ sa.sun_family = AF_UNIX;
+ if(connect(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0)
+ errx(1, "can't connect to X-10 daemon");
+ if((daemon = fdopen(sock, "w+")) == NULL)
+ errx(1, "can't attach stream to socket");
+ /*
+ * If interactive, copy standard input to daemon and report results
+ * on standard output.
+ */
+ if(interactive) {
+ while(!feof(stdin)) {
+ if(fgets(cmd, CMDLEN, stdin) != NULL) {
+ fprintf(daemon, "%s", cmd);
+ fflush(daemon);
+ if(fgets(reply, CMDLEN, daemon) != NULL) {
+ fprintf(stdout, "%s", reply);
+ fflush(stdout);
+ }
+ }
+ }
+ exit(0);
+ }
+ /*
+ * Otherwise, interpret arguments and issue commands to daemon,
+ * handling retries in case of errors.
+ */
+ if((h = find(argv[1], X10housenames)) < 0)
+ errx(1, "invalid house code: %s", argv[1]);
+ argv++;
+ argv++;
+ while(argc >= 3) {
+ cp = argv[0];
+ if((tmp = find(cp, X10housenames)) >= 0) {
+ h = tmp;
+ argv++;
+ argc--;
+ continue;
+ }
+ while(*cp != '\0' && *cp != ':') cp++;
+ if(*cp == ':') c = atoi(cp+1);
+ else c = 2;
+ *cp = '\0';
+ if((k = find(argv[0], X10cmdnames)) < 0) {
+ warnx("invalid key/unit code: %s", argv[0]);
+ error++;
+ }
+ error = 0;
+ while(error < RETRIES) {
+ fprintf(daemon, "send %s %s %d\n", X10housenames[h], X10cmdnames[k], c);
+ fflush(daemon);
+ fgets(reply, CMDLEN, daemon);
+ if(strncmp(reply, "ERROR", 5)) break;
+ error++;
+ usleep(200000);
+ }
+ if(error == RETRIES) {
+ warnx("command failed: send %s %s %d",
+ X10housenames[h], X10cmdnames[k], c);
+ }
+ argc--;
+ argv++;
+ }
+ fprintf(daemon, "done\n");
+ fgets(reply, CMDLEN, daemon);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: xten house key[:cnt] [[house] key[:cnt] ...]\n");
+ exit(1);
+}
+
+int
+find(s, tab)
+char *s;
+char *tab[];
+{
+ int i;
+
+ for(i = 0; tab[i] != NULL; i++) {
+ if(strcasecmp(s, tab[i]) == 0) return(i);
+ }
+ return(-1);
+}
diff --git a/usr.sbin/yp_mkdb/Makefile b/usr.sbin/yp_mkdb/Makefile
new file mode 100644
index 0000000..b886d19
--- /dev/null
+++ b/usr.sbin/yp_mkdb/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+PROG= yp_mkdb
+SRCS= yp_mkdb.c yp_dblookup.c yp_dbwrite.c
+
+MAN8= yp_mkdb.8
+
+.PATH: ${.CURDIR}/../../libexec/ypxfr ${.CURDIR}/../ypserv
+
+CFLAGS+= -Dyp_error=warnx -I${.CURDIR}/../../libexec/ypxfr
+CFLAGS+= -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..fef5eaf
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.8
@@ -0,0 +1,171 @@
+.\" 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.
+.\"
+.\" $Id: yp_mkdb.8,v 1.7 1997/10/27 12:29:24 charnier Exp $
+.\"
+.Dd March 12, 1996
+.Dt YP_MKDB 8
+.Os
+.Sh NAME
+.Nm yp_mkdb
+.Nd "generate the NIS databases"
+.Sh SYNOPSIS
+.Nm yp_mkdb
+.Fl c
+.Nm yp_mkdb
+.Fl u Ar dbname
+.Nm yp_mkdb
+.Op Fl c
+.Op Fl b
+.Op Fl s
+.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
+.Nm Yp_mkdb
+creates
+.Xr db 3
+style databases for use with FreeBSD's NIS server.
+.Nm Yp_mkdb
+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.
+.Nm Yp_mkdb
+is usually invoked by
+.Pa /var/yp/Makefile .
+.Nm Yp_mkdb
+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).
+.Sh OPTIONS
+The
+.Nm
+command supports the following flags and options:
+.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
+This flag causes
+.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 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.
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm
+to build the NIS databases
+.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..aea610f
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.c
@@ -0,0 +1,338 @@
+/*
+ * 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 lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#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()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: yp_mkdb -c",
+ " yp_mkdb -u dbname",
+ " yp_mkdb [-c] [-b] [-s] [-i inputfile] [-o outputfile]",
+ " [-d domainname ] [-m mastername] inputfile dbname");
+ exit(1);
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+static DB *open_db(path, flags)
+ char *path;
+ int flags;
+{
+ extern HASHINFO openinfo;
+
+ return(dbopen(path, flags, PERM_SECURE, DB_HASH, &openinfo));
+}
+
+static void unwind(map)
+ 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 (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int un = 0;
+ int clear = 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, "uhcbsd:i:o:m:")) != -1) {
+ switch(ch) {
+ 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 (*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,
+ xdr_void, (void *)&in,
+ xdr_void, (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..ec83451
--- /dev/null
+++ b/usr.sbin/ypbind/Makefile
@@ -0,0 +1,9 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.4 1997/02/22 16:14:53 peter Exp $
+
+SRCS= ypbind.c yp_ping.c
+PROG= ypbind
+MAN8= ypbind.8
+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..bc6d319
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.c
@@ -0,0 +1,545 @@
+/*
+ * 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.
+ */
+
+/*
+ * What follows is a special version of clntudp_call() that has been
+ * hacked to send requests and receive replies asynchronously. Similar
+ * magic is used inside rpc.nisd(8) for the special non-blocking,
+ * non-fork()ing, non-threading callback support.
+ */
+
+/*
+ * 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 = "@(#)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
+static const char rcsid[] =
+ "$Id: yp_ping.c,v 1.3 1997/10/27 07:45:45 charnier Exp $";
+#endif
+
+/*
+ * 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 <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/yp.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include "yp_ping.h"
+
+#ifndef timeradd
+#ifndef KERNEL /* use timevaladd/timevalsub in kernel */
+/* NetBSD/OpenBSD compatable interfaces */
+#define timeradd(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if ((vvp)->tv_usec >= 1000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+#endif
+
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_sock;
+ bool_t cu_closeit;
+ struct sockaddr_in cu_raddr;
+ int cu_rlen;
+ struct timeval cu_wait;
+ struct timeval cu_total;
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ u_int cu_xdrpos;
+ u_int cu_sendsz;
+ char *cu_outbuf;
+ u_int cu_recvsz;
+ char cu_inbuf[1];
+};
+
+static enum clnt_stat
+clntudp_a_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
+ register CLIENT *cl; /* client handle */
+ u_long proc; /* procedure number */
+ xdrproc_t xargs; /* xdr routine for args */
+ caddr_t argsp; /* pointer to args */
+ xdrproc_t xresults; /* xdr routine for results */
+ caddr_t resultsp; /* pointer to results */
+ struct timeval utimeout; /* seconds to wait before giving up */
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ register XDR *xdrs;
+ register int outlen = 0;
+ register int inlen;
+ int fromlen;
+ fd_set *fds, readfds;
+ struct sockaddr_in from;
+ struct rpc_msg reply_msg;
+ XDR reply_xdrs;
+ struct timeval time_waited, start, after, tmp1, tmp2, tv;
+ bool_t ok;
+ int nrefreshes = 2; /* number of times to refresh cred */
+ struct timeval timeout;
+
+ if (cu->cu_total.tv_usec == -1)
+ timeout = utimeout; /* use supplied timeout */
+ else
+ timeout = cu->cu_total; /* use default timeout */
+
+ if (cu->cu_sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ timerclear(&time_waited);
+
+call_again:
+ xdrs = &(cu->cu_outxdrs);
+ if (xargs == NULL)
+ goto get_reply;
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, cu->cu_xdrpos);
+ /*
+ * the transaction is the first thing in the out buffer
+ */
+ (*(u_short *)(cu->cu_outbuf))++;
+ if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp))) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
+ }
+ outlen = (int)XDR_GETPOS(xdrs);
+
+send_again:
+ if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
+ (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ }
+
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (!timerisset(&timeout)) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+
+get_reply:
+
+ /*
+ * sub-optimal code appears here because we have
+ * some clock time to spare while the packets are in flight.
+ * (We assume that this is actually only executed once.)
+ */
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = resultsp;
+ reply_msg.acpted_rply.ar_results.proc = xresults;
+
+ gettimeofday(&start, NULL);
+ for (;;) {
+ /* XXX we know the other bits are still clear */
+ FD_SET(cu->cu_sock, fds);
+ tv = cu->cu_wait;
+ switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
+
+ case 0:
+ timeradd(&time_waited, &cu->cu_wait, &tmp1);
+ time_waited = tmp1;
+ if (timercmp(&time_waited, &timeout, <))
+ goto send_again;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+
+ case -1:
+ if (errno == EINTR) {
+ gettimeofday(&after, NULL);
+ timersub(&after, &start, &tmp1);
+ timeradd(&time_waited, &tmp1, &tmp2);
+ time_waited = tmp2;
+ if (timercmp(&time_waited, &timeout, <))
+ continue;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+
+ do {
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
+ (int) cu->cu_recvsz, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ if (inlen < sizeof(u_int32_t))
+ continue;
+#ifdef dont_check_xid
+ /* see if reply transaction id matches sent id */
+ if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
+ continue;
+#endif
+ /* we now assume we have the proper reply */
+ break;
+ }
+
+ /*
+ * now decode and validate the response
+ */
+ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
+ ok = xdr_replymsg(&reply_xdrs, &reply_msg);
+ /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
+ if (ok) {
+ _seterr_reply(&reply_msg, &(cu->cu_error));
+ if (cu->cu_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(cl->cl_auth,
+ &reply_msg.acpted_rply.ar_verf)) {
+ cu->cu_error.re_status = RPC_AUTHERROR;
+ cu->cu_error.re_why = AUTH_INVALIDRESP;
+ }
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
+ nrefreshes--;
+ goto call_again;
+ }
+ } /* end of unsuccessful completion */
+ } /* end of valid reply message */
+ else {
+ cu->cu_error.re_status = RPC_CANTDECODERES;
+ }
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status);
+}
+
+
+/*
+ * 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(address, program, version, protocol)
+ 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, xdr_pmap, &parms,
+ 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;
+ unsigned long xid;
+};
+
+int __yp_ping(restricted_addrs, cnt, dom, port)
+ struct in_addr *restricted_addrs;
+ int cnt;
+ char *dom;
+ short *port;
+{
+ struct timeval tv = { 5 , 0 };
+ struct ping_req **reqs;
+ unsigned long i;
+ struct sockaddr_in sin, *any = NULL;
+ int winner = -1;
+ time_t xid_seed, xid_lookup;
+ int sock, dontblock = 1;
+ CLIENT *clnt;
+ char *foo = dom;
+ struct cu_data *cu;
+ enum clnt_stat (*oldfunc)();
+ 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, &tv);
+ ioctl(sock, FIONBIO, &dontblock);
+ oldfunc = clnt->cl_ops->cl_call;
+ clnt->cl_ops->cl_call = clntudp_a_call;
+
+ /* Transmit */
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL) {
+ /* subtract one; clntudp_call() will increment */
+ *((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1;
+ 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. */
+ xid_lookup = *((u_int32_t *)(cu->cu_inbuf));
+ 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 */
+ clnt->cl_ops->cl_call = oldfunc;
+ 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..eadb5de
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.h
@@ -0,0 +1 @@
+extern int __yp_ping __P(( 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..3005b53
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.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.
+.\"
+.\" $Id: ypbind.8,v 1.11 1997/10/27 07:45:46 charnier Exp $
+.\"
+.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
+.Op Fl S Ar domainname,server1,server2,...
+.Sh DESCRIPTION
+.Nm Ypbind
+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.
+.Nm Ypbind
+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.
+.Sh OPTIONS
+The following options are supported by
+.Nm Ns :
+.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 Ar domainname,server1,server2,server3,...
+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 ouf 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
+program 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 4 ,
+.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..52aaad1
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.c
@@ -0,0 +1,1009 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: ypbind.c,v 1.27 1998/01/19 23:31:38 wpaul Exp $";
+#endif /* not lint */
+
+#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 __P((void));
+void *ypbindproc_null_2_yp __P((SVCXPRT *, void *, CLIENT *));
+void *ypbindproc_setdom_2_yp __P((SVCXPRT *, struct ypbind_setdom *, CLIENT *));
+void rpc_received __P((char *, struct sockaddr_in *, int ));
+void broadcast __P((struct _dom_binding *));
+int ping __P((struct _dom_binding *));
+int tell_parent __P((char *, struct sockaddr_in *));
+void handle_children __P(( struct _dom_binding * ));
+void reaper __P((int));
+void terminate __P((int));
+void yp_restricted_mode __P((char *));
+int verify __P((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(transp, argp, clnt)
+SVCXPRT *transp;
+void *argp;
+CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ return (void *)&res;
+}
+
+struct ypbind_resp *
+ypbindproc_domain_2_yp(transp, argp, clnt)
+SVCXPRT *transp;
+domainname *argp;
+CLIENT *clnt;
+{
+ static struct ypbind_resp res;
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ bzero((char *)&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((char *)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_long *)&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(transp, argp, clnt)
+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((char *)&bindsin, sizeof bindsin);
+ bindsin.sin_family = AF_INET;
+ bindsin.sin_addr.s_addr = *(u_long *)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);
+}
+
+static void
+ypbindprog_2(rqstp, transp)
+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((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(transp, &argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ return;
+}
+
+/* Jack the reaper */
+void reaper(sig)
+int sig;
+{
+ int st;
+
+ while(wait3(&st, WNOHANG, NULL) > 0)
+ children--;
+}
+
+void terminate(sig)
+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(argc, argv)
+int argc;
+char **argv;
+{
+ struct timeval tv;
+ int i;
+ DIR *dird;
+ struct dirent *dirp;
+ struct _dom_binding *ypdb;
+
+ /* 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+1]);
+ else if (strcmp("-m", argv[i]) == 0)
+ yp_manycast++;
+ }
+
+ /* 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((char *)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=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()
+{
+ 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(ypdb)
+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((char *)&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(dom, addr)
+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((char *)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 seperate
+ * 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(ypdb)
+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((char *)&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((char *)&sin, sizeof(struct sockaddr_in));
+ bcopy((char *)&restricted_addrs[i],
+ (char *)&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 = (char *)&ypdb->dom_domain;
+ stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
+ xdr_domainname, (char *)&ptr, xdr_bool, (char *)&out,
+ broadcast_result);
+ }
+
+ if (stat != RPC_SUCCESS) {
+ bzero((char *)&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(ypdb)
+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 = (char *)&ypdb->dom_domain;
+
+ if ((stat = clnt_call(client_handle, YPPROC_DOMAIN,
+ xdr_domainname, (char *)&ptr, xdr_bool, (char *)&out,
+ timeout)) != 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(dom, raddrp, force)
+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((char *)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((char *)raddrp, (char *)&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 = (caddr_t)&(udptransp->xp_port);
+ iov[0].iov_len = sizeof udptransp->xp_port;
+ iov[1].iov_base = (caddr_t)&ybr;
+ iov[1].iov_len = sizeof ybr;
+
+ bzero(&ybr, sizeof ybr);
+ ybr.ypbind_status = YPBIND_SUCC_VAL;
+ *(u_long *)&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(addr)
+struct in_addr addr;
+{
+ int i;
+
+ for (i = 0; i < RESTRICTED_SERVERS; i++)
+ if (!bcmp((char *)&addr, (char *)&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(args)
+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 ((char *)h->h_addr_list[0], (char *)&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..7d4a553
--- /dev/null
+++ b/usr.sbin/yppoll/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.3 1997/02/22 16:14:58 peter Exp $
+
+PROG= yppoll
+MAN8= 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..f7ecd71
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.8
@@ -0,0 +1,84 @@
+.\" $NetBSD: yppoll.8,v 1.4 1997/07/30 22:55:00 jtc Exp $
+.\"
+.\" 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.
+.\"
+.\" $Id: yppoll.8,v 1.2 1997/10/27 12:30:30 charnier Exp $
+.\"
+.Dd October 25, 1994
+.Dt YPPOLL 8
+.Os
+.Sh NAME
+.Nm yppoll
+.Nd ask version of YP map from YP server
+.Sh SYNOPSIS
+.Nm yppoll
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar mapname
+.Sh DESCRIPTION
+.Nm Yppoll
+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 4 ,
+.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..3f1a305
--- /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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: yppoll [-h host] [-d domainname] mapname\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ char *domainname;
+ char *hostname = "localhost";
+ char *inmap, *master;
+ int order;
+ int c, r;
+
+ 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));
+ printf("Map %s has order number %d. %s", inmap, order,
+ ctime((time_t *)&order));
+ 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..68a33e0
--- /dev/null
+++ b/usr.sbin/yppush/Makefile
@@ -0,0 +1,29 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:15:00 peter Exp $
+
+PROG= yppush
+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
+
+MAN8= yppush.8
+
+CFLAGS+=-I. -I${.CURDIR}/../../libexec/ypxfr
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+RPCGEN= rpcgen -C
+
+.PATH: ${RPCDIR} ${.CURDIR}/../../usr.sbin/ypserv \
+ ${.CURDIR}/../../libexec/ypxfr
+
+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..add5529
--- /dev/null
+++ b/usr.sbin/yppush/yppush.8
@@ -0,0 +1,169 @@
+.\" 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.
+.\"
+.\" $Id: yppush.8,v 1.7 1997/11/03 07:53:41 charnier Exp $
+.\"
+.Dd February 5, 1995
+.Dt YPPUSH 8
+.Os
+.Sh NAME
+.Nm yppush
+.Nd "force propagation of updated NIS databases"
+.Sh SYNOPSIS
+.Nm yppush
+.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
+.Nm Yppush
+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
+.Nm NOPUSH=True
+entry in the Makefile must first be commented out
+(the default
+.Bx Free
+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
+.Sh OPTIONS
+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
+.Nm Yppush
+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: causes
+.Nm
+to print debugging messages as it runs. Specifying this flag twice
+makes
+.Nm
+even more verbose.
+.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 4 ,
+.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..6d95356
--- /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.
+ *
+ * $Id$
+ */
+
+/* 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 __P((void));
+extern void yppush_xfrrespprog_1 __P(( 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..768c328
--- /dev/null
+++ b/usr.sbin/yppush/yppush_main.c
@@ -0,0 +1,688 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yppush_main.c,v 1.9 1997/11/03 07:53:44 charnier Exp $";
+#endif /* not lint */
+
+#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 char *yppusherr_string(err)
+ 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(status, tid)
+ 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(now)
+ 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(sig)
+ 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()
+{
+#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(sig)
+ 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(job)
+ 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(server, map, tid)
+ 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_sock; /*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_sock, F_SETOWN, getpid()) == -1 ||
+ fcntl(xprt->xp_sock, 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(status, key, keylen, val, vallen, data)
+ 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(argc,argv)
+ 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;
+
+ 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));
+ 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..7157a3c
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile
@@ -0,0 +1,41 @@
+# $Id: Makefile,v 1.15 1998/05/10 16:03:17 bde Exp $
+
+PROG= ypserv
+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
+
+MAN8= ypserv.8 ypinit.8
+
+CFLAGS+= -I. -DDB_CACHE
+
+CLEANFILES= yp_svc.c ypxfr_clnt.c yp.h
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR}
+
+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} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${.CURDIR}/Makefile.yp \
+ ${DESTDIR}/var/yp/Makefile.dist
+ ${INSTALL} -c -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..6342c29
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile.yp
@@ -0,0 +1,583 @@
+#
+# Makefile for the NIS databases
+#
+# $Id: Makefile.yp,v 1.24 1998/02/21 18:14:30 wpaul Exp $
+#
+# 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
+RCAT = /bin/cat
+CAT = @$(RCAT)
+
+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
+# seperate 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
+GROUP = $(YPSRCDIR)/group
+ALIASES = $(YPSRCDIR)/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.host
+
+# 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 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.host
+.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
+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 $@..."
+ $(CAT) $(YPSERVERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*") print $$0"\t"$$0 }' $^ \
+ | $(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
+ $(CAT) $(ETHERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$2"\t"$$0 }' $^ | $(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
+ $(CAT) $(ETHERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$1"\t"$$0 }' $^ | $(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
+ $(CAT) $(BOOTPARAMS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 !~ "^#.*" && $$1 != "+") \
+ print $$0 }' $^ | $(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
+ $(CAT) $(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
+ @$(MAKE) -f ../Makefile netid
+.endif
+
+
+netgroup.byhost: $(NETGROUP)
+ @echo "Updating $@..."
+.if ${NETGROUP} == "/dev/null"
+ @echo "Netgroup source file not found -- skipping"
+.else
+ $(CAT) $(NETGROUP) | $(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
+ $(CAT) $(NETGROUP) | $(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 $@..."
+ $(CAT) $(HOSTS) | \
+ $(AWK) '/^[0-9]/ { for (n=2; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 }' $^ | $(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 $@..."
+ $(CAT) $(HOSTS) | \
+ $(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$0 }' $^ \
+ | $(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 $@..."
+ $(CAT) $(NETWORKS) | \
+ $(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $^ | $(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 $@..."
+ $(CAT) $(NETWORKS) | \
+ $(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $^ \
+ | $(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 $@..."
+ $(CAT) $(PROTOCOLS) | \
+ $(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $^ | $(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 $@..."
+ $(CAT) $(PROTOCOLS) | \
+ $(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $^ \
+ | $(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 $@..."
+ $(CAT) $(RPC) | \
+ $(AWK) \
+ '$$1 !~ "^#.*" { print $$1"\t"$$0; \
+ for (n=3; n<=NF && $$n !~ "^#.*"; n++) \
+ print $$n"\t"$$0 \
+ }' $^ | $(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 $@..."
+ $(CAT) $(RPC) | \
+ $(AWK) '$$1 !~ "^#.*" { print $$2"\t"$$0 }' $^ \
+ | $(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 $@..."
+ $(CAT) $(SERVICES) | \
+ $(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 ; \
+ }' $^ | $(DBLOAD) -i $(SERVICES) -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
+ $(CAT) $(PUBLICKEY) | \
+ $(AWK) '$$1 !~ "^#.*" { print $$1"\t"$$2 }' $^ \
+ | $(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 \
+ $(RCAT) $(MASTER) | \
+ $(AWK) -F: '{if ($$1 != "+") \
+ print $$1":*:"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; \
+ else $(RCAT) $(MASTER) | \
+ $(AWK) -F: '{if ($$1 != "+") \
+ print $$1":"$$2":"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; fi
+
+
+passwd.byname: $(PASSWD)
+ @echo "Updating $@..."
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -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 $@..."
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -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 $@..."
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -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 $@..."
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -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
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -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
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -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.host: $(AMDHOST)
+ @echo "Updating $@..."
+ $(CAT) $(AMDHOST) | \
+ $(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); \
+ }' | \
+ $(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..9d61afc
--- /dev/null
+++ b/usr.sbin/ypserv/yp_access.c
@@ -0,0 +1,325 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yp_access.c,v 1.15 1997/10/29 07:25:02 charnier Exp $";
+#endif /* not lint */
+
+#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 */
+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()
+{
+}
+#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()
+{
+ 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 seperate 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(map, domain, rqstp)
+#else
+int yp_access(map, rqstp)
+#endif
+ const char *map;
+#ifdef DB_CACHE
+ const char *domain;
+#endif
+ const struct svc_req *rqstp;
+{
+ struct sockaddr_in *rqhost;
+ int status = 0;
+ static unsigned long oldaddr = 0;
+#ifndef TCP_WRAPPER
+ struct securenet *tmp;
+#endif
+ char *yp_procedure = NULL;
+ char procbuf[50];
+
+ if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) {
+ snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", rqstp->rq_prog,
+ 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(domain)
+ 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..b271497
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dblookup.c
@@ -0,0 +1,754 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yp_dblookup.c,v 1.1 1997/11/09 20:54:38 wpaul Exp wpaul $";
+#endif /* not lint */
+
+#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 CIRCLEQ_HEAD(circlehead, circleq_entry) qhead;
+
+struct circleq_entry {
+ struct dbent *dbptr;
+ CIRCLEQ_ENTRY(circleq_entry) links;
+};
+
+/*
+ * Initialize the circular queue.
+ */
+void yp_init_dbs()
+{
+ CIRCLEQ_INIT(&qhead);
+ return;
+}
+
+/*
+ * Dynamically allocate an entry for the circular queue.
+ * Return a NULL pointer on failure.
+ */
+static struct circleq_entry *yp_malloc_qent()
+{
+ 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(q)
+ 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()
+{
+ register struct circleq_entry *qptr;
+
+ qptr = qhead.cqh_last;
+ CIRCLEQ_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()
+{
+ register struct circleq_entry *qptr;
+
+ while(qhead.cqh_first != (void *)&qhead) {
+ qptr = qhead.cqh_first; /* save this */
+ CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, 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(dbp)
+ 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(map, domain, flag)
+ 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);
+
+ for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
+ qptr = qptr->links.cqe_next) {
+ 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 (qhead.cqh_first->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(dbp, name, size)
+ 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);
+
+ CIRCLEQ_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(name, key, size)
+ char *name;
+ char *key;
+ int size;
+{
+ register struct circleq_entry *qptr;
+
+ for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
+ qptr = qptr->links.cqe_next) {
+ 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 != qhead.cqh_first) {
+ CIRCLEQ_REMOVE(&qhead, qptr, links);
+ CIRCLEQ_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(domain, map, key, size)
+ 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((char *)&buf, key, size)) != NULL) {
+ return(dbp);
+ } else {
+ if ((dbp = yp_open_db(domain, map)) != NULL) {
+ if (yp_cache_db(dbp, (char *)&buf, size)) {
+ (void)(dbp->close)(dbp);
+ yp_errno = YP_YPERR;
+ return(NULL);
+ }
+ }
+ }
+
+ return (dbp);
+}
+#endif
+
+/*
+ * Open a DB database.
+ */
+DB *yp_open_db(domain, map)
+ 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(dbp,key,data,allow)
+ DB *dbp;
+#else
+int yp_get_record(domain,map,key,data,allow)
+ const char *domain;
+ const char *map;
+#endif
+ const DBT *key;
+ DBT *data;
+ int allow;
+{
+#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]",
+ key->size, 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
+ qhead.cqh_first->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]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = "";
+ qhead.cqh_first->dbptr->size = 0;
+ }
+#else
+ bcopy((char *)data->data, (char *)&buf, data->size);
+ data->data = (void *)&buf;
+ (void)(dbp->close)(dbp);
+#endif
+
+ return(YP_TRUE);
+}
+
+int yp_first_record(dbp,key,data,allow)
+ 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
+ qhead.cqh_first->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
+ qhead.cqh_first->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]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+ }
+#else
+ bcopy((char *)data->data, (char *)&buf, data->size);
+ data->data = (void *)&buf;
+#endif
+
+ return(YP_TRUE);
+}
+
+int yp_next_record(dbp,key,data,all,allow)
+ 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
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+#endif
+ return(rval);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("retrieving next key, previous was: [%.*s]",
+ key->size, key->data);
+
+ if (!all) {
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->key == NULL) {
+#endif
+ (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
+ while(strncmp((char *)key->data,lkey.data,
+ (int)key->size) || key->size != lkey.size)
+ if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ return(YP_NOKEY);
+ }
+
+#ifdef DB_CACHE
+ }
+#endif
+ }
+
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ qhead.cqh_first->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
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+ }
+#else
+ bcopy((char *)key->data, (char *)&keybuf, key->size);
+ lkey.data = (void *)&keybuf;
+ lkey.size = key->size;
+ bcopy((char *)data->data, (char *)&datbuf, data->size);
+ data->data = (void *)&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(map, domain, key, allow)
+ 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(key, val)
+ 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(key, val)
+ 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(key, val)
+ 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..1c334a8
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dnslookup.c
@@ -0,0 +1,542 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yp_dnslookup.c,v 1.14 1998/06/09 05:06:27 imp Exp $";
+#endif /* not lint */
+
+/*
+ * 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(hp)
+ 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 __P((char *, int, char *, int));
+
+static CIRCLEQ_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;
+ CIRCLEQ_ENTRY(circleq_dnsentry) links;
+};
+
+static int pending = 0;
+
+int yp_init_resolver()
+{
+ CIRCLEQ_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()
+{
+ 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(name, type)
+ 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");
+ 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(id, type)
+ unsigned long id;
+ int type;
+{
+ register struct circleq_dnsentry *q;
+
+ for (q = qhead.cqh_first; q != (void *)&qhead; q = q->links.cqe_next) {
+ 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(q, buf)
+ 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()
+{
+ register struct circleq_dnsentry *q, *n;
+
+ q = qhead.cqh_first;
+ while(q != (void *)&qhead) {
+ q->ttl--;
+ n = q->links.cqe_next;
+ if (!q->ttl) {
+ CIRCLEQ_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()
+{
+ 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 the lookup failed, try appending one of the domains
+ * from resolv.conf. If we have no domains to test, the
+ * query has failed.
+ */
+ if (hent == NULL) {
+ if ((h_errno == TRY_AGAIN || h_errno == NO_RECOVERY)
+ && q->domain && *q->domain) {
+ snprintf(retrybuf, sizeof(retrybuf), "%s.%s",
+ q->name, *q->domain);
+ if (debug)
+ yp_error("retrying with: %s", retrybuf);
+ q->id = yp_send_dns_query(retrybuf, q->type);
+ q->ttl = DEF_TTL;
+ q->domain++;
+ return;
+ }
+ } else {
+ 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--;
+ CIRCLEQ_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(rqstp, 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_sock, 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);
+ CIRCLEQ_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(rqstp, 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_sock, 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);
+ CIRCLEQ_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..94adf32
--- /dev/null
+++ b/usr.sbin/ypserv/yp_error.c
@@ -0,0 +1,90 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * error logging/reporting facilities
+ * stolen from /usr/libexec/mail.local via ypserv
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+int debug;
+extern int _rpcpmstart;
+
+extern char *progname;
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void __verr(fmt, ap)
+ const char *fmt;
+ _BSD_VA_LIST_ ap;
+
+{
+ if (debug && !_rpcpmstart) {
+ fprintf(stderr,"%s: ",progname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vsyslog(LOG_NOTICE, fmt, ap);
+ }
+}
+
+void
+#ifdef __STDC__
+yp_error(const char *fmt, ...)
+#else
+yp_error(fmt, va_list)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ __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..0aaee26
--- /dev/null
+++ b/usr.sbin/ypserv/yp_extern.h
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ *
+ * $Id: yp_extern.h,v 1.11 1997/02/22 16:15:11 peter Exp $
+ */
+
+#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 enum ypstat yp_errno;
+extern void yp_error __P((const char *, ...));
+#ifdef DB_CACHE
+extern int yp_get_record __P(( DB *, const DBT *, DBT *, int));
+#else
+extern int yp_get_record __P(( const char *, const char *, const DBT *, DBT *, int));
+#endif
+extern int yp_first_record __P((const DB *, DBT *, DBT *, int));
+extern int yp_next_record __P((const DB *, DBT *, DBT *, int, int));
+extern char *yp_dnsname __P(( char * ));
+extern char *yp_dnsaddr __P(( const char * ));
+#ifdef DB_CACHE
+extern int yp_access __P((const char *, const char *, const struct svc_req * ));
+#else
+extern int yp_access __P((const char *, const struct svc_req * ));
+#endif
+extern int yp_validdomain __P((const char * ));
+extern DB *yp_open_db __P(( const char *, const char *));
+extern DB *yp_open_db_cache __P(( const char *, const char *, const char *, int ));
+extern void yp_flush_all __P(( void ));
+extern void yp_init_dbs __P(( void ));
+extern int yp_testflag __P(( char *, char *, int ));
+extern void load_securenets __P(( void ));
+
+#ifdef DB_CACHE
+extern ypstat yp_select_map __P(( char *, char *, keydat *, int ));
+extern ypstat yp_getbykey __P(( keydat *, valdat * ));
+extern ypstat yp_firstbykey __P(( keydat *, valdat * ));
+extern ypstat yp_nextbykey __P(( keydat *, valdat * ));
+#endif
+
+extern unsigned long svcudp_set_xid __P(( SVCXPRT *, unsigned long ));
+extern unsigned long svcudp_get_xid __P(( SVCXPRT * ));
+
+#ifndef RESOLVER_TIMEOUT
+#define RESOLVER_TIMEOUT 3600
+#endif
+
+extern int yp_init_resolver __P(( void ));
+extern void yp_run_dnsq __P(( void ));
+extern void yp_prune_dnsq __P(( void ));
+extern ypstat yp_async_lookup_name __P(( struct svc_req *, char * ));
+extern ypstat yp_async_lookup_addr __P(( 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..f20ccb3
--- /dev/null
+++ b/usr.sbin/ypserv/yp_main.c
@@ -0,0 +1,337 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yp_main.c,v 1.18 1998/06/04 15:11:14 wpaul Exp $";
+#endif /* not lint */
+
+/*
+ * 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 */
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#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 __P((struct svc_req *, register SVCXPRT *));
+extern void ypprog_2 __P((struct svc_req *, register SVCXPRT *));
+extern int _rpc_dtablesize __P((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, msg);
+ else
+ warnx("%s", msg);
+ } else
+ syslog(LOG_ERR, msg);
+}
+
+static void
+yp_svc_run()
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+ struct timeval timeout;
+
+ /* 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 */
+
+ 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:
+ yp_prune_dnsq();
+ break;
+ default:
+ if (FD_ISSET(resfd, &readfds)) {
+ yp_run_dnsq();
+ FD_CLR(resfd, &readfds);
+ }
+ svc_getreqset(&readfds);
+ }
+ if (forked && pid != getpid())
+ _exit(0);
+ }
+}
+
+static void unregister()
+{
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, YPOLDVERS);
+}
+
+static void reaper(sig)
+ 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()
+{
+ 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(argc, argv)
+ 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..76cb5ea
--- /dev/null
+++ b/usr.sbin/ypserv/yp_server.c
@@ -0,0 +1,961 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: yp_server.c,v 1.26 1998/02/11 19:15:32 wpaul Exp $";
+#endif /* not lint */
+
+#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 forked = 0;
+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
+
+/*
+ * 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(rval,addr,transid,prognum,port)
+ 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, (char *)&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, xdr_ypresp_xfr, (char *)&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(fork()) {
+ case 0:
+ {
+ char g[11], t[11], p[11];
+ char ypxfr_command[MAXPATHLEN + 2];
+
+ sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC);
+ sprintf (t, "%u", argp->transid);
+ sprintf (g, "%u", argp->prog);
+ sprintf (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);
+ }
+ forked++;
+ yp_error("ypxfr execl(%s): %s", ypxfr_command, strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ }
+ case -1:
+ yp_error("ypxfr fork(): %s", strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ default:
+ result.xfrstat = YPXFR_SUCC;
+ children++;
+ forked = 0;
+ 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(fork()) {
+ case 0:
+ forked++;
+ break;
+ case -1:
+ yp_error("ypall fork(): %s", strerror(errno));
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ break;
+ default:
+ children++;
+ forked = 0;
+ return (NULL);
+ break;
+ }
+ }
+
+ 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, xdr_my_ypresp_all, (char *)&result);
+
+ /*
+ * Returning NULL prevents the dispatcher from calling
+ * svc_sendreply() since we already did it.
+ */
+ return (NULL);
+}
+
+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((char *)val.valdat_val, (char *)&ypvalbuf,
+ val.valdat_len);
+ ypvalbuf[val.valdat_len] = '\0';
+ result.peer = (char *)&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((char *)val.valdat_val);
+ else
+ result.ordernum = 0;
+
+ return (&result);
+}
+
+static void yp_maplist_free(yp_maplist)
+ 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(domain)
+ 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 = (char *)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((char *)v2_result,
+ (char *)&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((char *)v2_result,
+ (char *)&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((char *)v2_result,
+ (char *)&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..728a181
--- /dev/null
+++ b/usr.sbin/ypserv/yp_svc_udp.c
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include "yp_extern.h"
+
+/*
+ * XXX Must not diverge from what's in src/lib/libc/rpc/svc_udp.c
+ */
+
+/*
+ * kept in xprt->xp_p2
+ */
+struct svcudp_data {
+ u_int su_iosz; /* byte size of send.recv buffer */
+ u_long su_xid; /* transaction id */
+ XDR su_xdrs; /* XDR handle */
+ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
+ char * su_cache; /* cached data, NULL if no cache */
+};
+#define su_data(xprt) ((struct svcudp_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(xprt)
+ SVCXPRT *xprt;
+{
+ struct svcudp_data *su;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ return(su->su_xid);
+}
+
+unsigned long
+svcudp_set_xid(xprt, xid)
+ SVCXPRT *xprt;
+ unsigned long xid;
+{
+ struct svcudp_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..614e3b1
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.8
@@ -0,0 +1,177 @@
+.\" 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.
+.\"
+.\" $Id: ypinit.8,v 1.3 1998/03/23 08:31:04 charnier Exp $
+.\"
+.Dd November 10, 1997
+.Dt YPINIT 8
+.Os FreeBSD 3.0
+.Sh NAME
+.Nm ypinit
+.Nd build and install NIS databases
+.Sh SYNOPSIS
+.Nm ypinit
+.Fl m
+.Op Ar domainname
+.Nm ypinit
+.Fl s
+.Ar master_server
+.Op Ar domainname
+.Nm ypinit
+.Fl u
+.Op Ar domainname
+.Sh DESCRIPTION
+The
+.Nm
+script 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
+command. The script 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
+command. The
+.Nm
+script 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
+command 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 script and
+modify the map list if necessary. Otherwise, indivudual maps can
+be transfered manually from the master using
+.Xr ypxfr 8 .
+.Sh OPTIONS
+.Nm Ypinit
+supports the following options:
+.Bl -tag -width indent
+.It Fl m Op Ar domainname
+Set up a master server. By default, the script 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
+command.
+.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, the script 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.
+.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 Ns )
+.El
+.Sh SEE ALSO
+.Xr yp 4 ,
+.Xr mknetid 8 ,
+.Xr revnetgroup 8 ,
+.Xr yp_mkdb 8 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh HISTORY
+This version of
+.Nm
+is based on the
+.Nm
+script in
+.Bx Open .
+It first appeared in
+.Bx Free
+in
+.Fx 3.0 .
+.Sh AUTHORS
+The original script was written by
+.An Mats O Jansson Aq moj@stacken.kth.se .
+It was modified for
+.Bx Free
+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..e96eb31
--- /dev/null
+++ b/usr.sbin/ypserv/ypinit.sh
@@ -0,0 +1,390 @@
+#!/bin/sh
+# $Id: ypinit.sh,v 1.1 1997/11/10 22:17:13 wpaul Exp $
+#
+# ypinit.sh - setup an 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..5fd7ce6
--- /dev/null
+++ b/usr.sbin/ypserv/ypserv.8
@@ -0,0 +1,426 @@
+.\" 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.
+.\"
+.\" $Id: ypserv.8,v 1.16 1999/02/23 03:49:14 ghelmer Exp $
+.\"
+.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
+program 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
+.Bx Free ,
+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
+program is started by
+.Pa /etc/rc.network
+if it has been enabled in
+.Pa /etc/rc.conf .
+.Sh SPECIAL FEATURES
+There are some problems associated with distributing FreeBSD's password
+database via
+.Tn NIS Ns :
+.Bx Free
+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
+.Bx Free
+version of
+.Nm
+handles the
+.Pa master.passwd.byname
+and
+.Pa master.basswd.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
+.Bx Free Ns 's
+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 .
+.Pp
+.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
+.Bx Free
+system to serve
+.Bx non-Free
+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
+.Bx non-Free
+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
+program also has support for Wietse Venema's
+.Em tcpwrapper
+package, though it is not compiled in by default since
+the
+.Em tcpwrapper
+package is not distributed with
+.Bx Free .
+However, if you have
+.Pa libwrap.a
+and
+.Pa tcpd.h ,
+you can easily recompile
+.Nm
+with them. 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.
+.Bx Free Ns 's
+.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 can not
+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 Ns :
+.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 compatiblity 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.
+.Bx Free Ns 's
+resolver can be configured to do DNS
+queries directly, therefore it is not necessary to enable this
+option when serving only
+.Bx Free
+.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 filesystem.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+the
+.Tn NIS
+maps
+.It Pa /etc/host.conf
+resolver configuration file
+.It Pa /var/yp/securenets
+host access control file
+.El
+.Sh SEE ALSO
+.Xr ypcat 1 ,
+.Xr db 3 ,
+.Xr yp 4 ,
+.Xr ypbind 8 ,
+.Xr ypinit 8 ,
+.Xr rpc.yppasswdd 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..2859110
--- /dev/null
+++ b/usr.sbin/ypset/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# Makefile,v 1.1 1994/08/08 01:10:27 wollman Exp
+
+PROG= ypset
+MAN8= 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..a5a7ecc
--- /dev/null
+++ b/usr.sbin/ypset/ypset.8
@@ -0,0 +1,85 @@
+.\"
+.\" 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.
+.\"
+.\" $Id: ypset.8,v 1.5 1997/10/29 07:26:09 charnier Exp $
+.\"
+.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 ypset
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar server
+.Sh DESCRIPTION
+.Nm Ypset
+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
+.Nm Ypset
+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 4 ,
+.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..1ab8f3d
--- /dev/null
+++ b/usr.sbin/ypset/ypset.c
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.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()
+{
+ fprintf(stderr, "usage: ypset [-h host] [-d domain] server\n");
+ exit(1);
+}
+
+int
+bind_tohost(sin, dom, server)
+struct sockaddr_in *sin;
+char *dom, *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,
+ xdr_ypbind_setdom, &ypsd, 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(argc, argv)
+char **argv;
+{
+ struct sockaddr_in sin;
+ struct hostent *hent;
+ extern char *optarg;
+ extern int optind;
+ char *domainname;
+ int c;
+
+ yp_get_default_domain(&domainname);
+
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0x7f000001);
+
+ while( (c=getopt(argc, argv, "h:d:")) != -1)
+ switch(c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ if( (sin.sin_addr.s_addr=inet_addr(optarg)) == -1) {
+ hent = gethostbyname(optarg);
+ if(hent==NULL)
+ errx(1, "host %s unknown", optarg);
+ bcopy(&hent->h_addr_list[0], &sin.sin_addr,
+ sizeof sin.sin_addr);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if(optind + 1 != argc )
+ usage();
+
+ if (bind_tohost(&sin, domainname, argv[optind]))
+ exit(1);
+ exit(0);
+}
diff --git a/usr.sbin/zic/Arts.htm b/usr.sbin/zic/Arts.htm
new file mode 100644
index 0000000..39dcab7
--- /dev/null
+++ b/usr.sbin/zic/Arts.htm
@@ -0,0 +1,177 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<HTML>
+<HEAD>
+<TITLE>Time and the Arts</TITLE>
+</HEAD>
+<BODY>
+<H1>Time and the Arts</H1>
+<P>
+<H6>
+@(#)Arts.htm 7.18
+</H6>
+</P>
+<PRE>
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:
+--------------------------------------------------------------------------
+Artist: Karrin Allyson
+CD: I Didn't Know About You
+Copyright Date: 1993
+Label: Concord Jazz, Inc.
+ID: CCD-4543
+Track Time: 3:44
+Personnel: Karrin Allyson, vocal
+ Russ Long, piano
+ Gerald Spaits, bass
+ Todd Strait, drums
+Notes: CD notes "additional lyric by Karrin Allyson;
+ arranged by Russ Long and Karrin Allyson"
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||175928">AMG Rating: 3.5 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Kevin Mahogany
+CD: Double Rainbow
+Copyright Date: 1993
+Label: Enja Records
+ID: ENJ-7097 2
+Track Time: 6:27
+Personnel: Kevin Mahogany, vocal
+ Kenny Barron, piano
+ Ray Drummond, bss
+ Ralph Moore, tenor saxophone
+ Lewis Nash, drums
+ADO Rating: 1.5 stars
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||262654">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Joe Williams
+CD: Here's to Life
+Copyright Date: 1994
+Label: Telarc International Corporation
+ID: CD-83357
+Track Time: 3:58
+Personnel: Joe Williams, vocal
+ The Robert Farnon [39 piece] Orchestra
+Notes: On-line information and samples available at
+ <A HREF="http://www.telarc.com/telarc/releases/release.req?ID=83357">http://telarc.dmn.com/telarc/releases/release.req?ID=83357</A>
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cgi-win/amg.exe?sql=1A_IDR|||194434">AMG Rating: 2 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Charles Fambrough
+CD: Keeper of the Spirit
+Copyright Date: 1995
+Label: AudioQuest Music
+ID: AQ-CD1033
+Track Time: 7:07
+Personnel: Charles Fambrough, bass
+ Joel Levine, tenor recorder
+ Edward Simon, piano
+ Lenny White, drums
+ Marion Simon, percussion
+Notes: On-line information and samples available at
+ <A HREF="http://wwmusic.com/~music/audioq/rel/1033.html">http://wwmusic.com/~music/audioq/rel/1033.html</A>
+ADO Rating: 2 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||224430">AMG Rating: unrated</A>
+Penguin Rating: 3 stars
+==========================================================================
+Also of note:
+--------------------------------------------------------------------------
+Artist: Holly Cole Trio
+CD: Blame It On My Youth
+Copyright Date: 1992
+Label: Manhattan
+ID: CDP 7 97349 2
+Total Time: 37:45
+Personnel: Holly Cole, voice
+ Aaron Davis, piano
+ David Piltch, string bass
+Notes: Lyrical reference to "Eastern Standard Time" in
+ Tom Waits' "Purple Avenue"
+ADO Rating: 2.5 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||157959">AMG Rating: 2 stars</A>
+Penguin Rating: unrated
+--------------------------------------------------------------------------
+Artist: Milt Hinton
+CD: Old Man Time
+Copyright Date: 1990
+Label: Chiaroscuro
+ID: CR(D) 310
+Total Time: 149:38 (two CDs)
+Personnel: Milt Hinton, bass
+ Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet
+ Al Grey, trombone
+ Eddie Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+ clarinet and saxophone
+ John Bunch, Red Richards, Norman Simmons, Derek Smith,
+ Ralph Sutton, piano
+ Danny Barker, Al Casey, guitar
+ Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+ drums
+ Lionel Hampton, vibraphone
+ Cab Calloway, Joe Williams, vocal
+ Buck Clayton, arrangements
+Notes: tunes include Old Man Time, Time After Time,
+ Sometimes I'm Happy,
+ A Hot Time in the Old Town Tonight,
+ Four or Five Times, Now's the Time,
+ Time on My Hands, This Time It's Us,
+ and Good Time Charlie
+ On-line samples available at
+ <A HREF="http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html">http://www.globalmusic.com/labels/chiaroscuro/chiaro_cd_gallery.html</A>
+ADO Rating: 3 stars
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||162344">AMG Rating: 4.5 stars</A>
+Penguin Rating: 3 stars
+--------------------------------------------------------------------------
+Artist: Paul Broadbent
+CD: Pacific Standard Time
+Copyright Date: 1995
+Label: Concord Jazz, Inc.
+ID: CCD-4664
+Total Time: 62:42
+Personnel: Paul Broadbent, piano
+ Putter Smith, Bass
+ Frank Gibson, Jr., drums
+Notes: The CD cover features an analemma for equation of time fans
+ADO Rating: 1 star
+<A HREF="http://205.186.189.2/cgi-win/AMG.exe?sql=1A_IDR|||223722">AMG Rating: 3 stars</A>
+Penguin Rating: 3.5 stars
+--------------------------------------------------------------------------
+Artist: Anthony Braxton/Richard Teitelbaum
+CD: Silence/Time Zones
+Copyright Date: 1996
+Label: Black Lion
+ID: BLCD 760221
+Total Time: 72:58
+Personnel: Anthony Braxton, sporanino and alto saxophones,
+ contrebasse clarinet, miscellaneous instruments
+ Leo Smith, trumpet and miscellaneous instruments
+ Leroy Jenkins, violin and miscellaneous instruments
+ Richard Teitelbaum, modular moog and micromoog synthesizer
+ADO Rating: black dot
+<A HREF="http://205.186.189.2/cg/AMG_.exe?sql=A310757">AMG Rating: unrated</A>
+--------------------------------------------------------------------------
+Artist: Jules Verne
+Book: Le Tour du Monde en Quatre-Vingts Jours
+ (Around the World in Eighty Days)
+Notes: Wall-clock time plays a central role in the plot.
+ European readers of the 1870s clearly held the U.S. press in
+ deep contempt; the protagonists cross the U.S. without once
+ reading a paper.
+ An on-line French-language version of the book
+ "with illustrations from the original 1873 French-language edition"
+ is available at
+ <A HREF="http://fourmilab.ch/etexts/www/tdm80j">http://fourmilab.ch/etexts/www/tdm80j</A>
+ An on-line English-language translation of the book is available at
+ <A HREF="http://www.literature.org/Works/Jules-Verne/eighty">http://www.literature.org/Works/Jules-Verne/eighty</A>
+--------------------------------------------------------------------------
+Film: Bell Science - About Time
+Notes: The Frank Baxter/Richard Deacon extravaganza
+ Information on ordering is available at
+ <A HREF="http://www.videoflicks.com/VF/38/038332.htm">http://www.videoflicks.com/VF/38/038332.htm</A>
+--------------------------------------------------------------------------
+The syndicated comic strip "Dilbert" featured an all-too-rare example of
+time zone humor on 1998-03-14.
+</PRE>
+</BODY>
+</HTML>
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
new file mode 100644
index 0000000..9d7e712
--- /dev/null
+++ b/usr.sbin/zic/Makefile
@@ -0,0 +1,4 @@
+# $Id: Makefile,v 1.4 1999/01/21 17:12:48 wollman Exp $
+SUBDIR= zic zdump
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/zic/Makefile.inc b/usr.sbin/zic/Makefile.inc
new file mode 100644
index 0000000..c961f85
--- /dev/null
+++ b/usr.sbin/zic/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id$
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/zic/README b/usr.sbin/zic/README
new file mode 100644
index 0000000..5de3d74
--- /dev/null
+++ b/usr.sbin/zic/README
@@ -0,0 +1,66 @@
+@(#)README 7.10
+
+"What time is it?" -- Richard Deacon as The King
+"Any time you want it to be." -- Frank Baxter as The Scientist
+ (from the Bell System film "About Time")
+
+The 1989 update of the time zone package featured
+
+* POSIXization (including interpretation of POSIX-style TZ environment
+ variables, provided by Guy Harris),
+* ANSIfication (including versions of "mktime" and "difftime"),
+* SVIDulation (an "altzone" variable)
+* MACHination (the "gtime" function)
+* corrections to some time zone data (including corrections to the rules
+ for Great Britain and New Zealand)
+* reference data from the United States Naval Observatory for folks who
+ want to do additional time zones
+* and the 1989 data for Saudi Arabia.
+
+(Since this code will be treated as "part of the implementation" in some places
+and as "part of the application" in others, there's no good way to name
+functions, such as timegm, that are not part of the proposed ANSI C standard;
+such functions have kept their old, underscore-free names in this update.)
+
+And the "dysize" function has disappeared; it was present to allow compilation
+of the "date" command on old BSD systems, and a version of "date" is now
+provided in the package. The "date" command is not created when you "make all"
+since it may lack options provided by the version distributed with your
+operating system, or may not interact with the system in the same way the
+native version does.
+
+Since POSIX frowns on correct leap second handling, the default behavior of
+the "zic" command (in the absence of a "-L" option) has been changed to omit
+leap second information from its output files.
+
+Be sure to read the comments in "Makefile" and make any changes
+needed to make things right for your system.
+
+To use the new functions, use a "-ltz" option when compiling or linking.
+
+Historical local time information has been included here not because it
+is particularly useful, but rather to:
+
+* give an idea of the variety of local time rules that have
+ existed in the past and thus an idea of the variety that may be
+ expected in the future;
+
+* provide a test of the generality of the local time rule description
+ system.
+
+The information in the time zone data files is by no means authoritative;
+if you know that the rules are different from those in a file, by all means
+feel free to change file (and please send the changed version to
+tz@elsie.nci.nih.gov for use in the future). Europeans take note!
+
+Thanks to these Timezone Caballeros who've made major contributions to the
+time conversion package: Keith Bostic; Bob Devine; Paul Eggert; Robert Elz;
+Guy Harris; Mark Horton; John Mackin; and Bradley White. Thanks also to
+Michael Bloom, Art Neilson, Stephen Prince, John Sovereign, and Frank Wales
+for testing work, and to Gwillim Law for checking local mean time data.
+None of them are responsible for remaining errors.
+
+Look in the ~ftp/pub directory of elsie.nci.nih.gov
+for updated versions of these files.
+
+Please send comments or information to tz@elsie.nci.nih.gov.
diff --git a/usr.sbin/zic/Theory b/usr.sbin/zic/Theory
new file mode 100644
index 0000000..1c43133
--- /dev/null
+++ b/usr.sbin/zic/Theory
@@ -0,0 +1,285 @@
+@(#)Theory 7.6
+
+
+----- Outline -----
+
+ Time and date functions
+ Names of time zone regions
+ Time zone abbreviations
+
+
+----- Time and date functions -----
+
+These time and date functions are upwards compatible with POSIX.1,
+an international standard for Unix-like systems.
+As of this writing, the current edition of POSIX.1 is:
+
+ Information technology --Portable Operating System Interface (POSIX (R))
+ -- Part 1: System Application Program Interface (API) [C Language]
+ ISO/IEC 9945-1:1996
+ ANSI/IEEE Std 1003.1, 1996 Edition
+ 1996-07-12
+
+POSIX.1 has the following properties and limitations.
+
+* In POSIX.1, time display in a process is controlled by the
+ environment variable TZ. Unfortunately, the POSIX.1 TZ string takes
+ a form that is hard to describe and is error-prone in practice.
+ Also, POSIX.1 TZ strings can't deal with other (for example, Israeli)
+ daylight saving time rules, or situations where more than two
+ time zone abbreviations are used in an area.
+
+ The POSIX.1 TZ string takes the following form:
+
+ stdoffset[dst[offset],date[/time],date[/time]]
+
+ where:
+
+ std and dst
+ are 3 or more characters specifying the standard
+ and daylight saving time (DST) zone names.
+ offset
+ is of the form `[-]hh:[mm[:ss]]' and specifies the
+ offset west of UTC. The default DST offset is one hour
+ ahead of standard time.
+ date[/time],date[/time]
+ specifies the beginning and end of DST. If this is absent,
+ the system supplies its own rules for DST, and these can
+ differ from year to year; typically US DST rules are used.
+ time
+ takes the form `hh:[mm[:ss]]' and defaults to 02:00.
+ date
+ takes one of the following forms:
+ Jn (1<=n<=365)
+ origin-1 day number not counting February 29
+ n (0<=n<=365)
+ origin-0 day number counting February 29 if present
+ Mm.n.d (0[Sunday]<=d<=6[Saturday], 1<=n<=5, 1<=m<=12)
+ for the dth day of week n of month m of the year,
+ where week 1 is the first week in which day d appears,
+ and `5' stands for the last week in which day d appears
+ (which may be either the 4th or 5th week).
+
+* In POSIX.1, when a TZ value like "EST5EDT" is parsed,
+ typically the current US DST rules are used,
+ but this means that the US DST rules are compiled into each program
+ that does time conversion. This means that when US time conversion
+ rules change (as in the United States in 1987), all programs that
+ do time conversion must be recompiled to ensure proper results.
+
+* In POSIX.1, there's no tamper-proof way for a process to learn the
+ system's best idea of local wall clock. (This is important for
+ applications that an administrator wants used only at certain times--
+ without regard to whether the user has fiddled the "TZ" environment
+ variable. While an administrator can "do everything in UTC" to get
+ around the problem, doing so is inconvenient and precludes handling
+ daylight saving time shifts--as might be required to limit phone
+ calls to off-peak hours.)
+
+* POSIX.1 requires that systems ignore leap seconds.
+
+These are the extensions that have been made to the POSIX.1 functions:
+
+* The "TZ" environment variable is used in generating the name of a file
+ from which time zone information is read (or is interpreted a la
+ POSIX); "TZ" is no longer constrained to be a three-letter time zone
+ name followed by a number of hours and an optional three-letter
+ daylight time zone name. The daylight saving time rules to be used
+ for a particular time zone are encoded in the time zone file;
+ the format of the file allows U.S., Australian, and other rules to be
+ encoded, and allows for situations where more than two time zone
+ abbreviations are used.
+
+ It was recognized that allowing the "TZ" environment variable to
+ take on values such as "America/New_York" might cause "old" programs
+ (that expect "TZ" to have a certain form) to operate incorrectly;
+ consideration was given to using some other environment variable
+ (for example, "TIMEZONE") to hold the string used to generate the
+ time zone information file name. In the end, however, it was decided
+ to continue using "TZ": it is widely used for time zone purposes;
+ separately maintaining both "TZ" and "TIMEZONE" seemed a nuisance;
+ and systems where "new" forms of "TZ" might cause problems can simply
+ use TZ values such as "EST5EDT" which can be used both by
+ "new" programs (a la POSIX) and "old" programs (as zone names and
+ offsets).
+
+* To handle places where more than two time zone abbreviations are used,
+ the functions "localtime" and "gmtime" set tzname[tmp->tm_isdst]
+ (where "tmp" is the value the function returns) to the time zone
+ abbreviation to be used. This differs from POSIX.1, where the elements
+ of tzname are only changed as a result of calls to tzset.
+
+* Since the "TZ" environment variable can now be used to control time
+ conversion, the "daylight" and "timezone" variables are no longer
+ needed. (These variables are defined and set by "tzset"; however, their
+ values will not be used by "localtime.")
+
+* The "localtime" function has been set up to deliver correct results
+ for near-minimum or near-maximum time_t values. (A comment in the
+ source code tells how to get compatibly wrong results).
+
+* A function "tzsetwall" has been added to arrange for the system's
+ best approximation to local wall clock time to be delivered by
+ subsequent calls to "localtime." Source code for portable
+ applications that "must" run on local wall clock time should call
+ "tzsetwall();" if such code is moved to "old" systems that don't
+ provide tzsetwall, you won't be able to generate an executable program.
+ (These time zone functions also arrange for local wall clock time to be
+ used if tzset is called--directly or indirectly--and there's no "TZ"
+ environment variable; portable applications should not, however, rely
+ on this behavior since it's not the way SVR2 systems behave.)
+
+* These functions can account for leap seconds, thanks to Bradley White
+ (bww@k.cs.cmu.edu).
+
+Points of interest to folks with other systems:
+
+* This package is already part of many POSIX-compliant hosts,
+ including BSD, HP, Linux, Network Appliance, SCO, SGI, and Sun.
+ On such hosts, the primary use of this package
+ is to update obsolete time zone rule tables.
+ To do this, you may need to compile the time zone compiler
+ `zic' supplied with this package instead of using the system `zic',
+ since the format of zic's input changed slightly in late 1994,
+ and many vendors still do not support the new input format.
+
+* The Unix Version 7 "timezone" function is not present in this package;
+ it's impossible to reliably map timezone's arguments (a "minutes west
+ of GMT" value and a "daylight saving time in effect" flag) to a
+ time zone abbreviation, and we refuse to guess.
+ Programs that in the past used the timezone function may now examine
+ tzname[localtime(&clock)->tm_isdst] to learn the correct time
+ zone abbreviation to use. Alternatively, use
+ localtime(&clock)->tm_zone if this has been enabled.
+
+* The 4.2BSD gettimeofday function is not used in this package.
+ This formerly let users obtain the current UTC offset and DST flag,
+ but this functionality was removed in later versions of BSD.
+
+* In SVR2, time conversion fails for near-minimum or near-maximum
+ time_t values when doing conversions for places that don't use UTC.
+ This package takes care to do these conversions correctly.
+
+The functions that are conditionally compiled if STD_INSPIRED is defined
+should, at this point, be looked on primarily as food for thought. They are
+not in any sense "standard compatible"--some are not, in fact, specified in
+*any* standard. They do, however, represent responses of various authors to
+standardization proposals.
+
+Other time conversion proposals, in particular the one developed by folks at
+Hewlett Packard, offer a wider selection of functions that provide capabilities
+beyond those provided here. The absence of such functions from this package
+is not meant to discourage the development, standardization, or use of such
+functions. Rather, their absence reflects the decision to make this package
+contain valid extensions to POSIX.1, to ensure its broad
+acceptability. If more powerful time conversion functions can be standardized,
+so much the better.
+
+
+----- Names of time zone rule files -----
+
+The names of this package's installed time zone rule files are chosen to
+help minimize possible future incompatibilities due to political events.
+Ordinarily, names of countries are not used, to avoid incompatibilities
+when countries change their name (e.g. Zaire->Congo) or
+when locations change countries (e.g. Hong Kong from UK colony to China).
+
+Names normally have the form AREA/LOCATION, where AREA is the name
+of a continent or ocean, and LOCATION is the name of a specific
+location within that region. North and South America share the same
+area, `America'. Typical names are `Africa/Cairo', `America/New_York',
+and `Pacific/Honolulu'.
+
+Here are the general rules used for choosing location names,
+in decreasing order of importance:
+
+ Use only valid Posix file names. Use only Ascii letters, digits, `.',
+ `-' and `_'. Do not exceed 14 characters or start with `-'.
+ E.g. prefer `Brunei' to `Bandar_Seri_Begawan'.
+ Include at least one location per time zone rule set per country.
+ One such location is enough.
+ If all the clocks in a country's region have agreed since 1970,
+ don't bother to include more than one location
+ even if subregions' clocks disagreed before 1970.
+ Otherwise these tables would become annoyingly large.
+ If a name is ambiguous, use a less ambiguous alternative;
+ e.g. many cities are named San Jose and Georgetown, so
+ prefer `Costa_Rica' to `San_Jose' and `Guyana' to `Georgetown'.
+ Keep locations compact. Use cities or small islands, not countries
+ or regions, so that any future time zone changes do not split
+ locations into different time zones. E.g. prefer `Paris'
+ to `France', since France has had multiple time zones.
+ Use traditional English spelling, e.g. prefer `Rome' to `Roma', and
+ prefer `Athens' to the true name (which uses Greek letters).
+ The Posix file name restrictions encourage this rule.
+ Use the most populous among locations in a country's time zone,
+ e.g. prefer `Shanghai' to `Beijing'. Among locations with
+ similar populations, pick the best-known location,
+ e.g. prefer `Rome' to `Milan'.
+ Use the singular form, e.g. prefer `Canary' to `Canaries'.
+ Omit common suffixes like `_Islands' and `_City', unless that
+ would lead to ambiguity. E.g. prefer `Cayman' to
+ `Cayman_Islands' and `Guatemala' to `Guatemala_City',
+ but prefer `Mexico_City' to `Mexico' because the country
+ of Mexico has several time zones.
+ Use `_' to represent a space.
+ Omit `.' from abbreviations in names, e.g. prefer `St_Helena'
+ to `St._Helena'.
+
+The file `zone.tab' lists the geographical locations used to name
+time zone rule files.
+
+Older versions of this package used a different naming scheme,
+and these older names are still supported.
+See the file `backwards' for most of these older names
+(e.g. `US/Eastern' instead of `America/New_York').
+The other old-fashioned names still supported are
+`WET', `CET', `MET', `EET' (see the file `europe'),
+and `Factory' (see the file `factory').
+
+
+----- Time zone abbreviations -----
+
+When this package is installed, it generates time zone abbreviations
+like `EST' to be compatible with human tradition and POSIX.1.
+Here are the general rules used for choosing time zone abbreviations,
+in decreasing order of importance:
+
+ Use abbreviations that consist of 3 or more upper-case Ascii letters,
+ except use "___" for locations while uninhabited.
+ Posix.1 requires at least 3 characters, and the restriction to
+ upper-case Ascii letters follows most traditions.
+ Previous editions of this database also used characters like
+ ' ' and '?', but these characters have a special meaning to
+ the shell and cause commands like
+ set `date`
+ to have unexpected effects. In theory, the character set could
+ be !%./@A-Z^_a-z{}, but these tables use only upper-case
+ Ascii letters (and "___").
+ Use abbreviations that are in common use among English-speakers,
+ e.g. `EST' for Eastern Standard Time in North America.
+ We assume that applications translate them to other languages
+ as part of the normal localization process; for example,
+ a French application might translate `EST' to `HNE'.
+ For zones whose times are taken from a city's longitude, use the
+ traditional xMT notation, e.g. `PMT' for Paris Mean Time.
+ The only name like this in current use is `GMT'.
+ If there is no common English abbreviation, abbreviate the English
+ translation of the usual phrase used by native speakers.
+ If this is not available or is a phrase mentioning the country
+ (e.g. ``Cape Verde Time''), then:
+
+ When a country has a single or principal time zone region,
+ append `T' to the country's ISO code, e.g. `CVT' for
+ Cape Verde Time. For summer time append `ST';
+ for double summer time append `DST'; etc.
+ When a country has multiple time zones, take the first three
+ letters of an English place name identifying each zone
+ and then append `T', `ST', etc. as before;
+ e.g. `VLAST' for VLAdivostok Summer Time.
+
+Application writers should note that these abbreviations are ambiguous
+in practice: e.g. `EST' has a different meaning in Australia than
+it does in the United States. In new applications, it's often better
+to use numeric UTC offsets like `-0500' instead of time zone
+abbreviations like `EST'; this avoids the ambiguity.
diff --git a/usr.sbin/zic/WWW.htm b/usr.sbin/zic/WWW.htm
new file mode 100644
index 0000000..2151c89
--- /dev/null
+++ b/usr.sbin/zic/WWW.htm
@@ -0,0 +1,97 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
+<HTML>
+<HEAD>
+<TITLE>Sources for Time Zone and Daylight Saving Time Data</TITLE>
+</HEAD>
+<BODY>
+<H1>Sources for Time Zone and Daylight Saving Time Data</H1>
+<P>
+<H6>
+@(#)WWW.htm 7.16
+</H6>
+<H2>Paul Eggert writes:</H2><P>
+The public-domain tz database contains code and data
+that represent the history of local time
+for many representative locations around the globe.
+It is updated periodically to reflect changes made by political bodies
+to UTC offsets and daylight-saving rules.
+This database (often called <samp>zoneinfo</samp>)
+is used by several implementations,
+including BSD, DJGPP, GNU/Linux, HP-UX, IRIX, Solaris, and UnixWare.
+In the tz database's
+<A HREF="ftp://elsie.nci.nih.gov/pub/">FTP distribution</A>,
+the code is in the file <samp>tzcode<var>C</var>.tar.gz</samp>,
+where <samp><var>C</var></samp> is the code's version;
+similarly, the data are in <samp>tzdata<var>D</var>.tar.gz</samp>,
+where <samp><var>D</var></samp> is the data's version.
+<P>
+The <A HREF="ftp://ftp.gnu.org/pub/gnu/GETTING.GNU.SOFTWARE">GNU C Library</A>
+has an independent, thread-safe implementation of
+a time zone file reader.
+This library is freely available under the GNU Library General Public License,
+and is widely used in GNU/Linux systems.
+<P>
+The Web has several other sources for time zone and daylight saving time data.
+Here are some recent links that may be of interest.
+<UL>
+<LI><A HREF="http://www.bsdi.com/date">Date and Time Gateway</A>
+is 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.
+<LI><A HREF="http://www.worldtime.com/">WORLDTIME: interactive atlas,
+time info, public holidays</A>
+contains information on local time, sunrise and sunset,
+and public holidays in several hundred cities around the world.
+<LI><A HREF="http://www.hilink.com.au/times/">Local Times Around the World</A>
+is a text-based system containing links to local time servers
+throughout the world; though the coverage is limited,
+the live data provide a nice way to check one's tables.
+<LI><A HREF="http://tycho.usno.navy.mil/tzones.html">World Time Zones</A>
+contains US Naval Observatory data, used as the source
+for the <samp>usno*</samp> files.
+<LI>The United States Central Intelligence agency publishes time zone maps;
+the
+<A HREF="http://www.lib.utexas.edu/Libs/PCL/Map_collection/world_maps.html">
+Perry-Casta&ntilde;eda Library Map Collection</A>
+of the University of Texas at Austin has on-line copies of
+the 1995 and 1997 editions.
+The pictorial quality is good,
+but the maps do not indicate summer time,
+and parts of the data are a few years out of date.
+<LI><A HREF="http://pisolo.cstv.to.cnr.it/toi/uk/toi.html">The
+Time of Internet</A>
+contains good descriptions of Time Zones and daylight saving time,
+with diagrams.
+The time zone map is out of date, however.
+<LI><A HREF="http://ecco.bsee.swin.edu.au/chronos/GMT-explained.html">A
+Few Facts Concerning GMT, UT, and the RGO</A>
+answers questions like ``What is the difference between GMT and UTC?''
+<LI><A HREF="http://www.energy.ca.gov/daylightsaving.html">Daylight
+Saving Time -- Saving Time, Saving Energy</A>
+is a history of DST in the US.
+<LI><A HREF="http://dir.yahoo.com/Science/Measurements_and_Units/Time/Time_Zones/">Yahoo! - Science:Measurements and Units:Time:Time Zones</A>
+is where the famous Internet indexing service Yahoo! collects its time zone
+info.
+<LI>The <A HREF="http://www.iata.org/">International Air Transport Association</A>
+publishes the IATA Standard Schedules Information Manual (SSIM),
+which gives current time zone rules for
+all the airports served by commercial aviation.
+<LI><A HREF="http://hpiers.obspm.fr/webiers/results/bul/README.html">Bulletins
+of IERS</A> contains official publications of the
+International Earth Rotation Service, the committee that decides
+when leap seconds occur.
+</UL>
+<P>
+-- <A HREF="mailto:eggert@twinsun.com">eggert@twinsun.com</A>
+(1998-09-22)
+</P>
+<H2>Arthur David Olson writes:</H2><P>
+A good source of information about ISO 8601 seems to be
+<A HREF="http://www.cl.cam.ac.uk/~mgk25/iso-time.html">International
+Standard Date and Time Notation</A>
+maintained by Markus Kuhn.
+<P>
+-- <A HREF="mailto:arthur_david_olson@nih.gov">arthur_david_olson@nih.gov</A>
+(1996-01-04)
+</P>
+</BODY>
+</HTML>
diff --git a/usr.sbin/zic/ialloc.c b/usr.sbin/zic/ialloc.c
new file mode 100644
index 0000000..31737f5
--- /dev/null
+++ b/usr.sbin/zic/ialloc.c
@@ -0,0 +1,86 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)ialloc.c 8.29";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: ialloc.c,v 1.4 1999/01/21 17:12:49 wollman Exp $";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+#define nonzero(n) (((n) == 0) ? 1 : (n))
+
+char *
+imalloc(n)
+const int n;
+{
+ return malloc((size_t) nonzero(n));
+}
+
+char *
+icalloc(nelem, elsize)
+int nelem;
+int elsize;
+{
+ if (nelem == 0 || elsize == 0)
+ nelem = elsize = 1;
+ return calloc((size_t) nelem, (size_t) elsize);
+}
+
+void *
+irealloc(pointer, size)
+void * const pointer;
+const int size;
+{
+ if (pointer == NULL)
+ return imalloc(size);
+ return realloc((void *) pointer, (size_t) nonzero(size));
+}
+
+char *
+icatalloc(old, new)
+char * const old;
+const char * const new;
+{
+ register char * result;
+ register int oldsize, newsize;
+
+ newsize = (new == NULL) ? 0 : strlen(new);
+ if (old == NULL)
+ oldsize = 0;
+ else if (newsize == 0)
+ return old;
+ else oldsize = strlen(old);
+ if ((result = irealloc(old, oldsize + newsize + 1)) != NULL)
+ if (new != NULL)
+ (void) strcpy(result + oldsize, new);
+ return result;
+}
+
+char *
+icpyalloc(string)
+const char * const string;
+{
+ return icatalloc((char *) NULL, string);
+}
+
+void
+ifree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
+
+void
+icfree(p)
+char * const p;
+{
+ if (p != NULL)
+ (void) free(p);
+}
diff --git a/usr.sbin/zic/private.h b/usr.sbin/zic/private.h
new file mode 100644
index 0000000..11a80e4
--- /dev/null
+++ b/usr.sbin/zic/private.h
@@ -0,0 +1,181 @@
+#ifndef PRIVATE_H
+
+#define PRIVATE_H
+
+/*
+** This file is in the public domain, so clarified as of
+** 1996-06-05 by Arthur David Olson (arthur_david_olson@nih.gov).
+*/
+
+/*
+ * FreeBSD modifications: separate libc's privates from zic's.
+ * This makes it easier when we need to update one but not the other.
+ * I have removed all of the ifdef spaghetti which is not relevant to
+ * zic from this file.
+ *
+ * $Id: private.h,v 1.2 1999/01/21 17:12:49 wollman Exp $
+ */
+
+/*
+** This header is for use ONLY with the time conversion code.
+** There is no guarantee that it will remain unchanged,
+** or that it will remain at all.
+** Do NOT copy it to any system include directory.
+** Thank you!
+*/
+
+/*
+** ID
+*/
+
+#ifndef lint
+#ifndef NOID
+static char privatehid[] = "@(#)private.h 7.48";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+/*
+** Defaults for preprocessor symbols.
+** You can override these in your C compiler options, e.g. `-DHAVE_ADJTIME=0'.
+*/
+
+#ifndef HAVE_GETTEXT
+#define HAVE_GETTEXT 0
+#endif /* !defined HAVE_GETTEXT */
+
+#ifndef HAVE_STRERROR
+#define HAVE_STRERROR 1
+#endif /* !defined HAVE_STRERROR */
+
+#ifndef HAVE_SYMLINK
+#define HAVE_SYMLINK 1
+#endif /* !defined HAVE_SYMLINK */
+
+#ifndef HAVE_UNISTD_H
+#define HAVE_UNISTD_H 1
+#endif /* !defined HAVE_UNISTD_H */
+
+/*
+** Nested includes
+*/
+
+#include "sys/types.h" /* for time_t */
+#include "stdio.h"
+#include "errno.h"
+#include "string.h"
+#include "limits.h" /* for CHAR_BIT */
+#include "time.h"
+#include "stdlib.h"
+
+#if HAVE_GETTEXT - 0
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#if HAVE_UNISTD_H - 0
+#include "unistd.h" /* for F_OK and R_OK */
+#endif /* HAVE_UNISTD_H - 0 */
+
+#if !(HAVE_UNISTD_H - 0)
+#ifndef F_OK
+#define F_OK 0
+#endif /* !defined F_OK */
+#ifndef R_OK
+#define R_OK 4
+#endif /* !defined R_OK */
+#endif /* !(HAVE_UNISTD_H - 0) */
+
+/* Unlike <ctype.h>'s isdigit, this also works if c < 0 | c > UCHAR_MAX. */
+#define is_digit(c) ((unsigned)(c) - '0' <= 9)
+
+#define P(x) x
+
+/*
+** Private function declarations.
+*/
+char * icalloc P((int nelem, int elsize));
+char * icatalloc P((char * old, const char * new));
+char * icpyalloc P((const char * string));
+char * imalloc P((int n));
+void * irealloc P((void * pointer, int size));
+void icfree P((char * pointer));
+void ifree P((char * pointer));
+char * scheck P((const char *string, const char *format));
+
+/*
+** Finally, some convenience items.
+*/
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef TYPE_BIT
+#define TYPE_BIT(type) (sizeof (type) * CHAR_BIT)
+#endif /* !defined TYPE_BIT */
+
+#ifndef TYPE_SIGNED
+#define TYPE_SIGNED(type) (((type) -1) < 0)
+#endif /* !defined TYPE_SIGNED */
+
+#ifndef INT_STRLEN_MAXIMUM
+/*
+** 302 / 1000 is log10(2.0) rounded up.
+** Subtract one for the sign bit if the type is signed;
+** add one for integer division truncation;
+** add one more for a minus sign if the type is signed.
+*/
+#define INT_STRLEN_MAXIMUM(type) \
+ ((TYPE_BIT(type) - TYPE_SIGNED(type)) * 302 / 1000 + 1 + TYPE_SIGNED(type))
+#endif /* !defined INT_STRLEN_MAXIMUM */
+
+/*
+** INITIALIZE(x)
+*/
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
+
+#endif /* !defined PRIVATE_H */
diff --git a/usr.sbin/zic/scheck.c b/usr.sbin/zic/scheck.c
new file mode 100644
index 0000000..d268a11
--- /dev/null
+++ b/usr.sbin/zic/scheck.c
@@ -0,0 +1,64 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)scheck.c 8.15";
+#endif /* !defined lint */
+#endif /* !defined NOID */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: scheck.c,v 1.3 1999/01/21 17:12:49 wollman Exp $";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+char *
+scheck(string, format)
+const char * const string;
+const char * const format;
+{
+ register char * fbuf;
+ register const char * fp;
+ register char * tp;
+ register int c;
+ register char * result;
+ char dummy;
+ static char nada;
+
+ result = &nada;
+ if (string == NULL || format == NULL)
+ return result;
+ fbuf = imalloc((int) (2 * strlen(format) + 4));
+ if (fbuf == NULL)
+ return result;
+ fp = format;
+ tp = fbuf;
+ while ((*tp++ = c = *fp++) != '\0') {
+ if (c != '%')
+ continue;
+ if (*fp == '%') {
+ *tp++ = *fp++;
+ continue;
+ }
+ *tp++ = '*';
+ if (*fp == '*')
+ ++fp;
+ while (is_digit(*fp))
+ *tp++ = *fp++;
+ if (*fp == 'l' || *fp == 'h')
+ *tp++ = *fp++;
+ else if (*fp == '[')
+ do *tp++ = *fp++;
+ while (*fp != '\0' && *fp != ']');
+ if ((*tp++ = *fp++) == '\0')
+ break;
+ }
+ *(tp - 1) = '%';
+ *tp++ = 'c';
+ *tp = '\0';
+ if (sscanf(string, fbuf, &dummy) != 1)
+ result = (char *) format;
+ ifree(fbuf);
+ return result;
+}
diff --git a/usr.sbin/zic/zdump.8 b/usr.sbin/zic/zdump.8
new file mode 100644
index 0000000..f9f4f95
--- /dev/null
+++ b/usr.sbin/zic/zdump.8
@@ -0,0 +1,44 @@
+.\"
+.\" @(#)zdump.8 7.3
+.\" $Id$
+.\"
+.Dd September 13, 1994
+.Dt ZDUMP 8
+.Os
+.Sh NAME
+.Nm zdump
+.Nd timezone dumper
+.Sh SYNOPSIS
+.Nm zdump
+.Op Fl v
+.Op Fl c Ar cutoffyear
+.Op Ar zonename ...
+.Sh DESCRIPTION
+.Nm Zdump
+prints the current time in each
+.Ar zonename
+named on the command line.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl v
+For each
+.Ar zonename
+on the command line,
+print the time at the lowest possible time value,
+the time one day after the lowest possible time value,
+the times both one second before and exactly at
+each detected time discontinuity,
+the time at one day less than the highest possible time value,
+and the time at the highest possible time value,
+Each line ends with
+.Em isdst=1
+if the given time is Daylight Saving Time or
+.Em isdst=0
+otherwise.
+.It Fl c Ar cutoffyear
+Cut off the verbose output near the start of the given year.
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zic 8
diff --git a/usr.sbin/zic/zdump.c b/usr.sbin/zic/zdump.c
new file mode 100644
index 0000000..9ee661d
--- /dev/null
+++ b/usr.sbin/zic/zdump.c
@@ -0,0 +1,375 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)zdump.c 7.28";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: zdump.c,v 1.5 1999/01/21 17:46:19 wollman Exp $";
+#endif /* not lint */
+
+/*
+** This code has been made independent of the rest of the time
+** conversion package to increase confidence in the verification it provides.
+** You can use this code to help in verifying other implementations.
+*/
+
+#include <err.h>
+#include <stdio.h> /* for stdout, stderr */
+#include <stdlib.h> /* for exit, malloc, atoi */
+#include <string.h> /* for strcpy */
+#include <sys/types.h> /* for time_t */
+#include <time.h> /* for struct tm */
+#include <unistd.h>
+
+#ifndef MAX_STRING_LENGTH
+#define MAX_STRING_LENGTH 1024
+#endif /* !defined MAX_STRING_LENGTH */
+
+#ifndef TRUE
+#define TRUE 1
+#endif /* !defined TRUE */
+
+#ifndef FALSE
+#define FALSE 0
+#endif /* !defined FALSE */
+
+#ifndef EXIT_SUCCESS
+#define EXIT_SUCCESS 0
+#endif /* !defined EXIT_SUCCESS */
+
+#ifndef EXIT_FAILURE
+#define EXIT_FAILURE 1
+#endif /* !defined EXIT_FAILURE */
+
+#ifndef SECSPERMIN
+#define SECSPERMIN 60
+#endif /* !defined SECSPERMIN */
+
+#ifndef MINSPERHOUR
+#define MINSPERHOUR 60
+#endif /* !defined MINSPERHOUR */
+
+#ifndef SECSPERHOUR
+#define SECSPERHOUR (SECSPERMIN * MINSPERHOUR)
+#endif /* !defined SECSPERHOUR */
+
+#ifndef HOURSPERDAY
+#define HOURSPERDAY 24
+#endif /* !defined HOURSPERDAY */
+
+#ifndef EPOCH_YEAR
+#define EPOCH_YEAR 1970
+#endif /* !defined EPOCH_YEAR */
+
+#ifndef TM_YEAR_BASE
+#define TM_YEAR_BASE 1900
+#endif /* !defined TM_YEAR_BASE */
+
+#ifndef DAYSPERNYEAR
+#define DAYSPERNYEAR 365
+#endif /* !defined DAYSPERNYEAR */
+
+#ifndef isleap
+#define isleap(y) ((((y) % 4) == 0 && ((y) % 100) != 0) || ((y) % 400) == 0)
+#endif /* !defined isleap */
+
+#if HAVE_GETTEXT - 0
+#include "locale.h" /* for setlocale */
+#include "libintl.h"
+#endif /* HAVE_GETTEXT - 0 */
+
+#ifndef GNUC_or_lint
+#ifdef lint
+#define GNUC_or_lint
+#endif /* defined lint */
+#ifndef lint
+#ifdef __GNUC__
+#define GNUC_or_lint
+#endif /* defined __GNUC__ */
+#endif /* !defined lint */
+#endif /* !defined GNUC_or_lint */
+
+#ifndef INITIALIZE
+#ifdef GNUC_or_lint
+#define INITIALIZE(x) ((x) = 0)
+#endif /* defined GNUC_or_lint */
+#ifndef GNUC_or_lint
+#define INITIALIZE(x)
+#endif /* !defined GNUC_or_lint */
+#endif /* !defined INITIALIZE */
+
+/*
+** For the benefit of GNU folk...
+** `_(MSGID)' uses the current locale's message library string for MSGID.
+** The default is to use gettext if available, and use MSGID otherwise.
+*/
+
+#ifndef _
+#if HAVE_GETTEXT - 0
+#define _(msgid) gettext(msgid)
+#else /* !(HAVE_GETTEXT - 0) */
+#define _(msgid) msgid
+#endif /* !(HAVE_GETTEXT - 0) */
+#endif /* !defined _ */
+
+#ifndef TZ_DOMAIN
+#define TZ_DOMAIN "tz"
+#endif /* !defined TZ_DOMAIN */
+
+#ifndef P
+#ifdef __STDC__
+#define P(x) x
+#endif /* defined __STDC__ */
+#ifndef __STDC__
+#define P(x) ()
+#endif /* !defined __STDC__ */
+#endif /* !defined P */
+
+extern char ** environ;
+extern char * tzname[2];
+
+static char * abbr P((struct tm * tmp));
+static long delta P((struct tm * newp, struct tm * oldp));
+static time_t hunt P((char * name, time_t lot, time_t hit));
+static size_t longest;
+static char * progname;
+static void show P((char * zone, time_t t, int v));
+static void usage(void);
+
+int
+main(argc, argv)
+int argc;
+char * argv[];
+{
+ register int i;
+ register int c;
+ register int vflag;
+ register char * cutoff;
+ register int cutyear;
+ register long cuttime;
+ char ** fakeenv;
+ time_t now;
+ time_t t;
+ time_t newt;
+ time_t hibit;
+ struct tm tm;
+ struct tm newtm;
+
+ INITIALIZE(cuttime);
+#if HAVE_GETTEXT - 0
+ (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+ (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined(TEXTDOMAINDIR) */
+ (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
+ vflag = 0;
+ cutoff = NULL;
+ while ((c = getopt(argc, argv, "c:v")) == 'c' || c == 'v')
+ if (c == 'v')
+ vflag = 1;
+ else cutoff = optarg;
+ if ((c != EOF && c != -1) ||
+ (optind == argc - 1 && strcmp(argv[optind], "=") == 0)) {
+ usage();
+ }
+ if (cutoff != NULL) {
+ int y;
+
+ cutyear = atoi(cutoff);
+ cuttime = 0;
+ for (y = EPOCH_YEAR; y < cutyear; ++y)
+ cuttime += DAYSPERNYEAR + isleap(y);
+ cuttime *= SECSPERHOUR * HOURSPERDAY;
+ }
+ (void) time(&now);
+ longest = 0;
+ for (i = optind; i < argc; ++i)
+ if (strlen(argv[i]) > longest)
+ longest = strlen(argv[i]);
+ for (hibit = 1; (hibit << 1) != 0; hibit <<= 1)
+ continue;
+ {
+ register int from;
+ register int to;
+
+ for (i = 0; environ[i] != NULL; ++i)
+ continue;
+ fakeenv = (char **) malloc((size_t) ((i + 2) *
+ sizeof *fakeenv));
+ if (fakeenv == NULL ||
+ (fakeenv[0] = (char *) malloc((size_t) (longest +
+ 4))) == NULL)
+ errx(EXIT_FAILURE,
+ _("malloc() failed"));
+ to = 0;
+ (void) strcpy(fakeenv[to++], "TZ=");
+ for (from = 0; environ[from] != NULL; ++from)
+ if (strncmp(environ[from], "TZ=", 3) != 0)
+ fakeenv[to++] = environ[from];
+ fakeenv[to] = NULL;
+ environ = fakeenv;
+ }
+ for (i = optind; i < argc; ++i) {
+ static char buf[MAX_STRING_LENGTH];
+
+ (void) strcpy(&fakeenv[0][3], argv[i]);
+ if (!vflag) {
+ show(argv[i], now, FALSE);
+ continue;
+ }
+ /*
+ ** Get lowest value of t.
+ */
+ t = hibit;
+ if (t > 0) /* time_t is unsigned */
+ t = 0;
+ show(argv[i], t, TRUE);
+ t += SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ tm = *localtime(&t);
+ (void) strncpy(buf, abbr(&tm), (sizeof buf) - 1);
+ for ( ; ; ) {
+ if (cutoff != NULL && t >= cuttime)
+ break;
+ newt = t + SECSPERHOUR * 12;
+ if (cutoff != NULL && newt >= cuttime)
+ break;
+ if (newt <= t)
+ break;
+ newtm = *localtime(&newt);
+ if (delta(&newtm, &tm) != (newt - t) ||
+ newtm.tm_isdst != tm.tm_isdst ||
+ strcmp(abbr(&newtm), buf) != 0) {
+ newt = hunt(argv[i], t, newt);
+ newtm = *localtime(&newt);
+ (void) strncpy(buf, abbr(&newtm),
+ (sizeof buf) - 1);
+ }
+ t = newt;
+ tm = newtm;
+ }
+ /*
+ ** Get highest value of t.
+ */
+ t = ~((time_t) 0);
+ if (t < 0) /* time_t is signed */
+ t &= ~hibit;
+ t -= SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ t += SECSPERHOUR * HOURSPERDAY;
+ show(argv[i], t, TRUE);
+ }
+ if (fflush(stdout) || ferror(stdout))
+ errx(EXIT_FAILURE, _("error writing standard output"));
+ exit(EXIT_SUCCESS);
+
+ /* gcc -Wall pacifier */
+ for ( ; ; )
+ continue;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, _("usage: zdump [-v] [-c cutoff] zonename ...\n"));
+ exit(EXIT_FAILURE);
+}
+
+static time_t
+hunt(name, lot, hit)
+char * name;
+time_t lot;
+time_t hit;
+{
+ time_t t;
+ struct tm lotm;
+ struct tm tm;
+ static char loab[MAX_STRING_LENGTH];
+
+ lotm = *localtime(&lot);
+ (void) strncpy(loab, abbr(&lotm), (sizeof loab) - 1);
+ while ((hit - lot) >= 2) {
+ t = lot / 2 + hit / 2;
+ if (t <= lot)
+ ++t;
+ else if (t >= hit)
+ --t;
+ tm = *localtime(&t);
+ if (delta(&tm, &lotm) == (t - lot) &&
+ tm.tm_isdst == lotm.tm_isdst &&
+ strcmp(abbr(&tm), loab) == 0) {
+ lot = t;
+ lotm = tm;
+ } else hit = t;
+ }
+ show(name, lot, TRUE);
+ show(name, hit, TRUE);
+ return hit;
+}
+
+/*
+** Thanks to Paul Eggert (eggert@twinsun.com) for logic used in delta.
+*/
+
+static long
+delta(newp, oldp)
+struct tm * newp;
+struct tm * oldp;
+{
+ long result;
+ int tmy;
+
+ if (newp->tm_year < oldp->tm_year)
+ return -delta(oldp, newp);
+ result = 0;
+ for (tmy = oldp->tm_year; tmy < newp->tm_year; ++tmy)
+ result += DAYSPERNYEAR + isleap(tmy + TM_YEAR_BASE);
+ result += newp->tm_yday - oldp->tm_yday;
+ result *= HOURSPERDAY;
+ result += newp->tm_hour - oldp->tm_hour;
+ result *= MINSPERHOUR;
+ result += newp->tm_min - oldp->tm_min;
+ result *= SECSPERMIN;
+ result += newp->tm_sec - oldp->tm_sec;
+ return result;
+}
+
+static void
+show(zone, t, v)
+char * zone;
+time_t t;
+int v;
+{
+ struct tm * tmp;
+
+ (void) printf("%-*s ", (int) longest, zone);
+ if (v)
+ (void) printf("%.24s UTC = ", asctime(gmtime(&t)));
+ tmp = localtime(&t);
+ (void) printf("%.24s", asctime(tmp));
+ if (*abbr(tmp) != '\0')
+ (void) printf(" %s", abbr(tmp));
+ if (v) {
+ (void) printf(" isdst=%d", tmp->tm_isdst);
+#ifdef TM_GMTOFF
+ (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
+#endif /* defined TM_GMTOFF */
+ }
+ (void) printf("\n");
+}
+
+static char *
+abbr(tmp)
+struct tm * tmp;
+{
+ register char * result;
+ static char nada;
+
+ if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
+ return &nada;
+ result = tzname[tmp->tm_isdst];
+ return (result == NULL) ? &nada : result;
+}
diff --git a/usr.sbin/zic/zdump/Makefile b/usr.sbin/zic/zdump/Makefile
new file mode 100644
index 0000000..46af3ca
--- /dev/null
+++ b/usr.sbin/zic/zdump/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zdump
+
+SRCS= zdump.c ialloc.c scheck.c
+MAN8= ${.CURDIR}/../zdump.8
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
+CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zic.8 b/usr.sbin/zic/zic.8
new file mode 100644
index 0000000..32a37e3
--- /dev/null
+++ b/usr.sbin/zic/zic.8
@@ -0,0 +1,387 @@
+.\" $Id: zic.8,v 1.8 1999/01/21 17:12:49 wollman Exp $
+.Dd
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm zic
+.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
+.Nm Zic
+reads text from the file(s) named on the command line
+and creates the time conversion information files specified in this input.
+If a
+.Ar filename
+is
+.Em - ,
+the standard input is read.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl 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 zic
+will instead error out immediately.
+.It Fl d Ar directory
+Create time conversion information files in the named directory rather than
+in the standard directory named below.
+.It Fl g Ar group
+After creating each output file, change its group ownership to the
+specified
+.Ar group
+(which can be either a name or a numeric group ID).
+.It Fl L Ar leapsecondfilename
+Read leap second information from the file with the given name.
+If this option is not used,
+no leap second information appears in output files.
+.It Fl l Ar timezone
+Use the given
+.Ar time zone
+as local time.
+.Nm Zic
+will act as if the input contained a link line of the form
+.Bd -literal -offset indent
+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.
+.Nm Zic
+will act as if the input contained a link line of the form
+.Bd -literal -offset indent
+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
+.Xr time 2
+values.
+.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.
+.It Fl y Ar command
+Use the given
+.Ar command
+rather than
+.Em yearistype
+when checking year types (see below).
+.El
+.Pp
+Input lines are made up of fields.
+Fields are separated from one another by any number of white space characters.
+Leading and trailing white space on input lines is ignored.
+An unquoted sharp character (#) in the input introduces a comment which extends
+to the end of the line the sharp character appears on.
+White space characters and sharp characters may be enclosed in double quotes
+(") if they're to be used as part of a field.
+Any line that is blank (after comment stripping) is ignored.
+Non-blank lines are expected to be of one of three types:
+rule lines, zone lines, and link lines.
+.Pp
+A rule line has the form:
+.Dl Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
+For example:
+.Dl Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D
+.Pp
+The fields that make up a rule line are:
+.Bl -tag -width "LETTER/S" -offset indent
+.It NAME
+Give the (arbitrary) name of the set of rules this rule is part of.
+.It FROM
+Give the first year in which the rule applies.
+Any integer year can be supplied; the Gregorian calendar is assumed.
+The word
+.Em minimum
+(or an abbreviation) means the minimum year representable as an integer.
+The word
+.Em maximum
+(or an abbreviation) means the maximum year representable as an integer.
+Rules can describe times that are not representable as time values,
+with the unrepresentable times ignored; this allows rules to be portable
+among hosts with differing time value types.
+.It TO
+Give the final year in which the rule applies.
+In addition to
+.Em minimum
+and
+.Em maximum
+(as above),
+the word
+.Em only
+(or an abbreviation)
+may be used to repeat the value of the
+.Em FROM
+field.
+.It TYPE
+Give the type of year in which the rule applies.
+If
+.Em TYPE
+is
+.Em \-
+then the rule applies in all years between
+.Em FROM
+and
+.Em TO
+inclusive.
+If
+.Em TYPE
+is something else, then
+.Nm
+executes the command
+.Li yearistype Ar year Ar type
+to check the type of a year:
+an exit status of zero is taken to mean that the year is of the given type;
+an exit status of one is taken to mean that the year is not of the given type.
+.It IN
+Name the month in which the rule takes effect.
+Month names may be abbreviated.
+.It ON
+Give the day on which the rule takes effect.
+Recognized forms include:
+.Pp
+.Bl -tag -width lastSun -compact -offset indent
+.It \&5
+the fifth of the month
+.It lastSun
+the last Sunday in the month
+.It lastMon
+the last Monday in the month
+.It Sun>=8
+first Sunday on or after the eighth
+.It Sun<=25
+last Sunday on or before the 25th
+.El
+.Pp
+Names of days of the week may be abbreviated or spelled out in full.
+Note that there must be no spaces within the
+.Em ON
+field.
+.It AT
+Give the time of day at which the rule takes effect.
+Recognized forms include:
+.Pp
+.Bl -tag -width "1:28:14" -offset indent -compact
+.It 2
+time in hours
+.It 2:00
+time in hours and minutes
+.It 15:00
+24-hour format time (for times after noon)
+.It 1:28:14
+time in hours, minutes, and seconds
+.El
+.Pp
+where hour 0 is midnight at the start of the day,
+and hour 24 is midnight at the end of the day.
+Any of these forms may be followed by the letter
+.Sq Li w
+if the given time is local
+.Dq "wall clock"
+time,
+.Sq Li s
+if the given time is local
+.Dq standard
+time, or
+.Sq Li u
+(or
+.Sq Li g
+or
+.Sq Li z )
+if the given time is universal time;
+in the absence of an indicator,
+wall clock time is assumed.
+.It SAVE
+Give the amount of time to be added to local standard time when the rule is in
+effect.
+This field has the same format as the
+.Em AT
+field
+(although, of course, the
+.Sq Li w
+and
+.Sq Li s
+suffixes are not used).
+.It LETTER/S
+Give the
+.Dq "variable part"
+(for example, the
+.Dq S
+or
+.Dq D
+in
+.Dq EST
+or
+.Dq EDT )
+of time zone abbreviations to be used when this rule is in effect.
+If this field is
+.Em \- ,
+the variable part is null.
+.El
+.Pp
+A zone line has the form:
+.Dl Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
+For example:
+.Dl Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
+The fields that make up a zone line are:
+.Bl -tag -width indent
+.It NAME
+The name of the time zone.
+This is the name used in creating the time conversion information file for the
+zone.
+.It GMTOFF
+The amount of time to add to UTC to get standard time in this zone.
+This field has the same format as the
+.Em AT
+and
+.Em SAVE
+fields of rule lines;
+begin the field with a minus sign if time must be subtracted from UTC.
+.It RULES/SAVE
+The name of the rule(s) that apply in the time zone or,
+alternately, an amount of time to add to local standard time.
+If this field is
+.Em \-
+then standard time always applies in the time zone.
+.It FORMAT
+The format for time zone abbreviations in this time zone.
+The pair of characters
+.Em %s
+is used to show where the
+.Dq "variable part"
+of the time zone abbreviation goes.
+Alternately,
+a slash (/)
+separates standard and daylight abbreviations.
+.It UNTIL
+The time at which the UTC offset or the rule(s) change for a location.
+It is specified as a year, a month, a day, and a time of day.
+If this is specified,
+the time zone information is generated from the given UTC offset
+and rule change until the time specified.
+The month, day, and time of day have the same format as the IN, ON, and AT
+columns of a rule; trailing columns can be omitted, and default to the
+earliest possible value for the missing columns.
+.Pp
+The next line must be a
+.Dq continuation
+line; this has the same form as a zone line except that the
+string
+.Dq Zone
+and the name are omitted, as the continuation line will
+place information starting at the time specified as the
+.Em UNTIL
+field in the previous line in the file used by the previous line.
+Continuation lines may contain an
+.Em UNTIL
+field, just as zone lines do, indicating that the next line is a further
+continuation.
+.El
+.Pp
+A link line has the form
+.Dl Link LINK-FROM LINK-TO
+For example:
+.Dl Link Europe/Istanbul Asia/Istanbul
+The
+.Em LINK-FROM
+field should appear as the
+.Em NAME
+field in some zone line;
+the
+.Em LINK-TO
+field is used as an alternate name for that zone.
+.Pp
+Except for continuation lines,
+lines may appear in any order in the input.
+.Pp
+Lines in the file that describes leap seconds have the following form:
+.Dl Leap YEAR MONTH DAY HH:MM:SS CORR R/S
+For example:
+.Dl Leap 1974 Dec 31 23:59:60 + S
+The
+.Em YEAR ,
+.Em MONTH ,
+.Em DAY ,
+and
+.Em HH:MM:SS
+fields tell when the leap second happened.
+The
+.Em CORR
+field
+should be
+.Dq +
+if a second was added
+or
+.Dq -
+if a second was skipped.
+.\" There's no need to document the following, since it's impossible for more
+.\" than one leap second to be inserted or deleted at a time.
+.\" The C Standard is in error in suggesting the possibility.
+.\" See Terry J Quinn, The BIPM and the accurate measure of time,
+.\" Proc IEEE 79, 7 (July 1991), 894-905.
+.\" or
+.\" .q ++
+.\" if two seconds were added
+.\" or
+.\" .q --
+.\" if two seconds were skipped.
+The
+.Em R/S
+field
+should be (an abbreviation of)
+.Dq Stationary
+if the leap second time given by the other fields should be interpreted as UTC
+or
+(an abbreviation of)
+.Dq Rolling
+if the leap second time given by the other fields should be interpreted as
+local wall clock time.
+.Sh NOTE
+For areas with more than two types of local time,
+you may need to use local standard time in the
+.Em AT
+field of the earliest transition time's rule to ensure that
+the earliest transition time recorded in the compiled file is correct.
+.Sh FILE
+.Bl -tag -width /usr/share/zoneinfo -compact
+.It /usr/share/zoneinfo
+standard directory used for created files
+.El
+.Sh "SEE ALSO"
+.Xr ctime 3 ,
+.Xr tzfile 5 ,
+.Xr zdump 8
+.\" @(#)zic.8 7.18
diff --git a/usr.sbin/zic/zic.c b/usr.sbin/zic/zic.c
new file mode 100644
index 0000000..a008e24
--- /dev/null
+++ b/usr.sbin/zic/zic.c
@@ -0,0 +1,2228 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)zic.c 7.96";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: zic.c,v 1.9 1999/01/21 21:09:56 wollman Exp $";
+#endif /* not lint */
+
+#include "private.h"
+#include "tzfile.h"
+#include <err.h>
+#include <locale.h>
+#include <sys/stat.h> /* for umask manifest constants */
+#include <sys/types.h>
+#include <unistd.h>
+
+/*
+** On some ancient hosts, predicates like `isspace(C)' are defined
+** only if isascii(C) || C == EOF. Modern hosts obey the C Standard,
+** which says they are defined only if C == ((unsigned char) C) || C == EOF.
+** Neither the C Standard nor Posix require that `isascii' exist.
+** For portability, we check both ancient and modern requirements.
+** If isascii is not defined, the isascii check succeeds trivially.
+*/
+#include "ctype.h"
+#ifndef isascii
+#define isascii(x) 1
+#endif
+
+struct rule {
+ const char * r_filename;
+ int r_linenum;
+ const char * r_name;
+
+ int r_loyear; /* for example, 1986 */
+ int r_hiyear; /* for example, 1986 */
+ const char * r_yrtype;
+
+ int r_month; /* 0..11 */
+
+ int r_dycode; /* see below */
+ int r_dayofmonth;
+ int r_wday;
+
+ long r_tod; /* time from midnight */
+ int r_todisstd; /* above is standard time if TRUE */
+ /* or wall clock time if FALSE */
+ int r_todisgmt; /* above is GMT if TRUE */
+ /* or local time if FALSE */
+ long r_stdoff; /* offset from standard time */
+ const char * r_abbrvar; /* variable part of abbreviation */
+
+ int r_todo; /* a rule to do (used in outzone) */
+ time_t r_temp; /* used in outzone */
+};
+
+/*
+** r_dycode r_dayofmonth r_wday
+*/
+
+#define DC_DOM 0 /* 1..31 */ /* unused */
+#define DC_DOWGEQ 1 /* 1..31 */ /* 0..6 (Sun..Sat) */
+#define DC_DOWLEQ 2 /* 1..31 */ /* 0..6 (Sun..Sat) */
+
+struct zone {
+ const char * z_filename;
+ int z_linenum;
+
+ const char * z_name;
+ long z_gmtoff;
+ const char * z_rule;
+ const char * z_format;
+
+ long z_stdoff;
+
+ struct rule * z_rules;
+ int z_nrules;
+
+ struct rule z_untilrule;
+ time_t z_untiltime;
+};
+
+static void addtt P((time_t starttime, int type));
+static int addtype P((long gmtoff, const char * abbr, int isdst,
+ int ttisstd, int ttisgmt));
+static void leapadd P((time_t t, int positive, int rolling, int count));
+static void adjleap P((void));
+static void associate P((void));
+static int ciequal P((const char * ap, const char * bp));
+static void convert P((long val, char * buf));
+static void dolink P((const char * fromfile, const char * tofile));
+static void doabbr P((char * abbr, const char * format,
+ const char * letters, int isdst));
+static void eat P((const char * name, int num));
+static void eats P((const char * name, int num,
+ const char * rname, int rnum));
+static long eitol P((int i));
+static void error P((const char * message));
+static char ** getfields P((char * buf));
+static long gethms P((const char * string, const char * errstrng,
+ int signable));
+static void infile P((const char * filename));
+static void inleap P((char ** fields, int nfields));
+static void inlink P((char ** fields, int nfields));
+static void inrule P((char ** fields, int nfields));
+static int inzcont P((char ** fields, int nfields));
+static int inzone P((char ** fields, int nfields));
+static int inzsub P((char ** fields, int nfields, int iscont));
+static int itsabbr P((const char * abbr, const char * word));
+static int itsdir P((const char * name));
+static int lowerit P((int c));
+static char * memcheck P((char * tocheck));
+static int mkdirs P((char * filename));
+static void newabbr P((const char * abbr));
+static long oadd P((long t1, long t2));
+static void outzone P((const struct zone * zp, int ntzones));
+static void puttzcode P((long code, FILE * fp));
+static int rcomp P((const void * leftp, const void * rightp));
+static time_t rpytime P((const struct rule * rp, int wantedy));
+static void rulesub P((struct rule * rp,
+ const char * loyearp, const char * hiyearp,
+ const char * typep, const char * monthp,
+ const char * dayp, const char * timep));
+static void setboundaries P((void));
+static void setgroup P((gid_t *flag, const char *name));
+static void setuser P((uid_t *flag, const char *name));
+static time_t tadd P((time_t t1, long t2));
+static void usage P((void));
+static void writezone P((const char * name));
+static int yearistype P((int year, const char * type));
+
+#if !(HAVE_STRERROR - 0)
+static char * strerror P((int));
+#endif /* !(HAVE_STRERROR - 0) */
+
+static int charcnt;
+static int errors;
+static const char * filename;
+static int leapcnt;
+static int linenum;
+static time_t max_time;
+static int max_year;
+static int max_year_representable;
+static time_t min_time;
+static int min_year;
+static int min_year_representable;
+static int noise;
+static const char * rfilename;
+static int rlinenum;
+static int timecnt;
+static int typecnt;
+
+/*
+** Line codes.
+*/
+
+#define LC_RULE 0
+#define LC_ZONE 1
+#define LC_LINK 2
+#define LC_LEAP 3
+
+/*
+** Which fields are which on a Zone line.
+*/
+
+#define ZF_NAME 1
+#define ZF_GMTOFF 2
+#define ZF_RULE 3
+#define ZF_FORMAT 4
+#define ZF_TILYEAR 5
+#define ZF_TILMONTH 6
+#define ZF_TILDAY 7
+#define ZF_TILTIME 8
+#define ZONE_MINFIELDS 5
+#define ZONE_MAXFIELDS 9
+
+/*
+** Which fields are which on a Zone continuation line.
+*/
+
+#define ZFC_GMTOFF 0
+#define ZFC_RULE 1
+#define ZFC_FORMAT 2
+#define ZFC_TILYEAR 3
+#define ZFC_TILMONTH 4
+#define ZFC_TILDAY 5
+#define ZFC_TILTIME 6
+#define ZONEC_MINFIELDS 3
+#define ZONEC_MAXFIELDS 7
+
+/*
+** Which files are which on a Rule line.
+*/
+
+#define RF_NAME 1
+#define RF_LOYEAR 2
+#define RF_HIYEAR 3
+#define RF_COMMAND 4
+#define RF_MONTH 5
+#define RF_DAY 6
+#define RF_TOD 7
+#define RF_STDOFF 8
+#define RF_ABBRVAR 9
+#define RULE_FIELDS 10
+
+/*
+** Which fields are which on a Link line.
+*/
+
+#define LF_FROM 1
+#define LF_TO 2
+#define LINK_FIELDS 3
+
+/*
+** Which fields are which on a Leap line.
+*/
+
+#define LP_YEAR 1
+#define LP_MONTH 2
+#define LP_DAY 3
+#define LP_TIME 4
+#define LP_CORR 5
+#define LP_ROLL 6
+#define LEAP_FIELDS 7
+
+/*
+** Year synonyms.
+*/
+
+#define YR_MINIMUM 0
+#define YR_MAXIMUM 1
+#define YR_ONLY 2
+
+static struct rule * rules;
+static int nrules; /* number of rules */
+
+static struct zone * zones;
+static int nzones; /* number of zones */
+
+struct link {
+ const char * l_filename;
+ int l_linenum;
+ const char * l_from;
+ const char * l_to;
+};
+
+static struct link * links;
+static int nlinks;
+
+struct lookup {
+ const char * l_word;
+ const int l_value;
+};
+
+static struct lookup const * byword P((const char * string,
+ const struct lookup * lp));
+
+static struct lookup const line_codes[] = {
+ { "Rule", LC_RULE },
+ { "Zone", LC_ZONE },
+ { "Link", LC_LINK },
+ { "Leap", LC_LEAP },
+ { NULL, 0}
+};
+
+static struct lookup const mon_names[] = {
+ { "January", TM_JANUARY },
+ { "February", TM_FEBRUARY },
+ { "March", TM_MARCH },
+ { "April", TM_APRIL },
+ { "May", TM_MAY },
+ { "June", TM_JUNE },
+ { "July", TM_JULY },
+ { "August", TM_AUGUST },
+ { "September", TM_SEPTEMBER },
+ { "October", TM_OCTOBER },
+ { "November", TM_NOVEMBER },
+ { "December", TM_DECEMBER },
+ { NULL, 0 }
+};
+
+static struct lookup const wday_names[] = {
+ { "Sunday", TM_SUNDAY },
+ { "Monday", TM_MONDAY },
+ { "Tuesday", TM_TUESDAY },
+ { "Wednesday", TM_WEDNESDAY },
+ { "Thursday", TM_THURSDAY },
+ { "Friday", TM_FRIDAY },
+ { "Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const lasts[] = {
+ { "last-Sunday", TM_SUNDAY },
+ { "last-Monday", TM_MONDAY },
+ { "last-Tuesday", TM_TUESDAY },
+ { "last-Wednesday", TM_WEDNESDAY },
+ { "last-Thursday", TM_THURSDAY },
+ { "last-Friday", TM_FRIDAY },
+ { "last-Saturday", TM_SATURDAY },
+ { NULL, 0 }
+};
+
+static struct lookup const begin_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { NULL, 0 }
+};
+
+static struct lookup const end_years[] = {
+ { "minimum", YR_MINIMUM },
+ { "maximum", YR_MAXIMUM },
+ { "only", YR_ONLY },
+ { NULL, 0 }
+};
+
+static struct lookup const leap_types[] = {
+ { "Rolling", TRUE },
+ { "Stationary", FALSE },
+ { NULL, 0 }
+};
+
+static const int len_months[2][MONSPERYEAR] = {
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
+ { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
+};
+
+static const int len_years[2] = {
+ DAYSPERNYEAR, DAYSPERLYEAR
+};
+
+static struct attype {
+ time_t at;
+ unsigned char type;
+} attypes[TZ_MAX_TIMES];
+static long gmtoffs[TZ_MAX_TYPES];
+static char isdsts[TZ_MAX_TYPES];
+static unsigned char abbrinds[TZ_MAX_TYPES];
+static char ttisstds[TZ_MAX_TYPES];
+static char ttisgmts[TZ_MAX_TYPES];
+static char chars[TZ_MAX_CHARS];
+static time_t trans[TZ_MAX_LEAPS];
+static long corr[TZ_MAX_LEAPS];
+static char roll[TZ_MAX_LEAPS];
+
+/*
+** Memory allocation.
+*/
+
+static char *
+memcheck(ptr)
+char * const ptr;
+{
+ if (ptr == NULL)
+ errx(EXIT_FAILURE, _("memory exhausted"));
+ return ptr;
+}
+
+#define emalloc(size) memcheck(imalloc(size))
+#define erealloc(ptr, size) memcheck(irealloc((ptr), (size)))
+#define ecpyalloc(ptr) memcheck(icpyalloc(ptr))
+#define ecatalloc(oldp, newp) memcheck(icatalloc((oldp), (newp)))
+
+/*
+** Error handling.
+*/
+
+#if !(HAVE_STRERROR - 0)
+static char *
+strerror(errnum)
+int errnum;
+{
+ extern char * sys_errlist[];
+ extern int sys_nerr;
+
+ return (errnum > 0 && errnum <= sys_nerr) ?
+ sys_errlist[errnum] : _("Unknown system error");
+}
+#endif /* !(HAVE_STRERROR - 0) */
+
+static void
+eats(name, num, rname, rnum)
+const char * const name;
+const int num;
+const char * const rname;
+const int rnum;
+{
+ filename = name;
+ linenum = num;
+ rfilename = rname;
+ rlinenum = rnum;
+}
+
+static void
+eat(name, num)
+const char * const name;
+const int num;
+{
+ eats(name, num, (char *) NULL, -1);
+}
+
+static void
+error(string)
+const char * const string;
+{
+ /*
+ ** Match the format of "cc" to allow sh users to
+ ** zic ... 2>&1 | error -t "*" -v
+ ** on BSD systems.
+ */
+ (void) fprintf(stderr, _("\"%s\", line %d: %s"),
+ filename, linenum, string);
+ if (rfilename != NULL)
+ (void) fprintf(stderr, _(" (rule from \"%s\", line %d)"),
+ rfilename, rlinenum);
+ (void) fprintf(stderr, "\n");
+ ++errors;
+}
+
+static void
+warning(string)
+const char * const string;
+{
+ char * cp;
+
+ cp = ecpyalloc(_("warning: "));
+ cp = ecatalloc(cp, string);
+ error(cp);
+ ifree(cp);
+ --errors;
+}
+
+static void
+usage P((void))
+{
+ (void) fprintf(stderr, "%s\n%s\n",
+_("usage: zic [-s] [-v] [-l localtime] [-p posixrules] [-d directory]"),
+_(" [-L leapseconds] [-y yearistype] [filename ... ]"));
+ (void) exit(EXIT_FAILURE);
+}
+
+static const char * psxrules;
+static const char * lcltime;
+static const char * directory;
+static const char * leapsec;
+static const char * yitcommand;
+static int sflag = FALSE;
+static int Dflag;
+static uid_t uflag = (uid_t)-1;
+static gid_t gflag = (gid_t)-1;
+static mode_t mflag = (S_IRUSR | S_IRGRP | S_IROTH
+ | S_IWUSR);
+
+int
+main(argc, argv)
+int argc;
+char * argv[];
+{
+ register int i;
+ register int j;
+ register int c;
+
+#ifdef unix
+ (void) umask(umask(S_IWGRP | S_IWOTH) | (S_IWGRP | S_IWOTH));
+#endif /* defined unix */
+#if HAVE_GETTEXT - 0
+ (void) setlocale(LC_MESSAGES, "");
+#ifdef TZ_DOMAINDIR
+ (void) bindtextdomain(TZ_DOMAIN, TZ_DOMAINDIR);
+#endif /* defined TEXTDOMAINDIR */
+ (void) textdomain(TZ_DOMAIN);
+#endif /* HAVE_GETTEXT - 0 */
+ while ((c = getopt(argc, argv, "Dd:g:l:m:p:L:u:vsy:")) != -1)
+ switch (c) {
+ default:
+ usage();
+ case 'D':
+ Dflag = 1;
+ break;
+ case 'd':
+ if (directory == NULL)
+ directory = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -d option specified"));
+ break;
+ case 'g':
+ setgroup(&gflag, optarg);
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -l option specified"));
+ break;
+ case 'm':
+ {
+ void *set = setmode(optarg);
+ getmode(set, mflag);
+ break;
+ }
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -p option specified"));
+ break;
+ case 'u':
+ setuser(&uflag, optarg);
+ break;
+ case 'y':
+ if (yitcommand == NULL)
+ yitcommand = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -y option specified"));
+ break;
+ case 'L':
+ if (leapsec == NULL)
+ leapsec = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -L option specified"));
+ break;
+ case 'v':
+ noise = TRUE;
+ break;
+ case 's':
+ sflag = TRUE;
+ break;
+ }
+ if (optind == argc - 1 && strcmp(argv[optind], "=") == 0)
+ usage(); /* usage message by request */
+ if (directory == NULL)
+ directory = TZDIR;
+ if (yitcommand == NULL)
+ yitcommand = "yearistype";
+
+ setboundaries();
+
+ if (optind < argc && leapsec != NULL) {
+ infile(leapsec);
+ adjleap();
+ }
+
+ for (i = optind; i < argc; ++i)
+ infile(argv[i]);
+ if (errors)
+ (void) exit(EXIT_FAILURE);
+ associate();
+ for (i = 0; i < nzones; i = j) {
+ /*
+ ** Find the next non-continuation zone entry.
+ */
+ for (j = i + 1; j < nzones && zones[j].z_name == NULL; ++j)
+ continue;
+ outzone(&zones[i], j - i);
+ }
+ /*
+ ** Make links.
+ */
+ for (i = 0; i < nlinks; ++i)
+ dolink(links[i].l_from, links[i].l_to);
+ if (lcltime != NULL)
+ dolink(lcltime, TZDEFAULT);
+ if (psxrules != NULL)
+ dolink(psxrules, TZDEFRULES);
+ return (errors == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
+}
+
+static void
+dolink(fromfile, tofile)
+const char * const fromfile;
+const char * const tofile;
+{
+ register char * fromname;
+ register char * toname;
+
+ if (fromfile[0] == '/')
+ fromname = ecpyalloc(fromfile);
+ else {
+ fromname = ecpyalloc(directory);
+ fromname = ecatalloc(fromname, "/");
+ fromname = ecatalloc(fromname, fromfile);
+ }
+ if (tofile[0] == '/')
+ toname = ecpyalloc(tofile);
+ else {
+ toname = ecpyalloc(directory);
+ toname = ecatalloc(toname, "/");
+ toname = ecatalloc(toname, tofile);
+ }
+ /*
+ ** We get to be careful here since
+ ** there's a fair chance of root running us.
+ */
+ if (!itsdir(toname))
+ (void) remove(toname);
+ if (link(fromname, toname) != 0) {
+ int result;
+
+ if (mkdirs(toname) != 0)
+ (void) exit(EXIT_FAILURE);
+ result = link(fromname, toname);
+#if (HAVE_SYMLINK - 0)
+ if (result != 0) {
+ result = symlink(fromname, toname);
+ if (result == 0)
+warning(_("hard link failed, symbolic link used"));
+ }
+#endif
+ if (result != 0) {
+ err(EXIT_FAILURE, _("can't link from %s to %s"),
+ fromname, toname);
+ }
+ }
+ ifree(fromname);
+ ifree(toname);
+}
+
+#ifndef INT_MAX
+#define INT_MAX ((int) (((unsigned)~0)>>1))
+#endif /* !defined INT_MAX */
+
+#ifndef INT_MIN
+#define INT_MIN ((int) ~(((unsigned)~0)>>1))
+#endif /* !defined INT_MIN */
+
+/*
+** The tz file format currently allows at most 32-bit quantities.
+** This restriction should be removed before signed 32-bit values
+** wrap around in 2038, but unfortunately this will require a
+** change to the tz file format.
+*/
+
+#define MAX_BITS_IN_FILE 32
+#define TIME_T_BITS_IN_FILE ((TYPE_BIT(time_t) < MAX_BITS_IN_FILE) ? TYPE_BIT(time_t) : MAX_BITS_IN_FILE)
+
+static void
+setboundaries P((void))
+{
+ if (TYPE_SIGNED(time_t)) {
+ min_time = ~ (time_t) 0;
+ min_time <<= TIME_T_BITS_IN_FILE - 1;
+ max_time = ~ (time_t) 0 - min_time;
+ if (sflag)
+ min_time = 0;
+ } else {
+ min_time = 0;
+ max_time = 2 - sflag;
+ max_time <<= TIME_T_BITS_IN_FILE - 1;
+ --max_time;
+ }
+ min_year = TM_YEAR_BASE + gmtime(&min_time)->tm_year;
+ max_year = TM_YEAR_BASE + gmtime(&max_time)->tm_year;
+ min_year_representable = min_year;
+ max_year_representable = max_year;
+}
+
+static int
+itsdir(name)
+const char * const name;
+{
+ register char * myname;
+ register int accres;
+
+ myname = ecpyalloc(name);
+ myname = ecatalloc(myname, "/.");
+ accres = access(myname, F_OK);
+ ifree(myname);
+ return accres == 0;
+}
+
+/*
+** Associate sets of rules with zones.
+*/
+
+/*
+** Sort by rule name.
+*/
+
+static int
+rcomp(cp1, cp2)
+const void * cp1;
+const void * cp2;
+{
+ return strcmp(((const struct rule *) cp1)->r_name,
+ ((const struct rule *) cp2)->r_name);
+}
+
+static void
+associate P((void))
+{
+ register struct zone * zp;
+ register struct rule * rp;
+ register int base, out;
+ register int i, j;
+
+ if (nrules != 0) {
+ (void) qsort((void *) rules, (size_t) nrules,
+ (size_t) sizeof *rules, rcomp);
+ for (i = 0; i < nrules - 1; ++i) {
+ if (strcmp(rules[i].r_name,
+ rules[i + 1].r_name) != 0)
+ continue;
+ if (strcmp(rules[i].r_filename,
+ rules[i + 1].r_filename) == 0)
+ continue;
+ eat(rules[i].r_filename, rules[i].r_linenum);
+ warning(_("same rule name in multiple files"));
+ eat(rules[i + 1].r_filename, rules[i + 1].r_linenum);
+ warning(_("same rule name in multiple files"));
+ for (j = i + 2; j < nrules; ++j) {
+ if (strcmp(rules[i].r_name,
+ rules[j].r_name) != 0)
+ break;
+ if (strcmp(rules[i].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ if (strcmp(rules[i + 1].r_filename,
+ rules[j].r_filename) == 0)
+ continue;
+ break;
+ }
+ i = j - 1;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ zp->z_rules = NULL;
+ zp->z_nrules = 0;
+ }
+ for (base = 0; base < nrules; base = out) {
+ rp = &rules[base];
+ for (out = base + 1; out < nrules; ++out)
+ if (strcmp(rp->r_name, rules[out].r_name) != 0)
+ break;
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (strcmp(zp->z_rule, rp->r_name) != 0)
+ continue;
+ zp->z_rules = rp;
+ zp->z_nrules = out - base;
+ }
+ }
+ for (i = 0; i < nzones; ++i) {
+ zp = &zones[i];
+ if (zp->z_nrules == 0) {
+ /*
+ ** Maybe we have a local standard time offset.
+ */
+ eat(zp->z_filename, zp->z_linenum);
+ zp->z_stdoff = gethms(zp->z_rule, _("unruly zone"),
+ TRUE);
+ /*
+ ** Note, though, that if there's no rule,
+ ** a '%s' in the format is a bad thing.
+ */
+ if (strchr(zp->z_format, '%') != 0)
+ error(_("%s in ruleless zone"));
+ }
+ }
+ if (errors)
+ (void) exit(EXIT_FAILURE);
+}
+
+static void
+infile(name)
+const char * name;
+{
+ register FILE * fp;
+ register char ** fields;
+ register char * cp;
+ register const struct lookup * lp;
+ register int nfields;
+ register int wantcont;
+ register int num;
+ char buf[BUFSIZ];
+
+ if (strcmp(name, "-") == 0) {
+ name = _("standard input");
+ fp = stdin;
+ } else if ((fp = fopen(name, "r")) == NULL)
+ err(EXIT_FAILURE, _("can't open %s"), name);
+ wantcont = FALSE;
+ for (num = 1; ; ++num) {
+ eat(name, num);
+ if (fgets(buf, (int) sizeof buf, fp) != buf)
+ break;
+ cp = strchr(buf, '\n');
+ if (cp == NULL) {
+ error(_("line too long"));
+ (void) exit(EXIT_FAILURE);
+ }
+ *cp = '\0';
+ fields = getfields(buf);
+ nfields = 0;
+ while (fields[nfields] != NULL) {
+ static char nada;
+
+ if (strcmp(fields[nfields], "-") == 0)
+ fields[nfields] = &nada;
+ ++nfields;
+ }
+ if (nfields == 0) {
+ /* nothing to do */
+ } else if (wantcont) {
+ wantcont = inzcont(fields, nfields);
+ } else {
+ lp = byword(fields[0], line_codes);
+ if (lp == NULL)
+ error(_("input line of unknown type"));
+ else switch ((int) (lp->l_value)) {
+ case LC_RULE:
+ inrule(fields, nfields);
+ wantcont = FALSE;
+ break;
+ case LC_ZONE:
+ wantcont = inzone(fields, nfields);
+ break;
+ case LC_LINK:
+ inlink(fields, nfields);
+ wantcont = FALSE;
+ break;
+ case LC_LEAP:
+ if (name != leapsec)
+ warnx(
+_("leap line in non leap seconds file %s"), name);
+ else inleap(fields, nfields);
+ wantcont = FALSE;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+_("panic: invalid l_value %d"), lp->l_value);
+ }
+ }
+ ifree((char *) fields);
+ }
+ if (ferror(fp))
+ errx(EXIT_FAILURE, _("error reading %s"), filename);
+ if (fp != stdin && fclose(fp))
+ err(EXIT_FAILURE, _("error closing %s"), filename);
+ if (wantcont)
+ error(_("expected continuation line not found"));
+}
+
+/*
+** Convert a string of one of the forms
+** h -h hh:mm -hh:mm hh:mm:ss -hh:mm:ss
+** into a number of seconds.
+** A null string maps to zero.
+** Call error with errstring and return zero on errors.
+*/
+
+static long
+gethms(string, errstring, signable)
+const char * string;
+const char * const errstring;
+const int signable;
+{
+ int hh, mm, ss, sign;
+
+ if (string == NULL || *string == '\0')
+ return 0;
+ if (!signable)
+ sign = 1;
+ else if (*string == '-') {
+ sign = -1;
+ ++string;
+ } else sign = 1;
+ if (sscanf(string, scheck(string, "%d"), &hh) == 1)
+ mm = ss = 0;
+ else if (sscanf(string, scheck(string, "%d:%d"), &hh, &mm) == 2)
+ ss = 0;
+ else if (sscanf(string, scheck(string, "%d:%d:%d"),
+ &hh, &mm, &ss) != 3) {
+ error(errstring);
+ return 0;
+ }
+ if ((hh < 0 || hh >= HOURSPERDAY ||
+ mm < 0 || mm >= MINSPERHOUR ||
+ ss < 0 || ss > SECSPERMIN) &&
+ !(hh == HOURSPERDAY && mm == 0 && ss == 0)) {
+ error(errstring);
+ return 0;
+ }
+ return eitol(sign) *
+ (eitol(hh * MINSPERHOUR + mm) *
+ eitol(SECSPERMIN) + eitol(ss));
+}
+
+static void
+inrule(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ static struct rule r;
+
+ if (nfields != RULE_FIELDS) {
+ error(_("wrong number of fields on Rule line"));
+ return;
+ }
+ if (*fields[RF_NAME] == '\0') {
+ error(_("nameless rule"));
+ return;
+ }
+ r.r_filename = filename;
+ r.r_linenum = linenum;
+ r.r_stdoff = gethms(fields[RF_STDOFF], _("invalid saved time"), TRUE);
+ rulesub(&r, fields[RF_LOYEAR], fields[RF_HIYEAR], fields[RF_COMMAND],
+ fields[RF_MONTH], fields[RF_DAY], fields[RF_TOD]);
+ r.r_name = ecpyalloc(fields[RF_NAME]);
+ r.r_abbrvar = ecpyalloc(fields[RF_ABBRVAR]);
+ rules = (struct rule *) (void *) erealloc((char *) rules,
+ (int) ((nrules + 1) * sizeof *rules));
+ rules[nrules++] = r;
+}
+
+static int
+inzone(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ register int i;
+ static char * buf;
+
+ if (nfields < ZONE_MINFIELDS || nfields > ZONE_MAXFIELDS) {
+ error(_("wrong number of fields on Zone line"));
+ return FALSE;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFAULT) == 0 && lcltime != NULL) {
+ buf = erealloc(buf, (int) (132 + strlen(TZDEFAULT)));
+ (void) sprintf(buf,
+_("\"Zone %s\" line and -l option are mutually exclusive"),
+ TZDEFAULT);
+ error(buf);
+ return FALSE;
+ }
+ if (strcmp(fields[ZF_NAME], TZDEFRULES) == 0 && psxrules != NULL) {
+ buf = erealloc(buf, (int) (132 + strlen(TZDEFRULES)));
+ (void) sprintf(buf,
+_("\"Zone %s\" line and -p option are mutually exclusive"),
+ TZDEFRULES);
+ error(buf);
+ return FALSE;
+ }
+ for (i = 0; i < nzones; ++i)
+ if (zones[i].z_name != NULL &&
+ strcmp(zones[i].z_name, fields[ZF_NAME]) == 0) {
+ buf = erealloc(buf, (int) (132 +
+ strlen(fields[ZF_NAME]) +
+ strlen(zones[i].z_filename)));
+ (void) sprintf(buf,
+_("duplicate zone name %s (file \"%s\", line %d)"),
+ fields[ZF_NAME],
+ zones[i].z_filename,
+ zones[i].z_linenum);
+ error(buf);
+ return FALSE;
+ }
+ return inzsub(fields, nfields, FALSE);
+}
+
+static int
+inzcont(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ if (nfields < ZONEC_MINFIELDS || nfields > ZONEC_MAXFIELDS) {
+ error(_("wrong number of fields on Zone continuation line"));
+ return FALSE;
+ }
+ return inzsub(fields, nfields, TRUE);
+}
+
+static int
+inzsub(fields, nfields, iscont)
+register char ** const fields;
+const int nfields;
+const int iscont;
+{
+ register char * cp;
+ static struct zone z;
+ register int i_gmtoff, i_rule, i_format;
+ register int i_untilyear, i_untilmonth;
+ register int i_untilday, i_untiltime;
+ register int hasuntil;
+
+ if (iscont) {
+ i_gmtoff = ZFC_GMTOFF;
+ i_rule = ZFC_RULE;
+ i_format = ZFC_FORMAT;
+ i_untilyear = ZFC_TILYEAR;
+ i_untilmonth = ZFC_TILMONTH;
+ i_untilday = ZFC_TILDAY;
+ i_untiltime = ZFC_TILTIME;
+ z.z_name = NULL;
+ } else {
+ i_gmtoff = ZF_GMTOFF;
+ i_rule = ZF_RULE;
+ i_format = ZF_FORMAT;
+ i_untilyear = ZF_TILYEAR;
+ i_untilmonth = ZF_TILMONTH;
+ i_untilday = ZF_TILDAY;
+ i_untiltime = ZF_TILTIME;
+ z.z_name = ecpyalloc(fields[ZF_NAME]);
+ }
+ z.z_filename = filename;
+ z.z_linenum = linenum;
+ z.z_gmtoff = gethms(fields[i_gmtoff], _("invalid UTC offset"), TRUE);
+ if ((cp = strchr(fields[i_format], '%')) != 0) {
+ if (*++cp != 's' || strchr(cp, '%') != 0) {
+ error(_("invalid abbreviation format"));
+ return FALSE;
+ }
+ }
+ z.z_rule = ecpyalloc(fields[i_rule]);
+ z.z_format = ecpyalloc(fields[i_format]);
+ hasuntil = nfields > i_untilyear;
+ if (hasuntil) {
+ z.z_untilrule.r_filename = filename;
+ z.z_untilrule.r_linenum = linenum;
+ rulesub(&z.z_untilrule,
+ fields[i_untilyear],
+ "only",
+ "",
+ (nfields > i_untilmonth) ?
+ fields[i_untilmonth] : "Jan",
+ (nfields > i_untilday) ? fields[i_untilday] : "1",
+ (nfields > i_untiltime) ? fields[i_untiltime] : "0");
+ z.z_untiltime = rpytime(&z.z_untilrule,
+ z.z_untilrule.r_loyear);
+ if (iscont && nzones > 0 &&
+ z.z_untiltime > min_time &&
+ z.z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime > min_time &&
+ zones[nzones - 1].z_untiltime < max_time &&
+ zones[nzones - 1].z_untiltime >= z.z_untiltime) {
+ error(_("Zone continuation line end time is not after end time of previous line"));
+ return FALSE;
+ }
+ }
+ zones = (struct zone *) (void *) erealloc((char *) zones,
+ (int) ((nzones + 1) * sizeof *zones));
+ zones[nzones++] = z;
+ /*
+ ** If there was an UNTIL field on this line,
+ ** there's more information about the zone on the next line.
+ */
+ return hasuntil;
+}
+
+static void
+inleap(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ register const char * cp;
+ register const struct lookup * lp;
+ register int i, j;
+ int year, month, day;
+ long dayoff, tod;
+ time_t t;
+
+ if (nfields != LEAP_FIELDS) {
+ error(_("wrong number of fields on Leap line"));
+ return;
+ }
+ dayoff = 0;
+ cp = fields[LP_YEAR];
+ if (sscanf(cp, scheck(cp, "%d"), &year) != 1) {
+ /*
+ * Leapin' Lizards!
+ */
+ error(_("invalid leaping year"));
+ return;
+ }
+ j = EPOCH_YEAR;
+ while (j != year) {
+ if (year > j) {
+ i = len_years[isleap(j)];
+ ++j;
+ } else {
+ --j;
+ i = -len_years[isleap(j)];
+ }
+ dayoff = oadd(dayoff, eitol(i));
+ }
+ if ((lp = byword(fields[LP_MONTH], mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return;
+ }
+ month = lp->l_value;
+ j = TM_JANUARY;
+ while (j != month) {
+ i = len_months[isleap(year)][j];
+ dayoff = oadd(dayoff, eitol(i));
+ ++j;
+ }
+ cp = fields[LP_DAY];
+ if (sscanf(cp, scheck(cp, "%d"), &day) != 1 ||
+ day <= 0 || day > len_months[isleap(year)][month]) {
+ error(_("invalid day of month"));
+ return;
+ }
+ dayoff = oadd(dayoff, eitol(day - 1));
+ if (dayoff < 0 && !TYPE_SIGNED(time_t)) {
+ error(_("time before zero"));
+ return;
+ }
+ t = (time_t) dayoff * SECSPERDAY;
+ /*
+ ** Cheap overflow check.
+ */
+ if (t / SECSPERDAY != dayoff) {
+ error(_("time overflow"));
+ return;
+ }
+ tod = gethms(fields[LP_TIME], _("invalid time of day"), FALSE);
+ cp = fields[LP_CORR];
+ {
+ register int positive;
+ int count;
+
+ if (strcmp(cp, "") == 0) { /* infile() turns "-" into "" */
+ positive = FALSE;
+ count = 1;
+ } else if (strcmp(cp, "--") == 0) {
+ positive = FALSE;
+ count = 2;
+ } else if (strcmp(cp, "+") == 0) {
+ positive = TRUE;
+ count = 1;
+ } else if (strcmp(cp, "++") == 0) {
+ positive = TRUE;
+ count = 2;
+ } else {
+ error(_("illegal CORRECTION field on Leap line"));
+ return;
+ }
+ if ((lp = byword(fields[LP_ROLL], leap_types)) == NULL) {
+ error(_("illegal Rolling/Stationary field on Leap line"));
+ return;
+ }
+ leapadd(tadd(t, tod), positive, lp->l_value, count);
+ }
+}
+
+static void
+inlink(fields, nfields)
+register char ** const fields;
+const int nfields;
+{
+ struct link l;
+
+ if (nfields != LINK_FIELDS) {
+ error(_("wrong number of fields on Link line"));
+ return;
+ }
+ if (*fields[LF_FROM] == '\0') {
+ error(_("blank FROM field on Link line"));
+ return;
+ }
+ if (*fields[LF_TO] == '\0') {
+ error(_("blank TO field on Link line"));
+ return;
+ }
+ l.l_filename = filename;
+ l.l_linenum = linenum;
+ l.l_from = ecpyalloc(fields[LF_FROM]);
+ l.l_to = ecpyalloc(fields[LF_TO]);
+ links = (struct link *) (void *) erealloc((char *) links,
+ (int) ((nlinks + 1) * sizeof *links));
+ links[nlinks++] = l;
+}
+
+static void
+rulesub(rp, loyearp, hiyearp, typep, monthp, dayp, timep)
+register struct rule * const rp;
+const char * const loyearp;
+const char * const hiyearp;
+const char * const typep;
+const char * const monthp;
+const char * const dayp;
+const char * const timep;
+{
+ register const struct lookup * lp;
+ register const char * cp;
+ register char * dp;
+ register char * ep;
+
+ if ((lp = byword(monthp, mon_names)) == NULL) {
+ error(_("invalid month name"));
+ return;
+ }
+ rp->r_month = lp->l_value;
+ rp->r_todisstd = FALSE;
+ rp->r_todisgmt = FALSE;
+ dp = ecpyalloc(timep);
+ if (*dp != '\0') {
+ ep = dp + strlen(dp) - 1;
+ switch (lowerit(*ep)) {
+ case 's': /* Standard */
+ rp->r_todisstd = TRUE;
+ rp->r_todisgmt = FALSE;
+ *ep = '\0';
+ break;
+ case 'w': /* Wall */
+ rp->r_todisstd = FALSE;
+ rp->r_todisgmt = FALSE;
+ *ep = '\0';
+ break;
+ case 'g': /* Greenwich */
+ case 'u': /* Universal */
+ case 'z': /* Zulu */
+ rp->r_todisstd = TRUE;
+ rp->r_todisgmt = TRUE;
+ *ep = '\0';
+ break;
+ }
+ }
+ rp->r_tod = gethms(dp, _("invalid time of day"), FALSE);
+ ifree(dp);
+ /*
+ ** Year work.
+ */
+ cp = loyearp;
+ lp = byword(cp, begin_years);
+ if (lp != NULL) switch ((int) lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_loyear = INT_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_loyear = INT_MAX;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+ _("panic: invalid l_value %d"), lp->l_value);
+ } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_loyear) != 1) {
+ error(_("invalid starting year"));
+ return;
+ } else if (noise) {
+ if (rp->r_loyear < min_year_representable)
+ warning(_("starting year too low to be represented"));
+ else if (rp->r_loyear > max_year_representable)
+ warning(_("starting year too high to be represented"));
+ }
+ cp = hiyearp;
+ if ((lp = byword(cp, end_years)) != NULL) switch ((int) lp->l_value) {
+ case YR_MINIMUM:
+ rp->r_hiyear = INT_MIN;
+ break;
+ case YR_MAXIMUM:
+ rp->r_hiyear = INT_MAX;
+ break;
+ case YR_ONLY:
+ rp->r_hiyear = rp->r_loyear;
+ break;
+ default: /* "cannot happen" */
+ errx(EXIT_FAILURE,
+ _("panic: invalid l_value %d"), lp->l_value);
+ } else if (sscanf(cp, scheck(cp, "%d"), &rp->r_hiyear) != 1) {
+ error(_("invalid ending year"));
+ return;
+ } else if (noise) {
+ if (rp->r_loyear < min_year_representable)
+ warning(_("starting year too low to be represented"));
+ else if (rp->r_loyear > max_year_representable)
+ warning(_("starting year too high to be represented"));
+ }
+ if (rp->r_loyear > rp->r_hiyear) {
+ error(_("starting year greater than ending year"));
+ return;
+ }
+ if (*typep == '\0')
+ rp->r_yrtype = NULL;
+ else {
+ if (rp->r_loyear == rp->r_hiyear) {
+ error(_("typed single year"));
+ return;
+ }
+ rp->r_yrtype = ecpyalloc(typep);
+ }
+ if (rp->r_loyear < min_year && rp->r_loyear > 0)
+ min_year = rp->r_loyear;
+ /*
+ ** Day work.
+ ** Accept things such as:
+ ** 1
+ ** last-Sunday
+ ** Sun<=20
+ ** Sun>=7
+ */
+ dp = ecpyalloc(dayp);
+ if ((lp = byword(dp, lasts)) != NULL) {
+ rp->r_dycode = DC_DOWLEQ;
+ rp->r_wday = lp->l_value;
+ rp->r_dayofmonth = len_months[1][rp->r_month];
+ } else {
+ if ((ep = strchr(dp, '<')) != 0)
+ rp->r_dycode = DC_DOWLEQ;
+ else if ((ep = strchr(dp, '>')) != 0)
+ rp->r_dycode = DC_DOWGEQ;
+ else {
+ ep = dp;
+ rp->r_dycode = DC_DOM;
+ }
+ if (rp->r_dycode != DC_DOM) {
+ *ep++ = 0;
+ if (*ep++ != '=') {
+ error(_("invalid day of month"));
+ ifree(dp);
+ return;
+ }
+ if ((lp = byword(dp, wday_names)) == NULL) {
+ error(_("invalid weekday name"));
+ ifree(dp);
+ return;
+ }
+ rp->r_wday = lp->l_value;
+ }
+ if (sscanf(ep, scheck(ep, "%d"), &rp->r_dayofmonth) != 1 ||
+ rp->r_dayofmonth <= 0 ||
+ (rp->r_dayofmonth > len_months[1][rp->r_month])) {
+ error(_("invalid day of month"));
+ ifree(dp);
+ return;
+ }
+ }
+ ifree(dp);
+}
+
+static void
+convert(val, buf)
+const long val;
+char * const buf;
+{
+ register int i;
+ register long shift;
+
+ for (i = 0, shift = 24; i < 4; ++i, shift -= 8)
+ buf[i] = val >> shift;
+}
+
+static void
+puttzcode(val, fp)
+const long val;
+FILE * const fp;
+{
+ char buf[4];
+
+ convert(val, buf);
+ (void) fwrite((void *) buf, (size_t) sizeof buf, (size_t) 1, fp);
+}
+
+static int
+atcomp(avp, bvp)
+void * avp;
+void * bvp;
+{
+ if (((struct attype *) avp)->at < ((struct attype *) bvp)->at)
+ return -1;
+ else if (((struct attype *) avp)->at > ((struct attype *) bvp)->at)
+ return 1;
+ else return 0;
+}
+
+static void
+writezone(name)
+const char * const name;
+{
+ register FILE * fp;
+ register int i, j;
+ static char * fullname;
+ static struct tzhead tzh;
+ time_t ats[TZ_MAX_TIMES];
+ unsigned char types[TZ_MAX_TIMES];
+
+ /*
+ ** Sort.
+ */
+ if (timecnt > 1)
+ (void) qsort((void *) attypes, (size_t) timecnt,
+ (size_t) sizeof *attypes, atcomp);
+ /*
+ ** Optimize.
+ */
+ {
+ int fromi;
+ int toi;
+
+ toi = 0;
+ fromi = 0;
+ while (fromi < timecnt && attypes[fromi].at < min_time)
+ ++fromi;
+ if (isdsts[0] == 0)
+ while (fromi < timecnt && attypes[fromi].type == 0)
+ ++fromi; /* handled by default rule */
+ for ( ; fromi < timecnt; ++fromi) {
+ if (toi != 0
+ && ((attypes[fromi].at
+ + gmtoffs[attypes[toi - 1].type])
+ <= (attypes[toi - 1].at
+ + gmtoffs[toi == 1 ? 0
+ : attypes[toi - 2].type]))) {
+ attypes[toi - 1].type = attypes[fromi].type;
+ continue;
+ }
+ if (toi == 0 ||
+ attypes[toi - 1].type != attypes[fromi].type)
+ attypes[toi++] = attypes[fromi];
+ }
+ timecnt = toi;
+ }
+ /*
+ ** Transfer.
+ */
+ for (i = 0; i < timecnt; ++i) {
+ ats[i] = attypes[i].at;
+ types[i] = attypes[i].type;
+ }
+ fullname = erealloc(fullname,
+ (int) (strlen(directory) + 1 + strlen(name) + 1));
+ (void) sprintf(fullname, "%s/%s", directory, name);
+
+ /*
+ * Remove old file, if any, to snap links.
+ */
+ if (!itsdir(fullname) && remove(fullname) != 0 && errno != ENOENT)
+ err(EXIT_FAILURE, _("can't remove %s"), fullname);
+
+ if ((fp = fopen(fullname, "wb")) == NULL) {
+ if (mkdirs(fullname) != 0)
+ (void) exit(EXIT_FAILURE);
+ if ((fp = fopen(fullname, "wb")) == NULL)
+ err(EXIT_FAILURE, _("can't create %s"), fullname);
+ }
+ convert(eitol(typecnt), tzh.tzh_ttisgmtcnt);
+ convert(eitol(typecnt), tzh.tzh_ttisstdcnt);
+ convert(eitol(leapcnt), tzh.tzh_leapcnt);
+ convert(eitol(timecnt), tzh.tzh_timecnt);
+ convert(eitol(typecnt), tzh.tzh_typecnt);
+ convert(eitol(charcnt), tzh.tzh_charcnt);
+ (void) strncpy(tzh.tzh_magic, TZ_MAGIC, sizeof tzh.tzh_magic);
+#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
+ DO(tzh_magic);
+ DO(tzh_reserved);
+ DO(tzh_ttisgmtcnt);
+ DO(tzh_ttisstdcnt);
+ DO(tzh_leapcnt);
+ DO(tzh_timecnt);
+ DO(tzh_typecnt);
+ DO(tzh_charcnt);
+#undef DO
+ for (i = 0; i < timecnt; ++i) {
+ j = leapcnt;
+ while (--j >= 0)
+ if (ats[i] >= trans[j]) {
+ ats[i] = tadd(ats[i], corr[j]);
+ break;
+ }
+ puttzcode((long) ats[i], fp);
+ }
+ if (timecnt > 0)
+ (void) fwrite((void *) types, (size_t) sizeof types[0],
+ (size_t) timecnt, fp);
+ for (i = 0; i < typecnt; ++i) {
+ puttzcode((long) gmtoffs[i], fp);
+ (void) putc(isdsts[i], fp);
+ (void) putc(abbrinds[i], fp);
+ }
+ if (charcnt != 0)
+ (void) fwrite((void *) chars, (size_t) sizeof chars[0],
+ (size_t) charcnt, fp);
+ for (i = 0; i < leapcnt; ++i) {
+ if (roll[i]) {
+ if (timecnt == 0 || trans[i] < ats[0]) {
+ j = 0;
+ while (isdsts[j])
+ if (++j >= typecnt) {
+ j = 0;
+ break;
+ }
+ } else {
+ j = 1;
+ while (j < timecnt && trans[i] >= ats[j])
+ ++j;
+ j = types[j - 1];
+ }
+ puttzcode((long) tadd(trans[i], -gmtoffs[j]), fp);
+ } else puttzcode((long) trans[i], fp);
+ puttzcode((long) corr[i], fp);
+ }
+ for (i = 0; i < typecnt; ++i)
+ (void) putc(ttisstds[i], fp);
+ for (i = 0; i < typecnt; ++i)
+ (void) putc(ttisgmts[i], fp);
+ if (ferror(fp) || fclose(fp))
+ errx(EXIT_FAILURE, _("error writing %s"), fullname);
+ if (chmod(fullname, mflag) < 0)
+ err(EXIT_FAILURE, _("cannot change mode of %s to %03o"),
+ fullname, (unsigned)mflag);
+ if ((uflag != (uid_t)-1 || gflag != (gid_t)-1)
+ && chown(fullname, uflag, gflag) < 0)
+ err(EXIT_FAILURE, _("cannot change ownership of %s"),
+ fullname);
+}
+
+static void
+doabbr(abbr, format, letters, isdst)
+char * const abbr;
+const char * const format;
+const char * const letters;
+const int isdst;
+{
+ if (strchr(format, '/') == NULL) {
+ if (letters == NULL)
+ (void) strcpy(abbr, format);
+ else (void) sprintf(abbr, format, letters);
+ } else if (isdst)
+ (void) strcpy(abbr, strchr(format, '/') + 1);
+ else {
+ (void) strcpy(abbr, format);
+ *strchr(abbr, '/') = '\0';
+ }
+}
+
+static void
+outzone(zpfirst, zonecount)
+const struct zone * const zpfirst;
+const int zonecount;
+{
+ register const struct zone * zp;
+ register struct rule * rp;
+ register int i, j;
+ register int usestart, useuntil;
+ register time_t starttime, untiltime;
+ register long gmtoff;
+ register long stdoff;
+ register int year;
+ register long startoff;
+ register int startttisstd;
+ register int startttisgmt;
+ register int type;
+ char startbuf[BUFSIZ];
+
+ INITIALIZE(untiltime);
+ INITIALIZE(starttime);
+ /*
+ ** Now. . .finally. . .generate some useful data!
+ */
+ timecnt = 0;
+ typecnt = 0;
+ charcnt = 0;
+ /*
+ ** 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) {
+ zp = &zpfirst[i];
+ usestart = i > 0 && (zp - 1)->z_untiltime > min_time;
+ useuntil = i < (zonecount - 1);
+ if (useuntil && zp->z_untiltime <= min_time)
+ continue;
+ gmtoff = zp->z_gmtoff;
+ eat(zp->z_filename, zp->z_linenum);
+ *startbuf = '\0';
+ startoff = zp->z_gmtoff;
+ if (zp->z_nrules == 0) {
+ stdoff = zp->z_stdoff;
+ doabbr(startbuf, zp->z_format,
+ (char *) NULL, stdoff != 0);
+ type = addtype(oadd(zp->z_gmtoff, stdoff),
+ startbuf, stdoff != 0, startttisstd,
+ startttisgmt);
+ if (usestart) {
+ addtt(starttime, type);
+ usestart = FALSE;
+ }
+ else if (stdoff != 0)
+ addtt(min_time, type);
+ } else for (year = min_year; year <= max_year; ++year) {
+ if (useuntil && year > zp->z_untilrule.r_hiyear)
+ break;
+ /*
+ ** Mark which rules to do in the current year.
+ ** For those to do, calculate rpytime(rp, year);
+ */
+ for (j = 0; j < zp->z_nrules; ++j) {
+ rp = &zp->z_rules[j];
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ rp->r_todo = year >= rp->r_loyear &&
+ year <= rp->r_hiyear &&
+ yearistype(year, rp->r_yrtype);
+ if (rp->r_todo)
+ rp->r_temp = rpytime(rp, year);
+ }
+ for ( ; ; ) {
+ register int k;
+ register time_t jtime, ktime;
+ register long offset;
+ char buf[BUFSIZ];
+
+ INITIALIZE(ktime);
+ if (useuntil) {
+ /*
+ ** Turn untiltime into UTC
+ ** assuming the current gmtoff and
+ ** stdoff values.
+ */
+ untiltime = zp->z_untiltime;
+ if (!zp->z_untilrule.r_todisgmt)
+ untiltime = tadd(untiltime,
+ -gmtoff);
+ if (!zp->z_untilrule.r_todisstd)
+ untiltime = tadd(untiltime,
+ -stdoff);
+ }
+ /*
+ ** Find the rule (of those to do, if any)
+ ** that takes effect earliest in the year.
+ */
+ k = -1;
+ for (j = 0; j < zp->z_nrules; ++j) {
+ rp = &zp->z_rules[j];
+ if (!rp->r_todo)
+ continue;
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ offset = rp->r_todisgmt ? 0 : gmtoff;
+ if (!rp->r_todisstd)
+ offset = oadd(offset, stdoff);
+ jtime = rp->r_temp;
+ if (jtime == min_time ||
+ jtime == max_time)
+ continue;
+ jtime = tadd(jtime, -offset);
+ if (k < 0 || jtime < ktime) {
+ k = j;
+ ktime = jtime;
+ }
+ }
+ if (k < 0)
+ break; /* go on to next year */
+ rp = &zp->z_rules[k];
+ rp->r_todo = FALSE;
+ if (useuntil && ktime >= untiltime)
+ break;
+ stdoff = rp->r_stdoff;
+ if (usestart && ktime == starttime)
+ usestart = FALSE;
+ if (usestart) {
+ if (ktime < starttime) {
+ startoff = oadd(zp->z_gmtoff,
+ stdoff);
+ doabbr(startbuf, zp->z_format,
+ rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ continue;
+ }
+ if (*startbuf == '\0' &&
+ startoff == oadd(zp->z_gmtoff,
+ stdoff)) {
+ doabbr(startbuf, zp->z_format,
+ rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ }
+ }
+ eats(zp->z_filename, zp->z_linenum,
+ rp->r_filename, rp->r_linenum);
+ doabbr(buf, zp->z_format, rp->r_abbrvar,
+ rp->r_stdoff != 0);
+ offset = oadd(zp->z_gmtoff, rp->r_stdoff);
+ type = addtype(offset, buf, rp->r_stdoff != 0,
+ rp->r_todisstd, rp->r_todisgmt);
+ addtt(ktime, type);
+ }
+ }
+ if (usestart) {
+ if (*startbuf == '\0' &&
+ zp->z_format != NULL &&
+ strchr(zp->z_format, '%') == NULL &&
+ strchr(zp->z_format, '/') == NULL)
+ (void) strcpy(startbuf, zp->z_format);
+ eat(zp->z_filename, zp->z_linenum);
+ if (*startbuf == '\0')
+error(_("can't determine time zone abbreviation to use just after until time"));
+ else addtt(starttime,
+ addtype(startoff, startbuf,
+ startoff != zp->z_gmtoff,
+ startttisstd,
+ startttisgmt));
+ }
+ /*
+ ** Now we may get to set starttime for the next zone line.
+ */
+ if (useuntil) {
+ startttisstd = zp->z_untilrule.r_todisstd;
+ startttisgmt = zp->z_untilrule.r_todisgmt;
+ starttime = zp->z_untiltime;
+ if (!startttisstd)
+ starttime = tadd(starttime, -stdoff);
+ if (!startttisgmt)
+ starttime = tadd(starttime, -gmtoff);
+ }
+ }
+ writezone(zpfirst->z_name);
+}
+
+static void
+addtt(starttime, type)
+const time_t starttime;
+int type;
+{
+ if (starttime <= min_time ||
+ (timecnt == 1 && attypes[0].at < min_time)) {
+ gmtoffs[0] = gmtoffs[type];
+ isdsts[0] = isdsts[type];
+ ttisstds[0] = ttisstds[type];
+ ttisgmts[0] = ttisgmts[type];
+ if (abbrinds[type] != 0)
+ (void) strcpy(chars, &chars[abbrinds[type]]);
+ abbrinds[0] = 0;
+ charcnt = strlen(chars) + 1;
+ typecnt = 1;
+ timecnt = 0;
+ type = 0;
+ }
+ if (timecnt >= TZ_MAX_TIMES) {
+ error(_("too many transitions?!"));
+ (void) exit(EXIT_FAILURE);
+ }
+ attypes[timecnt].at = starttime;
+ attypes[timecnt].type = type;
+ ++timecnt;
+}
+
+static int
+addtype(gmtoff, abbr, isdst, ttisstd, ttisgmt)
+const long gmtoff;
+const char * const abbr;
+const int isdst;
+const int ttisstd;
+const int ttisgmt;
+{
+ register int i, j;
+
+ if (isdst != TRUE && isdst != FALSE) {
+ error(_("internal error - addtype called with bad isdst"));
+ (void) exit(EXIT_FAILURE);
+ }
+ if (ttisstd != TRUE && ttisstd != FALSE) {
+ error(_("internal error - addtype called with bad ttisstd"));
+ (void) exit(EXIT_FAILURE);
+ }
+ if (ttisgmt != TRUE && ttisgmt != FALSE) {
+ error(_("internal error - addtype called with bad ttisgmt"));
+ (void) exit(EXIT_FAILURE);
+ }
+ /*
+ ** See if there's already an entry for this zone type.
+ ** If so, just return its index.
+ */
+ for (i = 0; i < typecnt; ++i) {
+ if (gmtoff == gmtoffs[i] && isdst == isdsts[i] &&
+ strcmp(abbr, &chars[abbrinds[i]]) == 0 &&
+ ttisstd == ttisstds[i] &&
+ ttisgmt == ttisgmts[i])
+ return i;
+ }
+ /*
+ ** There isn't one; add a new one, unless there are already too
+ ** many.
+ */
+ if (typecnt >= TZ_MAX_TYPES) {
+ error(_("too many local time types"));
+ (void) exit(EXIT_FAILURE);
+ }
+ gmtoffs[i] = gmtoff;
+ isdsts[i] = isdst;
+ ttisstds[i] = ttisstd;
+ ttisgmts[i] = ttisgmt;
+
+ for (j = 0; j < charcnt; ++j)
+ if (strcmp(&chars[j], abbr) == 0)
+ break;
+ if (j == charcnt)
+ newabbr(abbr);
+ abbrinds[i] = j;
+ ++typecnt;
+ return i;
+}
+
+static void
+leapadd(t, positive, rolling, count)
+const time_t t;
+const int positive;
+const int rolling;
+int count;
+{
+ register int i, j;
+
+ if (leapcnt + (positive ? count : 1) > TZ_MAX_LEAPS) {
+ error(_("too many leap seconds"));
+ (void) exit(EXIT_FAILURE);
+ }
+ for (i = 0; i < leapcnt; ++i)
+ if (t <= trans[i]) {
+ if (t == trans[i]) {
+ error(_("repeated leap second moment"));
+ (void) exit(EXIT_FAILURE);
+ }
+ break;
+ }
+ do {
+ for (j = leapcnt; j > i; --j) {
+ trans[j] = trans[j - 1];
+ corr[j] = corr[j - 1];
+ roll[j] = roll[j - 1];
+ }
+ trans[i] = t;
+ corr[i] = positive ? 1L : eitol(-count);
+ roll[i] = rolling;
+ ++leapcnt;
+ } while (positive && --count != 0);
+}
+
+static void
+adjleap P((void))
+{
+ register int i;
+ register long last = 0;
+
+ /*
+ ** propagate leap seconds forward
+ */
+ for (i = 0; i < leapcnt; ++i) {
+ trans[i] = tadd(trans[i], last);
+ last = corr[i] += last;
+ }
+}
+
+static int
+yearistype(year, type)
+const int year;
+const char * const type;
+{
+ static char * buf;
+ int result;
+
+ if (type == NULL || *type == '\0')
+ return TRUE;
+ buf = erealloc(buf, (int) (132 + strlen(yitcommand) + strlen(type)));
+ (void) sprintf(buf, "%s %d %s", yitcommand, year, type);
+ result = system(buf);
+ if (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);
+}
+
+static int
+lowerit(a)
+int a;
+{
+ a = (unsigned char) a;
+ return (isascii(a) && isupper(a)) ? tolower(a) : a;
+}
+
+static int
+ciequal(ap, bp) /* case-insensitive equality */
+register const char * ap;
+register const char * bp;
+{
+ while (lowerit(*ap) == lowerit(*bp++))
+ if (*ap++ == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+static int
+itsabbr(abbr, word)
+register const char * abbr;
+register const char * word;
+{
+ if (lowerit(*abbr) != lowerit(*word))
+ return FALSE;
+ ++word;
+ while (*++abbr != '\0')
+ do {
+ if (*word == '\0')
+ return FALSE;
+ } while (lowerit(*word++) != lowerit(*abbr));
+ return TRUE;
+}
+
+static const struct lookup *
+byword(word, table)
+register const char * const word;
+register const struct lookup * const table;
+{
+ register const struct lookup * foundlp;
+ register const struct lookup * lp;
+
+ if (word == NULL || table == NULL)
+ return NULL;
+ /*
+ ** Look for exact match.
+ */
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (ciequal(word, lp->l_word))
+ return lp;
+ /*
+ ** Look for inexact match.
+ */
+ foundlp = NULL;
+ for (lp = table; lp->l_word != NULL; ++lp)
+ if (itsabbr(word, lp->l_word)) {
+ if (foundlp == NULL)
+ foundlp = lp;
+ else return NULL; /* multiple inexact matches */
+ }
+ return foundlp;
+}
+
+static char **
+getfields(cp)
+register char * cp;
+{
+ register char * dp;
+ register char ** array;
+ register int nsubs;
+
+ if (cp == NULL)
+ return NULL;
+ array = (char **) (void *)
+ emalloc((int) ((strlen(cp) + 1) * sizeof *array));
+ nsubs = 0;
+ for ( ; ; ) {
+ while (isascii(*cp) && isspace((unsigned char) *cp))
+ ++cp;
+ if (*cp == '\0' || *cp == '#')
+ break;
+ array[nsubs++] = dp = cp;
+ do {
+ if ((*dp = *cp++) != '"')
+ ++dp;
+ else while ((*dp = *cp++) != '"')
+ if (*dp != '\0')
+ ++dp;
+ else error(_("odd number of quotation marks"));
+ } while (*cp != '\0' && *cp != '#' &&
+ (!isascii(*cp) || !isspace((unsigned char) *cp)));
+ if (isascii(*cp) && isspace((unsigned char) *cp))
+ ++cp;
+ *dp = '\0';
+ }
+ array[nsubs] = NULL;
+ return array;
+}
+
+static long
+oadd(t1, t2)
+const long t1;
+const long t2;
+{
+ register long t;
+
+ t = t1 + t2;
+ if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+ error(_("time overflow"));
+ (void) exit(EXIT_FAILURE);
+ }
+ return t;
+}
+
+static time_t
+tadd(t1, t2)
+const time_t t1;
+const long t2;
+{
+ register time_t t;
+
+ if (t1 == max_time && t2 > 0)
+ return max_time;
+ if (t1 == min_time && t2 < 0)
+ return min_time;
+ t = t1 + t2;
+ if ((t2 > 0 && t <= t1) || (t2 < 0 && t >= t1)) {
+ error(_("time overflow"));
+ (void) exit(EXIT_FAILURE);
+ }
+ return t;
+}
+
+/*
+** Given a rule, and a year, compute the date - in seconds since January 1,
+** 1970, 00:00 LOCAL time - in that year that the rule refers to.
+*/
+
+static time_t
+rpytime(rp, wantedy)
+register const struct rule * const rp;
+register const int wantedy;
+{
+ register int y, m, i;
+ register long dayoff; /* with a nod to Margaret O. */
+ register time_t t;
+
+ if (wantedy == INT_MIN)
+ return min_time;
+ if (wantedy == INT_MAX)
+ return max_time;
+ dayoff = 0;
+ m = TM_JANUARY;
+ y = EPOCH_YEAR;
+ while (wantedy != y) {
+ if (wantedy > y) {
+ i = len_years[isleap(y)];
+ ++y;
+ } else {
+ --y;
+ i = -len_years[isleap(y)];
+ }
+ dayoff = oadd(dayoff, eitol(i));
+ }
+ while (m != rp->r_month) {
+ i = len_months[isleap(y)][m];
+ dayoff = oadd(dayoff, eitol(i));
+ ++m;
+ }
+ i = rp->r_dayofmonth;
+ if (m == TM_FEBRUARY && i == 29 && !isleap(y)) {
+ if (rp->r_dycode == DC_DOWLEQ)
+ --i;
+ else {
+ error(_("use of 2/29 in non leap-year"));
+ (void) exit(EXIT_FAILURE);
+ }
+ }
+ --i;
+ dayoff = oadd(dayoff, eitol(i));
+ if (rp->r_dycode == DC_DOWGEQ || rp->r_dycode == DC_DOWLEQ) {
+ register long wday;
+
+#define LDAYSPERWEEK ((long) DAYSPERWEEK)
+ wday = eitol(EPOCH_WDAY);
+ /*
+ ** Don't trust mod of negative numbers.
+ */
+ if (dayoff >= 0)
+ wday = (wday + dayoff) % LDAYSPERWEEK;
+ else {
+ wday -= ((-dayoff) % LDAYSPERWEEK);
+ if (wday < 0)
+ wday += LDAYSPERWEEK;
+ }
+ while (wday != eitol(rp->r_wday))
+ if (rp->r_dycode == DC_DOWGEQ) {
+ dayoff = oadd(dayoff, (long) 1);
+ if (++wday >= LDAYSPERWEEK)
+ wday = 0;
+ ++i;
+ } else {
+ dayoff = oadd(dayoff, (long) -1);
+ if (--wday < 0)
+ wday = LDAYSPERWEEK - 1;
+ --i;
+ }
+ if (i < 0 || i >= len_months[isleap(y)][m]) {
+ error(_("no day in month matches rule"));
+ (void) exit(EXIT_FAILURE);
+ }
+ }
+ if (dayoff < 0 && !TYPE_SIGNED(time_t))
+ return min_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);
+}
+
+static void
+newabbr(string)
+const char * const string;
+{
+ register int i;
+
+ i = strlen(string) + 1;
+ if (charcnt + i > TZ_MAX_CHARS) {
+ error(_("too many, or too long, time zone abbreviations"));
+ (void) exit(EXIT_FAILURE);
+ }
+ (void) strcpy(&chars[charcnt], string);
+ charcnt += eitol(i);
+}
+
+static int
+mkdirs(argname)
+char * const argname;
+{
+ register char * name;
+ register char * cp;
+
+ if (argname == NULL || *argname == '\0' || Dflag)
+ return 0;
+ cp = name = ecpyalloc(argname);
+ while ((cp = strchr(cp + 1, '/')) != 0) {
+ *cp = '\0';
+#ifndef unix
+ /*
+ ** DOS drive specifier?
+ */
+ if (isalpha((unsigned char) name[0]) &&
+ name[1] == ':' && name[2] == '\0') {
+ *cp = '/';
+ continue;
+ }
+#endif /* !defined unix */
+ if (!itsdir(name)) {
+ /*
+ ** It doesn't seem to exist, so we try to create it.
+ ** Creation may fail because of the directory being
+ ** created by some other multiprocessor, so we get
+ ** to do extra checking.
+ */
+ if (mkdir(name, (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 = '/';
+ }
+ ifree(name);
+ return 0;
+}
+
+static long
+eitol(i)
+const int i;
+{
+ long l;
+
+ l = i;
+ if ((i < 0 && l >= 0) || (i == 0 && l != 0) || (i > 0 && l <= 0))
+ errx(EXIT_FAILURE, _("%d did not sign extend correctly"), i);
+ return l;
+}
+
+#include <grp.h>
+#include <pwd.h>
+
+static void
+setgroup(flag, name)
+ gid_t *flag;
+ const char *name;
+{
+ struct group *gr;
+
+ if (*flag != (gid_t)-1)
+ errx(EXIT_FAILURE, _("multiple -g flags specified"));
+
+ gr = getgrnam(name);
+ if (gr == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ errx(EXIT_FAILURE, _("group `%s' not found"), name);
+ }
+ *flag = gr->gr_gid;
+}
+
+static void
+setuser(flag, name)
+ uid_t *flag;
+ const char *name;
+{
+ struct passwd *pw;
+
+ if (*flag != (gid_t)-1)
+ errx(EXIT_FAILURE, _("multiple -u flags specified"));
+
+ pw = getpwnam(name);
+ if (pw == 0) {
+ char *ep;
+ unsigned long ul;
+
+ ul = strtoul(name, &ep, 10);
+ if (ul == (unsigned long)(gid_t)ul && *ep == '\0') {
+ *flag = ul;
+ return;
+ }
+ errx(EXIT_FAILURE, _("user `%s' not found"), name);
+ }
+ *flag = pw->pw_uid;
+}
+
+/*
+** UNIX was a registered trademark of 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..2f6e49d
--- /dev/null
+++ b/usr.sbin/zic/zic/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zic
+
+SRCS= zic.c ialloc.c scheck.c
+MAN8= ${.CURDIR}/../zic.8
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+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
+
+.include <bsd.prog.mk>
OpenPOWER on IntegriCloud